Ye-Song commited on
Commit
dfd20e8
·
verified ·
1 Parent(s): 8c141f8

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. env/kimodo/lib/python3.10/site-packages/SpatialTransform/__init__.py +3 -0
  2. env/kimodo/lib/python3.10/site-packages/SpatialTransform/__pycache__/__init__.cpython-310.pyc +0 -0
  3. env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/__pycache__/euler.cpython-310.pyc +0 -0
  4. env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/__pycache__/pose.cpython-310.pyc +0 -0
  5. env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/__pycache__/transform.cpython-310.pyc +0 -0
  6. env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/euler.py +117 -0
  7. env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/pose.py +132 -0
  8. env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/transform.py +403 -0
  9. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__init__.py +0 -0
  10. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/__init__.cpython-310.pyc +0 -0
  11. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_euler.cpython-310.pyc +0 -0
  12. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_hierarchy.cpython-310.pyc +0 -0
  13. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_methods.cpython-310.pyc +0 -0
  14. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_pose.cpython-310.pyc +0 -0
  15. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_properties.cpython-310.pyc +0 -0
  16. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_transform_extended.cpython-310.pyc +0 -0
  17. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/utils.cpython-310.pyc +0 -0
  18. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_euler.py +120 -0
  19. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_hierarchy.py +188 -0
  20. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_methods.py +166 -0
  21. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_pose.py +233 -0
  22. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_properties.py +173 -0
  23. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_transform_extended.py +268 -0
  24. env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/utils.py +31 -0
  25. env/kimodo/lib/python3.10/site-packages/_yaml/__init__.py +33 -0
  26. env/kimodo/lib/python3.10/site-packages/_yaml/__pycache__/__init__.cpython-310.pyc +0 -0
  27. env/kimodo/lib/python3.10/site-packages/multipart/__init__.py +24 -0
  28. env/kimodo/lib/python3.10/site-packages/multipart/__pycache__/__init__.cpython-310.pyc +0 -0
  29. env/kimodo/lib/python3.10/site-packages/multipart/__pycache__/decoders.cpython-310.pyc +0 -0
  30. env/kimodo/lib/python3.10/site-packages/multipart/__pycache__/exceptions.cpython-310.pyc +0 -0
  31. env/kimodo/lib/python3.10/site-packages/multipart/__pycache__/multipart.cpython-310.pyc +0 -0
  32. env/kimodo/lib/python3.10/site-packages/multipart/decoders.py +1 -0
  33. env/kimodo/lib/python3.10/site-packages/multipart/exceptions.py +1 -0
  34. env/kimodo/lib/python3.10/site-packages/multipart/multipart.py +1 -0
  35. env/kimodo/lib/python3.10/site-packages/pygments/__init__.py +82 -0
  36. env/kimodo/lib/python3.10/site-packages/pygments/__main__.py +17 -0
  37. env/kimodo/lib/python3.10/site-packages/pygments/cmdline.py +668 -0
  38. env/kimodo/lib/python3.10/site-packages/pygments/console.py +70 -0
  39. env/kimodo/lib/python3.10/site-packages/pygments/filter.py +70 -0
  40. env/kimodo/lib/python3.10/site-packages/pygments/filters/__init__.py +940 -0
  41. env/kimodo/lib/python3.10/site-packages/pygments/filters/__pycache__/__init__.cpython-310.pyc +0 -0
  42. env/kimodo/lib/python3.10/site-packages/pygments/formatter.py +129 -0
  43. env/kimodo/lib/python3.10/site-packages/pygments/formatters/__init__.py +157 -0
  44. env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/__init__.cpython-310.pyc +0 -0
  45. env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/_mapping.cpython-310.pyc +0 -0
  46. env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/bbcode.cpython-310.pyc +0 -0
  47. env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/groff.cpython-310.pyc +0 -0
  48. env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/html.cpython-310.pyc +0 -0
  49. env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/img.cpython-310.pyc +0 -0
  50. env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/irc.cpython-310.pyc +0 -0
env/kimodo/lib/python3.10/site-packages/SpatialTransform/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .lib.transform import Transform
2
+ from .lib.pose import Pose
3
+ from .lib.euler import Euler
env/kimodo/lib/python3.10/site-packages/SpatialTransform/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (301 Bytes). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/__pycache__/euler.cpython-310.pyc ADDED
Binary file (4.53 kB). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/__pycache__/pose.cpython-310.pyc ADDED
Binary file (5.5 kB). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/__pycache__/transform.cpython-310.pyc ADDED
Binary file (17.3 kB). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/euler.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import glm
3
+
4
+
5
+ # https://en.wikipedia.org/wiki/Euler_angles
6
+
7
+
8
+ class Euler:
9
+ """Static collection of conversions for euler angles.
10
+
11
+ Matrices are expected to be column major, like glm.
12
+ """
13
+
14
+ def getOrders() -> list[str]:
15
+ """List of possible rotation orders in 3D space."""
16
+ return list(['XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX'])
17
+
18
+ def toQuatFrom(radians: glm.vec3, order: str = 'ZXY', extrinsic: bool = True) -> glm.quat:
19
+ """Converts euler angles to quaternion.
20
+
21
+ Rotation order of eulers must be given as 'XYZ' in any order.
22
+
23
+ If extrinsic the rotation will be around the world axes, ignoring previous rotations."""
24
+ result = glm.quat()
25
+ radians = glm.vec3(radians)
26
+
27
+ order = order.upper()
28
+ if extrinsic:
29
+ order = reversed(order)
30
+ for axis in order:
31
+ if axis == 'X': result = glm.rotate(result, radians.x, (1, 0, 0)); continue
32
+ if axis == 'Y': result = glm.rotate(result, radians.y, (0, 1, 0)); continue
33
+ if axis == 'Z': result = glm.rotate(result, radians.z, (0, 0, 1)); continue
34
+
35
+ return result
36
+
37
+ def toMatFrom(radians: glm.vec3, order: str = 'ZXY', extrinsic: bool = True) -> glm.mat3:
38
+ """Converts euler angles to 3x3 rotation matrix.
39
+
40
+ Rotation order of eulers must be given as 'XYZ' in any order.
41
+
42
+ If extrinsic the rotation will be around the world axes, ignoring previous rotations."""
43
+ return glm.mat3_cast(Euler.toQuatFrom(radians, order, extrinsic))
44
+
45
+ def fromQuatTo(quat: glm.quat, order: str = 'ZXY', extrinsic: bool = True) -> glm.vec3:
46
+ """Converts a quaternion to intrinsic euler angles as radians.
47
+
48
+ Rotation order of eulers must be given as 'XYZ' in any order.
49
+
50
+ If extrinsic the rotation will be around the world axes, ignoring previous rotations."""
51
+ return Euler.fromMatTo(glm.mat3_cast(quat), order, extrinsic)
52
+
53
+ def fromMatTo(mat: glm.mat3, order: str = 'ZXY', extrinsic: bool = True) -> glm.vec3:
54
+ """Converts a 3x3 rotation matrix to intrinsic euler angles as radians.
55
+
56
+ Rotation order of eulers must be given as 'XYZ' in any order.
57
+
58
+ If extrinsic the rotation will be around the world axes, ignoring previous rotations."""
59
+ order = order.upper()
60
+ if extrinsic: order = order[::-1]
61
+
62
+ if order == 'XYZ': return fromMatToXYZ(mat)
63
+ if order == 'XZY': return fromMatToXZY(mat)
64
+ if order == 'YXZ': return fromMatToYXZ(mat)
65
+ if order == 'YZX': return fromMatToYZX(mat)
66
+ if order == 'ZXY': return fromMatToZXY(mat)
67
+ if order == 'ZYX': return fromMatToZYX(mat)
68
+
69
+ raise ValueError(f'given order "{order}" is invalid. Must be "XYZ" in any order')
70
+
71
+
72
+ def fromMatToXZY(mat: glm.mat3) -> glm.vec3:
73
+ return glm.vec3(
74
+ math.atan2(mat[1, 2], mat[1, 1]),
75
+ math.atan2(mat[2, 0], mat[0, 0]),
76
+ math.atan2(-mat[1, 0], math.sqrt(max(0, 1 - mat[1, 0]**2))),
77
+ )
78
+
79
+
80
+ def fromMatToXYZ(mat: glm.mat3) -> glm.vec3:
81
+ return glm.vec3(
82
+ math.atan2(-mat[2, 1], mat[2, 2]),
83
+ math.atan2(mat[2, 0], math.sqrt(max(0, 1 - mat[2, 0]**2))),
84
+ math.atan2(-mat[1, 0], mat[0, 0]),
85
+ )
86
+
87
+
88
+ def fromMatToYXZ(mat: glm.mat3) -> glm.vec3:
89
+ return glm.vec3(
90
+ math.atan2(-mat[2, 1], math.sqrt(max(0, 1 - mat[2, 1]**2))),
91
+ math.atan2(mat[2, 0], mat[2, 2]),
92
+ math.atan2(mat[0, 1], mat[1, 1]),
93
+ )
94
+
95
+
96
+ def fromMatToYZX(mat: glm.mat3) -> glm.vec3:
97
+ return glm.vec3(
98
+ math.atan2(-mat[2, 1], mat[1, 1]),
99
+ math.atan2(-mat[0, 2], mat[0, 0]),
100
+ math.atan2(mat[0, 1], math.sqrt(max(0, 1 - mat[0, 1]**2))),
101
+ )
102
+
103
+
104
+ def fromMatToZYX(mat: glm.mat3) -> glm.vec3:
105
+ return glm.vec3(
106
+ math.atan2(mat[1, 2], mat[2, 2]),
107
+ math.atan2(-mat[0, 2], math.sqrt(max(0, 1 - mat[0, 2]**2))),
108
+ math.atan2(mat[0, 1], mat[0, 0]),
109
+ )
110
+
111
+
112
+ def fromMatToZXY(mat: glm.mat3) -> glm.vec3:
113
+ return glm.vec3(
114
+ math.atan2(mat[1, 2], math.sqrt(max(0, 1 - mat[1, 2]**2))),
115
+ math.atan2(-mat[0, 2], mat[2, 2]),
116
+ math.atan2(-mat[1, 0], mat[1, 1]),
117
+ )
env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/pose.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glm
2
+ from .euler import Euler
3
+
4
+
5
+ class Pose:
6
+ """Spatial definition of an linear space with position, rotation and scale.
7
+ - ONLY provides properties and methods for local space.
8
+ - There is no parent child relation between poses
9
+ - Space is defined as right handed where -> Y+ is up, and X+ is right and Z- is forward.
10
+ - Positive rotations are counter clockwise."""
11
+
12
+ @property
13
+ def Space(self) -> glm.mat4:
14
+ """Transform space with properties."""
15
+ if self.__isOutdated:
16
+ self._Space = glm.translate(self.Position)
17
+ self._Space = glm.scale(self._Space, self.Scale)
18
+ self._Space = self._Space * glm.mat4_cast(self.Rotation)
19
+ self.__isOutdated = False
20
+ return glm.mat4(self._Space)
21
+
22
+ @property
23
+ def SpaceInverse(self) -> glm.mat4:
24
+ """Inverted transform space."""
25
+ return glm.inverse(self.Space)
26
+
27
+ @property
28
+ def Position(self) -> glm.vec3:
29
+ """Position of the space."""
30
+ return glm.vec3(self._Position)
31
+
32
+ @Position.setter
33
+ def Position(self, value: glm.vec3) -> None:
34
+ self._Position = glm.vec3(value)
35
+ self.__isOutdated = True
36
+
37
+ @property
38
+ def Rotation(self) -> glm.quat:
39
+ """Rotation of the space."""
40
+ return glm.quat(self._Rotation)
41
+
42
+ @Rotation.setter
43
+ def Rotation(self, value: glm.quat) -> None:
44
+ self._Rotation = glm.quat(value)
45
+ self.__isOutdated = True
46
+
47
+ @property
48
+ def Scale(self) -> glm.vec3:
49
+ """Scale of the space."""
50
+ return glm.vec3(self._Scale)
51
+
52
+ @Scale.setter
53
+ def Scale(self, value: glm.vec3) -> None:
54
+ self._Scale = glm.vec3(value)
55
+ self.__isOutdated = True
56
+
57
+ @property
58
+ def Forward(self) -> glm.vec3:
59
+ """Current alignment of the Z-axis."""
60
+ return self.Rotation * (0, 0, -1)
61
+
62
+ @property
63
+ def Right(self) -> glm.vec3:
64
+ """Current alignment of the X-axis."""
65
+ return self.Rotation * (1, 0, 0)
66
+
67
+ @property
68
+ def Up(self) -> glm.vec3:
69
+ """Current alignment of the Y-axis."""
70
+ return self.Rotation * (0, 1, 0)
71
+
72
+ def __init__(self, position: glm.vec3 = None, rotation: glm.quat = None, scale: glm.vec3 = None) -> None:
73
+ """Creates a new pose."""
74
+
75
+ self._Space = glm.mat4(1)
76
+ self._Position = glm.vec3() if position is None else glm.vec3(position)
77
+ self._Rotation = glm.quat() if rotation is None else glm.quat(rotation)
78
+ self._Scale = glm.vec3(1) if scale is None else glm.vec3(scale)
79
+ self.__isOutdated = True
80
+
81
+ def __repr__(self) -> str:
82
+ return (f"Pos: {self.Position}, Rot: {self.Rotation}, Scale: {self.Scale}")
83
+
84
+ def __str__(self) -> str:
85
+ return self.__repr__()
86
+
87
+ def reset(self) -> "Pose":
88
+ self.Position = glm.vec3(0)
89
+ self.Rotation = glm.quat()
90
+ self.Scale = glm.vec3(1)
91
+
92
+ def getEuler(self, order: str = 'ZXY', extrinsic: bool = True) -> glm.vec3:
93
+ """Returns the current Rotation as euler angles in the given order.
94
+ - If extrinsic the rotation will be around the world axes, ignoring previous rotations."""
95
+ return glm.degrees(Euler.fromQuatTo(self.Rotation, order, extrinsic))
96
+
97
+ def setEuler(self, degrees: glm.vec3, order: str = 'ZXY', extrinsic: bool = True) -> "Pose":
98
+ """Converts the given euler anlges to quaternion and sets the rotation property.
99
+ - If extrinsic the rotation will be around the world axes, ignoring previous rotations.
100
+
101
+ Returns itself."""
102
+ self.Rotation = Euler.toQuatFrom(glm.radians(degrees), order, extrinsic)
103
+ return self
104
+
105
+ def addEuler(self, degrees: glm.vec3, order: str = 'ZXY', extrinsic: bool = True, last: bool = True) -> "Pose":
106
+ """Adds an euler rotation to the current rotation.
107
+ - If extrinsic the rotation will be around the world axes, ignoring previous rotations.
108
+ - If last is True -> The rotation is added after the current rotation.
109
+
110
+ Returns itself."""
111
+ if last:
112
+ self.Rotation = Euler.toQuatFrom(glm.radians(degrees), order, extrinsic) * self.Rotation
113
+ else:
114
+ self.Rotation = self.Rotation * Euler.toQuatFrom(glm.radians(degrees), order, extrinsic)
115
+
116
+ return self
117
+
118
+ def lookAt(self, direction: glm.vec3, up: glm.vec3 = glm.vec3(0, 1, 0)) -> "Pose":
119
+ """Sets Rotation so the Z- axis aligns with the given direction.
120
+ - Direction is considered as local space.
121
+
122
+ Returns itself."""
123
+ direction = glm.normalize(direction)
124
+ dirDot = abs(glm.dot(direction, (0, 1, 0)))
125
+ upAxis = up if dirDot < 0.999 else glm.vec3(1, 0, 0)
126
+ self.Rotation = glm.quatLookAtRH(direction, upAxis)
127
+
128
+ return self
129
+
130
+ def duplicate(self) -> "Pose":
131
+ """Returns a duplicate of this pose."""
132
+ return Pose(self.Position, self.Rotation, self.Scale)
env/kimodo/lib/python3.10/site-packages/SpatialTransform/lib/transform.py ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import glm
3
+ import random
4
+ import string
5
+ from .pose import Pose
6
+
7
+
8
+ class Transform(Pose):
9
+ """Spatial definition of an linear space with position, rotation and scale.
10
+ - Based on 'Pose' class, extended with parent child relation ship propertis and methods.
11
+ - Space is defined as right handed where -> Y+ is up, and X+ is right and Z- is forward.
12
+ - Positive rotations are counter clockwise."""
13
+
14
+ @property
15
+ def Name(self) -> str:
16
+ """Name of the transform."""
17
+ return self._Name
18
+
19
+ @Name.setter
20
+ def Name(self, value: str) -> None:
21
+ self._Name = value
22
+
23
+ @property
24
+ def SpaceWorld(self) -> glm.mat4:
25
+ """Transform space with respect to the parent."""
26
+ return (self.Parent.SpaceWorld if self.Parent else glm.mat4()) * self.Space
27
+
28
+ @property
29
+ def SpaceWorldInverse(self) -> glm.mat4:
30
+ """Inverted transform space with respect to the parent."""
31
+ return glm.inverse(self.SpaceWorld)
32
+
33
+ @property
34
+ def PositionWorld(self) -> glm.vec3:
35
+ """World position of the space."""
36
+ parentSpace = self.Parent.SpaceWorld if self.Parent else glm.mat4()
37
+ return parentSpace * self.Position
38
+
39
+ @PositionWorld.setter
40
+ def PositionWorld(self, value: glm.vec3) -> None:
41
+ parentSpaceInverse = self.Parent.SpaceWorldInverse if self.Parent else glm.mat4()
42
+ self._Position = parentSpaceInverse * value
43
+
44
+ @property
45
+ def RotationWorld(self) -> glm.quat:
46
+ """World rotation of the space."""
47
+ parentSpace = self.Parent.RotationWorld if self.Parent else glm.quat()
48
+ return parentSpace * self.Rotation
49
+
50
+ @RotationWorld.setter
51
+ def RotationWorld(self, value: glm.quat) -> None:
52
+ parentSpaceInverse = self.Parent.RotationWorldInverse if self.Parent else glm.quat()
53
+ self._Rotation = parentSpaceInverse * value
54
+
55
+ @property
56
+ def RotationWorldInverse(self) -> glm.quat:
57
+ """Inverse world rotation of the space."""
58
+ return glm.inverse(self.RotationWorld)
59
+
60
+ @property
61
+ def ScaleWorld(self) -> glm.vec3:
62
+ """World scale of the space."""
63
+ parentSpace = self.Parent.ScaleWorld if self.Parent else glm.vec3(1)
64
+ return parentSpace * self.Scale
65
+
66
+ @ScaleWorld.setter
67
+ def ScaleWorld(self, value: glm.vec3) -> None:
68
+ parentSpaceInverse = self.Parent.ScaleWorldInverse if self.Parent else glm.vec3(1)
69
+ self._Scale = parentSpaceInverse * value
70
+
71
+ @property
72
+ def ScaleWorldInverse(self) -> glm.vec3:
73
+ """Inverse world scale of the space."""
74
+ return (1.0 / self.ScaleWorld)
75
+
76
+ @property
77
+ def ForwardWorld(self) -> glm.vec3:
78
+ """Current rotation of the Z-axis in world space."""
79
+ return (self.RotationWorld * (0, 0, -1, 0)).xyz
80
+
81
+ @property
82
+ def RightWorld(self) -> glm.vec3:
83
+ """Current rotation of the X-axis in world space."""
84
+ return (self.RotationWorld * (1, 0, 0, 0)).xyz
85
+
86
+ @property
87
+ def UpWorld(self) -> glm.vec3:
88
+ """Current rotation of the Y-axis in world space."""
89
+ return (self.RotationWorld * (0, 1, 0, 0)).xyz
90
+
91
+ @property
92
+ def Parent(self) -> "Transform":
93
+ """Transform where this one aligns in repsect to it."""
94
+ return self._Parent
95
+
96
+ @property
97
+ def Children(self) -> list["Transform"]:
98
+ """Attachted transforms. This transform builds the parent space for those children."""
99
+ return self._Children
100
+
101
+ def __init__(self, name: str = None, position: glm.vec3 = None, rotation: glm.quat = None, scale: glm.vec3 = None) -> None:
102
+ """Creates a new transform. Parameters are considered as local space properties."""
103
+ super().__init__(position, rotation, scale)
104
+
105
+ self.Name = name if name is not None else ''.join(random.choice(string.ascii_letters) for _ in range(8))
106
+ self._Parent: "Transform" = None
107
+ self._Children: list["Transform"] = []
108
+
109
+ def __repr__(self) -> str:
110
+ return (f"{self.Name}")
111
+
112
+ def __str__(self) -> str:
113
+ return (f"Name: {self.Name}, Children: {len(self.Children)}, {super().__str__()}")
114
+
115
+ def reset(self, recursive: bool = False) -> "Transform":
116
+ """Resets the transform to pos: (0,0,0) scale: (1,1,1) and no rotation.
117
+
118
+ Returns itself."""
119
+ super().reset()
120
+
121
+ if recursive:
122
+ for child in self.Children:
123
+ child.reset(recursive=True)
124
+
125
+ return self
126
+
127
+ def pointToWorld(self, point: glm.vec3) -> glm.vec3:
128
+ """Transforms a given point in this space to world space."""
129
+ return self.SpaceWorld * point
130
+
131
+ def pointToLocal(self, point: glm.vec3) -> glm.vec3:
132
+ """Transforms a given point in world space to this local space."""
133
+ return self.SpaceWorldInverse * point
134
+
135
+ def directionToWorld(self, direction: glm.vec3) -> glm.vec3:
136
+ """Transforms a given direction in this space to world space."""
137
+ return self.RotationWorld * direction
138
+
139
+ def directionToLocal(self, direction: glm.vec3) -> glm.vec3:
140
+ """Transforms a given direction in world space to this local space."""
141
+ return self.RotationWorldInverse * direction
142
+
143
+ def lookAt(self, direction: glm.vec3, up: glm.vec3 = glm.vec3(0, 1, 0)) -> "Transform":
144
+ return super().lookAt(direction, up)
145
+
146
+ def lookAtWorld(self, direction: glm.vec3, up: glm.vec3 = glm.vec3(0, 1, 0)) -> "Transform":
147
+ """Sets Rotation so the Z- axis aligns with the given direction.
148
+ - Direction is considered as world space.
149
+
150
+ Returns itself."""
151
+ parentWorldRotationInverse = (self.Parent.RotationWorldInverse if self.Parent else glm.mat4())
152
+ direction = parentWorldRotationInverse * direction
153
+
154
+ return super().lookAt(direction, up)
155
+
156
+ def setEuler(self, degrees: glm.vec3, order: str = 'ZXY', extrinsic: bool = True) -> "Transform":
157
+ return super().setEuler(degrees, order, extrinsic)
158
+
159
+ def attach(self, *nodes: "Transform", keep: list[str] = ['position', 'rotation', 'scale']) -> "Transform":
160
+ """Attaches the given transforms to this one as a child.
161
+ - If keep contains properties -> the property is modified to keep its spatial algiment in world space.
162
+ - If keep is None or empty -> Local space propteries do not change.
163
+ - If the transform is detatched first if it already has parent.
164
+ - Nothing will change if the node already has a relation to this transform.
165
+
166
+ Returns itself."""
167
+ for node in nodes:
168
+ # validate given joint
169
+ if node is None: raise ValueError('Given joint value is None')
170
+ if node is self: raise ValueError(f'Joint "{self.Name}" cannot be parent of itself')
171
+ if node in self.Children: continue
172
+
173
+ # detach
174
+ if node._Parent is not None:
175
+ node._Parent.detach(node, keep=keep)
176
+
177
+ # attatch
178
+ self.Children.append(node)
179
+ node._Parent = self
180
+
181
+ # correct world space alignment
182
+ if keep is not None:
183
+ if 'position' in keep: node.Position = self.SpaceWorldInverse * node.Position
184
+ if 'rotation' in keep: node.Rotation = self.RotationWorldInverse * node.Rotation
185
+ if 'scale' in keep: node.Scale = self.ScaleWorldInverse * node.Scale
186
+ return self
187
+
188
+ def detach(self, *nodes: "Transform", keep: list[str] = ['position', 'rotation', 'scale']) -> "Transform":
189
+ """Detachs the given child transform.
190
+ - If keep contains properties -> the property is modified to keep its spatial algiment in world space.
191
+ - If keep is None or empty -> Local space propteries do not change.
192
+ - Nothing will change if the node has no relation to this transform.
193
+
194
+ Returns itself."""
195
+ for node in nodes:
196
+ # validate given joint
197
+ if node is None: raise ValueError('Given joint value is None')
198
+ if node is self: raise ValueError(f'Joint "{self.Name}" cannot be detachd from itself')
199
+ if node.Parent is self and node not in self.Children: raise ValueError(f'Joint "{node.Name}" has "{self.Name}" as parent, bust does not exist in the children list. Avoid manual child parent modifications')
200
+ if node.Parent is None or node.Parent != self: continue
201
+
202
+ # correct world space alignment
203
+ if keep is not None:
204
+ if 'position' in keep: node.Position = self.SpaceWorld * node.Position
205
+ if 'rotation' in keep: node.Rotation = self.RotationWorld * node.Rotation
206
+ if 'scale' in keep: node.Scale = self.ScaleWorld * node.Scale
207
+
208
+ # detach
209
+ self.Children.remove(node)
210
+ node._Parent = None
211
+ return self
212
+
213
+ def clearParent(self, keep: list[str] = ['position', 'rotation', 'scale']) -> "Transform":
214
+ """Detaches/detachs itself from the parent.
215
+ - If keep contains properties -> the property is modified to keep its spatial algiment in world space.
216
+ - If keep is None or empty -> Local space propteries do not change.
217
+
218
+ Returns itself."""
219
+ if self.Parent is not None:
220
+ self.Parent.detach(self, keep=keep)
221
+ return self
222
+
223
+ def clearChildren(self, keep: list[str] = ['position', 'rotation', 'scale']) -> "Transform":
224
+ """Detachs all children of this transform.
225
+ - If keep contains properties -> the property is modified to keep its spatial algiment in world space.
226
+ - If keep is None or empty -> Local space propteries do not change.
227
+
228
+ Returns itself."""
229
+ if (len(self.Children) > 0):
230
+ self.detach(*self.Children, keep=keep)
231
+ return self
232
+
233
+ def _applyPositionGetChanges(self, position: glm.vec3 = None) -> tuple[glm.vec3, glm.vec3]:
234
+ change = -self.Position if position is None else position
235
+ changeInverse = glm.inverse(self.Rotation) * ((1.0 / self.Scale) * -change)
236
+ return (change, changeInverse)
237
+
238
+ def _applyPositionChange(self, change: glm.vec3):
239
+ self.Position = self.Position + change
240
+
241
+ def _applyPositionChangeInverse(self, changeInverse: glm.vec3):
242
+ self.Position = self.Position + changeInverse
243
+
244
+ def applyPosition(self, position: glm.vec3 = None, recursive: bool = False) -> "Transform":
245
+ """Changes the position of this transform and updates its children to keep them spatial unchanged.
246
+ - If position is None -> the transform resets its position to (0, 0, 0).
247
+ - If position IS set -> the given position is added to the current position.
248
+
249
+ Returns itself."""
250
+ change, changeInverse = self._applyPositionGetChanges(position)
251
+
252
+ self._applyPositionChange(change)
253
+
254
+ for child in self.Children:
255
+ child._applyPositionChangeInverse(changeInverse)
256
+
257
+ if recursive:
258
+ child.applyPosition(position=position, recursive=True)
259
+
260
+ return self
261
+
262
+ def _applyRotationGetChanges(self, rotation: glm.quat = None) -> tuple[glm.quat, glm.quat]:
263
+ change = glm.inverse(self.Rotation) if rotation is None else rotation
264
+ changeInverse = glm.inverse(change)
265
+ return (change, changeInverse)
266
+
267
+ def _applyRotationChange(self, change: glm.quat):
268
+ self.Rotation = self.Rotation * change
269
+
270
+ def _applyRotationChangeInverse(self, changeInverse: glm.quat, bake: bool = False):
271
+ self.Position = changeInverse * self.Position
272
+ if not bake:
273
+ self.Rotation = changeInverse * self.Rotation
274
+
275
+ def applyRotation(self, rotation: glm.quat = None, recursive: bool = False, bake: bool = False) -> "Transform":
276
+ """Changes the rotation of this transform and updates its children to keep them spatial unchanged.
277
+ - If rotation is None -> the transform resets its rotation to (1, 0, 0, 0).
278
+ - If rotation IS set -> the given rotation is added to the current rotation.
279
+ - If bake Is True -> The rotation correction is NOT passed to the children, only positions will be modified.
280
+
281
+ Returns itself."""
282
+ change, changeInverse = self._applyRotationGetChanges(rotation)
283
+
284
+ self._applyRotationChange(change)
285
+
286
+ for child in self.Children:
287
+ child._applyRotationChangeInverse(changeInverse, bake=bake)
288
+
289
+ if recursive:
290
+ child.applyRotation(rotation=rotation, recursive=True, bake=bake)
291
+
292
+ return self
293
+
294
+ def _applyScaleGetChanges(self, scale: glm.vec3 = None) -> tuple[glm.vec3, glm.vec3]:
295
+ change = (1.0 / self.Scale) if scale is None else scale
296
+ changeInverse = (1.0 / change)
297
+ return (change, changeInverse)
298
+
299
+ def _applyScaleChange(self, change: glm.vec3):
300
+ self.Scale = self.Scale * change
301
+
302
+ def _applyScaleChangeInverse(self, changeInverse: glm.vec3, bake: bool = False):
303
+ self.Position = changeInverse * self.Position
304
+ if not bake:
305
+ self.Scale = changeInverse * self.Scale
306
+
307
+ def applyScale(self, scale: glm.vec3 = None, recursive: bool = False, bake: bool = False) -> "Transform":
308
+ """Changes the scale of the transform and updates its children to keep them spatial unchanged.
309
+ - If scale is NOT set -> The transform resets its scale to (1, 1, 1).
310
+ - If scale IS set -> The given scale is added to the current scale.
311
+ - If bake Is True -> The scale correction is NOT passed to the children, only positions will be modified.
312
+
313
+ Returns itself."""
314
+ change, changeInverse = self._applyScaleGetChanges(scale)
315
+
316
+ self._applyScaleChange(change)
317
+
318
+ for child in self.Children:
319
+ child._applyScaleChangeInverse(changeInverse, bake=bake)
320
+
321
+ if recursive:
322
+ child.applyScale(scale=scale, recursive=True, bake=bake)
323
+
324
+ return self
325
+
326
+ def layout(self, index: int = 0, depth: int = 0) -> list[tuple["Transform", int, int]]:
327
+ """Returns the hierarchy, inclunding this transform, in order of 'depth first' with their index and depth.
328
+ - Order of the tuple -> [transform, index, depth]"""
329
+ result = [[self, index, depth]]
330
+ for child in self.Children:
331
+ result.extend(child.layout(result[-1][1] + 1, depth + 1))
332
+ return result
333
+
334
+ def printTree(self, markerStr="+- ", levelMarkers=[]) -> None:
335
+ # src: https://simonhessner.de/python-3-recursively-print-structured-tree-including-hierarchy-markers-using-depth-first-search/
336
+ """
337
+ Recursive function that prints the hierarchical structure of a tree including markers that indicate parent-child relationships between nodes.
338
+
339
+ Parameters:
340
+ - root: Node instance, possibly containing children Nodes
341
+ - markerStr: String to print in front of each node ("+- " by default)
342
+ - levelMarkers: Internally used by recursion to indicate where to
343
+ print markers and connections (see explanations below)
344
+ """
345
+
346
+ def mapper(draw): return connectionStr if draw else emptyStr
347
+ emptyStr = " " * len(markerStr)
348
+ connectionStr = "|" + emptyStr[:-1]
349
+ level = len(levelMarkers) # recursion level
350
+ markers = "".join(map(mapper, levelMarkers[:-1]))
351
+ markers += markerStr if level > 0 else ""
352
+ print(f"{markers}{self.Name}")
353
+
354
+ for i, child in enumerate(self.Children):
355
+ isLast = i == len(self.Children) - 1
356
+ child.printTree(markerStr, [*levelMarkers, not isLast])
357
+
358
+ def filter(self, pattern: str, isEqual: bool = False, caseSensitive: bool = False) -> list["Transform"]:
359
+ """Tries to find transforms that matches the pattern in their name name.
360
+ - If isEqual is true, the name has to be equal to the pattern. Otherwise the pattern must only appear anywhere in the name."""
361
+ result = []
362
+
363
+ selfname = self.Name if caseSensitive else self.Name.lower()
364
+ if not caseSensitive: pattern = pattern.lower()
365
+
366
+ if (isEqual and pattern == selfname) or (not isEqual and pattern in selfname):
367
+ result.append(self)
368
+ for child in self.Children:
369
+ result.extend(child.filter(pattern, isEqual, caseSensitive))
370
+
371
+ return result
372
+
373
+ def filterRegex(self, pattern: str) -> list["Transform"]:
374
+ """Tries to find transforms that matches the pattern in their name name."""
375
+ result = []
376
+
377
+ if re.match(pattern, self.Name) is not None:
378
+ result.append(self)
379
+ for child in self.Children:
380
+ result.extend(child.filterRegex(pattern))
381
+
382
+ return result
383
+
384
+ def duplicate(self, recursive: bool = False) -> "Transform":
385
+ """Returns a duplicate of this transform.
386
+ - If recursive is True -> All child transfroms are duplicated too, into this duplicate."""
387
+
388
+ newChildren = [child.duplicate(recursive=True) for child in self.Children] if recursive else []
389
+ newDuplicate = Transform(self.Name, self.Position, self.Rotation, self.Scale)
390
+ return newDuplicate.attach(*newChildren, keep=None)
391
+
392
+ def toPose(self, worldSpace: bool = False) -> Pose:
393
+ """Returns this transform as new pose object.
394
+ - If worldSpace is True -> The world spcae properties are copied into the pose."""
395
+ if worldSpace:
396
+ return Pose(self.PositionWorld, rotation=self.RotationWorld, scale=self.ScaleWorld)
397
+ else:
398
+ return Pose(self.Position, rotation=self.Rotation, scale=self.Scale)
399
+
400
+ def fromPose(pose: Pose, name: str = None) -> "Transform":
401
+ """Returns this pose as new transform.
402
+ - If name is set -> The name will be set for the new transform."""
403
+ return Transform(name=name, position=pose.Position, rotation=pose.Rotation, scale=pose.Scale)
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__init__.py ADDED
File without changes
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (184 Bytes). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_euler.cpython-310.pyc ADDED
Binary file (4.25 kB). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_hierarchy.cpython-310.pyc ADDED
Binary file (5.22 kB). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_methods.cpython-310.pyc ADDED
Binary file (5.9 kB). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_pose.cpython-310.pyc ADDED
Binary file (7.51 kB). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_properties.cpython-310.pyc ADDED
Binary file (5.31 kB). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/test_transform_extended.cpython-310.pyc ADDED
Binary file (6.61 kB). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/__pycache__/utils.cpython-310.pyc ADDED
Binary file (1.27 kB). View file
 
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_euler.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glm
2
+ import unittest
3
+ import math
4
+ from .utils import *
5
+ from SpatialTransform import Euler
6
+
7
+ class Conversions(unittest.TestCase):
8
+ def test_toQuatFrom(self):
9
+ for _ in range(randomSamples):
10
+ r = randomRotation()
11
+ e = glm.eulerAngles(r)
12
+ angle = glm.angle(Euler.toQuatFrom(e, order='XYZ', extrinsic=True) * glm.inverse(r))
13
+ self.assertFalse(0.01 < angle < (glm.two_pi()-0.01))
14
+
15
+ def test_fromMatTo(self):
16
+ for _ in range(randomSamples):
17
+ r = randomRotation()
18
+ e = glm.eulerAngles(r)
19
+ m = glm.mat3_cast(r)
20
+ self.assertGreater(0.01, glm.distance(e, Euler.fromMatTo(m, order='XYZ', extrinsic=True)))
21
+
22
+ def test_toMatFrom(self):
23
+ """Test toMatFrom method."""
24
+ for _ in range(randomSamples):
25
+ euler = glm.vec3(
26
+ random.uniform(-math.pi, math.pi),
27
+ random.uniform(-math.pi, math.pi),
28
+ random.uniform(-math.pi, math.pi)
29
+ )
30
+
31
+ # Test different orders
32
+ orders = ['XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX']
33
+ for order in orders:
34
+ for extrinsic in [True, False]:
35
+ mat = Euler.toMatFrom(euler, order=order, extrinsic=extrinsic)
36
+ self.assertIsInstance(mat, glm.mat3)
37
+
38
+ # Verify it's a proper rotation matrix (determinant should be 1)
39
+ det = glm.determinant(mat)
40
+ self.assertGreater(0.01, abs(det - 1.0))
41
+
42
+ def test_fromQuatTo(self):
43
+ """Test fromQuatTo method."""
44
+ for _ in range(randomSamples):
45
+ rotation = randomRotation()
46
+
47
+ # Test different orders
48
+ orders = ['XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX']
49
+ for order in orders:
50
+ for extrinsic in [True, False]:
51
+ euler = Euler.fromQuatTo(rotation, order=order, extrinsic=extrinsic)
52
+ self.assertIsInstance(euler, glm.vec3)
53
+
54
+ # Round trip test
55
+ recovered_quat = Euler.toQuatFrom(euler, order=order, extrinsic=extrinsic)
56
+ angle_diff = glm.angle(rotation * glm.inverse(recovered_quat))
57
+ self.assertFalse(0.01 < angle_diff < (glm.two_pi()-0.01))
58
+
59
+ def test_all_rotation_orders(self):
60
+ """Test all supported rotation orders."""
61
+ orders = ['XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX']
62
+
63
+ for order in orders:
64
+ for extrinsic in [True, False]:
65
+ euler = glm.vec3(0.5, 1.0, 1.5) # Test angles in radians
66
+
67
+ # Test conversion chain: euler -> quat -> mat -> euler
68
+ quat = Euler.toQuatFrom(euler, order=order, extrinsic=extrinsic)
69
+ mat = Euler.toMatFrom(euler, order=order, extrinsic=extrinsic)
70
+
71
+ # Verify quat and mat represent same rotation
72
+ mat_from_quat = glm.mat3_cast(quat)
73
+ for i in range(3):
74
+ for j in range(3):
75
+ self.assertGreater(0.01, abs(mat[i][j] - mat_from_quat[i][j]))
76
+
77
+ def test_round_trip_conversions(self):
78
+ """Test round trip conversions between different representations."""
79
+ for _ in range(100): # Fewer samples for complex test
80
+ original_euler = glm.vec3(
81
+ random.uniform(-math.pi/2, math.pi/2), # Avoid gimbal lock
82
+ random.uniform(-math.pi/2, math.pi/2),
83
+ random.uniform(-math.pi/2, math.pi/2)
84
+ )
85
+
86
+ order = 'XYZ'
87
+ extrinsic = True
88
+
89
+ # euler -> quat -> euler
90
+ quat = Euler.toQuatFrom(original_euler, order=order, extrinsic=extrinsic)
91
+ recovered_euler = Euler.fromQuatTo(quat, order=order, extrinsic=extrinsic)
92
+ self.assertGreater(0.1, glm.distance(original_euler, recovered_euler))
93
+
94
+ # euler -> mat -> euler
95
+ mat = Euler.toMatFrom(original_euler, order=order, extrinsic=extrinsic)
96
+ recovered_euler_mat = Euler.fromMatTo(mat, order=order, extrinsic=extrinsic)
97
+ self.assertGreater(0.1, glm.distance(original_euler, recovered_euler_mat))
98
+
99
+
100
+ class EulerUtilities(unittest.TestCase):
101
+ def test_getOrders(self):
102
+ """Test getOrders static method."""
103
+ orders = Euler.getOrders()
104
+ expected_orders = ['XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX']
105
+
106
+ self.assertEqual(len(expected_orders), len(orders))
107
+ for order in expected_orders:
108
+ self.assertIn(order, orders)
109
+
110
+ def test_invalid_order(self):
111
+ """Test behavior with invalid rotation order."""
112
+ euler = glm.vec3(0.1, 0.2, 0.3)
113
+
114
+ # This should raise an error for invalid order
115
+ with self.assertRaises(ValueError):
116
+ Euler.fromMatTo(glm.mat3(), order='INVALID', extrinsic=True)
117
+
118
+
119
+ if __name__ == '__main__':
120
+ unittest.main()
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_hierarchy.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ from .utils import *
3
+ from SpatialTransform import Transform
4
+
5
+ class AddRemove(unittest.TestCase):
6
+ def test_attatch(self):
7
+ root = Transform()
8
+ child1 = Transform()
9
+ child2= Transform()
10
+ child3 = Transform()
11
+ child4= Transform()
12
+
13
+ root.attach(child1)
14
+ self.assertEqual(root, child1.Parent)
15
+ self.assertEqual(1, len(root.Children))
16
+
17
+ root.attach(child2)
18
+ self.assertEqual(root, child2.Parent)
19
+ self.assertEqual(2, len(root.Children))
20
+
21
+ root.attach(child3, child4)
22
+ self.assertEqual(root, child3.Parent)
23
+ self.assertEqual(root, child4.Parent)
24
+ self.assertEqual(4, len(root.Children))
25
+
26
+ root.attach(child1)
27
+
28
+
29
+ def test_attatchKeepProperties(self):
30
+ for _ in range(randomSamples):
31
+ root = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
32
+ child1 = Transform()
33
+ child2 = Transform()
34
+
35
+ for _ in range(2):
36
+ root.attach(child1, keep=None)
37
+ self.assertGreater(deltaPosition, glm.distance2(root.SpaceWorld * glm.vec3(0), child1.PositionWorld))
38
+ self.assertGreater(deltaRotation, glm.angle(root.RotationWorld * glm.inverse(child1.RotationWorld)))
39
+ self.assertGreater(deltaScale, glm.distance2(root.ScaleWorld, child1.ScaleWorld))
40
+
41
+ for _ in range(2):
42
+ root.attach(child2, keep=['position', 'rotation', 'scale'])
43
+ self.assertGreater(deltaPosition, glm.distance2(glm.vec3(0), child2.PositionWorld))
44
+ self.assertGreater(deltaRotation, glm.angle(child2.RotationWorld))
45
+ self.assertGreater(deltaScale, glm.distance2(glm.vec3(1), child2.ScaleWorld))
46
+
47
+ def test_detatch(self):
48
+ root = Transform()
49
+ child1 = Transform()
50
+ child2 = Transform()
51
+
52
+ root.attach(child1, child2)
53
+
54
+ root.detach(child1)
55
+ self.assertEqual(None, child1.Parent)
56
+ self.assertEqual(1, len(root.Children))
57
+
58
+ root.detach(child2)
59
+ self.assertEqual(None, child2.Parent)
60
+ self.assertEqual(0, len(root.Children))
61
+
62
+ otherRoot = Transform().attach(child1)
63
+ root.clearChildren().attach(child2)
64
+ root.detach(child1, child2)
65
+ self.assertEqual(1, len(otherRoot.Children))
66
+ self.assertEqual(0, len(root.Children))
67
+ self.assertEqual(otherRoot, child1.Parent)
68
+ self.assertEqual(None, child2.Parent)
69
+
70
+ def test_detatch_Exceptions(self):
71
+ child = Transform()
72
+ root = Transform().attach(child)
73
+ root.Children.remove(child)
74
+
75
+ self.assertRaises(ValueError, root.detach, None)
76
+ self.assertRaises(ValueError, root.detach, root)
77
+ self.assertRaises(ValueError, root.detach, child)
78
+
79
+ def test_attach_exceptions(self):
80
+ """Test attach method exception handling."""
81
+ root = Transform()
82
+
83
+ # Test attaching None
84
+ with self.assertRaises(ValueError):
85
+ root.attach(None)
86
+
87
+ # Test attaching self
88
+ with self.assertRaises(ValueError):
89
+ root.attach(root)
90
+
91
+ def test_attach_already_attached(self):
92
+ """Test attaching already attached child (should not duplicate)."""
93
+ root = Transform()
94
+ child = Transform()
95
+
96
+ root.attach(child)
97
+ initial_count = len(root.Children)
98
+
99
+ # Attach same child again - should not duplicate
100
+ root.attach(child)
101
+ self.assertEqual(initial_count, len(root.Children))
102
+
103
+ def test_detach_unrelated_transform(self):
104
+ """Test detaching transform that isn't a child."""
105
+ root = Transform()
106
+ other_root = Transform()
107
+ child = Transform()
108
+
109
+ other_root.attach(child)
110
+
111
+ # Should not raise error, just do nothing
112
+ initial_count = len(root.Children)
113
+ root.detach(child)
114
+ self.assertEqual(initial_count, len(root.Children))
115
+
116
+ def test_clear_operations_on_empty(self):
117
+ """Test clear operations on transforms with no children/parent."""
118
+ transform = Transform()
119
+
120
+ # Should not raise errors
121
+ transform.clearChildren()
122
+ transform.clearParent()
123
+
124
+ self.assertEqual(0, len(transform.Children))
125
+ self.assertIsNone(transform.Parent)
126
+
127
+ def test_detatchKeepProperties(self):
128
+ for _ in range(randomSamples):
129
+ root = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
130
+ child1 = Transform()
131
+ child2 = Transform()
132
+ root.attach(child1, child2, keep=None)
133
+
134
+ for _ in range(2):
135
+ root.detach(child1, keep=None)
136
+ self.assertGreater(deltaPosition, glm.distance2(glm.vec3(0), child1.PositionWorld))
137
+ self.assertGreater(deltaRotation, glm.angle(child1.RotationWorld))
138
+ self.assertGreater(deltaScale, glm.distance2(glm.vec3(1), child1.ScaleWorld))
139
+
140
+ for _ in range(2):
141
+ root.detach(child2, keep=['position', 'rotation', 'scale'])
142
+ self.assertGreater(deltaPosition, glm.distance2(root.SpaceWorld * glm.vec3(0), child2.PositionWorld))
143
+ self.assertGreater(deltaRotation, glm.angle(root.RotationWorld * glm.inverse(child2.RotationWorld)))
144
+ self.assertGreater(deltaScale, glm.distance2(root.ScaleWorld, child2.ScaleWorld))
145
+
146
+ def test_clearChildren(self):
147
+ root = Transform()
148
+ child1 = Transform()
149
+ child2 = Transform()
150
+
151
+ root.attach(child1)
152
+ root.clearChildren()
153
+ self.assertEqual(None, child1.Parent)
154
+ self.assertEqual(0, len(root.Children))
155
+
156
+ root.attach(child1, child2)
157
+ root.clearChildren()
158
+ self.assertEqual(None, child1.Parent)
159
+ self.assertEqual(None, child2.Parent)
160
+ self.assertEqual(0, len(root.Children))
161
+
162
+ root.clearChildren()
163
+ self.assertEqual(None, child1.Parent)
164
+ self.assertEqual(None, child2.Parent)
165
+ self.assertEqual(0, len(root.Children))
166
+
167
+ def test_clearParent(self):
168
+ root = Transform()
169
+ child1 = Transform()
170
+ child2 = Transform()
171
+
172
+ root.attach(child1, child2)
173
+ child1.clearParent()
174
+ self.assertEqual(None, child1.Parent)
175
+ self.assertEqual(1, len(root.Children))
176
+
177
+ child2.clearParent()
178
+ self.assertEqual(None, child1.Parent)
179
+ self.assertEqual(None, child2.Parent)
180
+ self.assertEqual(0, len(root.Children))
181
+
182
+ child2.clearParent()
183
+ self.assertEqual(None, child1.Parent)
184
+ self.assertEqual(None, child2.Parent)
185
+ self.assertEqual(0, len(root.Children))
186
+
187
+ if __name__ == '__main__':
188
+ unittest.main()
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_methods.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glm
2
+ import unittest
3
+ from .utils import *
4
+ from SpatialTransform import Transform, Euler
5
+
6
+ class Convertions(unittest.TestCase):
7
+ def test_pointToWorld(self):
8
+ for _ in range(randomSamples):
9
+ p = randomPosition() * 10
10
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
11
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
12
+ t.attach(c)
13
+ self.assertEqual(c.pointToWorld(p), (t.Space * c.Space) * p)
14
+
15
+ def test_pointToLocal(self):
16
+ for _ in range(randomSamples):
17
+ p = randomPosition() * 10
18
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
19
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
20
+ t.attach(c)
21
+ self.assertEqual(c.pointToLocal(p), glm.inverse(t.Space * c.Space) * p)
22
+
23
+ def test_directionToWorld(self):
24
+ for _ in range(randomSamples):
25
+ p = randomPosition()
26
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
27
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
28
+ t.attach(c)
29
+ self.assertEqual(c.directionToWorld(p), (t.Rotation * c.Rotation) * p)
30
+
31
+ def test_directionToLocal(self):
32
+ for _ in range(randomSamples):
33
+ p = randomPosition()
34
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
35
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
36
+ t.attach(c)
37
+ self.assertEqual(c.directionToLocal(p), glm.inverse((t.Rotation * c.Rotation)) * p)
38
+
39
+ def test_duplicate(self):
40
+ p = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
41
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
42
+ p.attach(c, keep=None)
43
+ copy = p.duplicate(recursive=True)
44
+
45
+ self.assertNotEqual(copy, p)
46
+ self.assertEqual(copy.Name, p.Name)
47
+ self.assertEqual(copy.Position, p.Position)
48
+ self.assertEqual(copy.Rotation, p.Rotation)
49
+ self.assertEqual(copy.Scale, p.Scale)
50
+ self.assertEqual(copy.Parent, p.Parent)
51
+ self.assertEqual(len(copy.Children), len(p.Children))
52
+
53
+ self.assertNotEqual(copy.Children[0], p.Children[0])
54
+ self.assertEqual(copy.Children[0].Name, p.Children[0].Name)
55
+ self.assertEqual(copy.Children[0].Position, p.Children[0].Position)
56
+ self.assertEqual(copy.Children[0].Rotation, p.Children[0].Rotation)
57
+ self.assertEqual(copy.Children[0].Scale, p.Children[0].Scale)
58
+ self.assertEqual(copy.Children[0].Parent, copy)
59
+
60
+ class Rotations(unittest.TestCase):
61
+ def test_setEuler(self):
62
+ t = Transform()
63
+ t.setEuler((+90, 0, 0), order='ZXY', extrinsic=True)
64
+ self.assertGreater(deltaPosition, glm.distance(glm.vec3(0,+1,0), t.Forward))
65
+ t.setEuler((-90, 0, 0), order='ZXY', extrinsic=True)
66
+ self.assertGreater(deltaPosition, glm.distance(glm.vec3(0,-1,0), t.Forward))
67
+
68
+ t.setEuler((0, +90, 0), order='ZXY', extrinsic=True)
69
+ self.assertGreater(deltaPosition, glm.distance(glm.vec3(-1,0,0), t.Forward))
70
+ t.setEuler((0, -90, 0), order='ZXY', extrinsic=True)
71
+ self.assertGreater(deltaPosition, glm.distance(glm.vec3(+1,0,0), t.Forward))
72
+
73
+ t.setEuler((0, 0, -90), order='ZXY', extrinsic=True)
74
+ self.assertGreater(deltaPosition, glm.distance(glm.vec3(0,0,-1), t.Forward))
75
+ t.setEuler((0, 0, +90), order='ZXY', extrinsic=True)
76
+ self.assertGreater(deltaPosition, glm.distance(glm.vec3(0,0,-1), t.Forward))
77
+
78
+ for _ in range(randomSamples):
79
+ r = randomRotation()
80
+ e = glm.degrees(glm.eulerAngles(r))
81
+ t.setEuler(e, order='XYZ', extrinsic=True)
82
+ angle = glm.angle(r * glm.inverse(t.Rotation))
83
+ self.assertFalse(0.01 < angle < (glm.two_pi()-0.01))
84
+
85
+ def test_lookAtWorld(self):
86
+ for _ in range(randomSamples):
87
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
88
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
89
+ t.attach(c)
90
+ d = randomDirection()
91
+ c.lookAtWorld(d)
92
+ self.assertGreater(deltaPosition, glm.distance(glm.normalize(d), c.ForwardWorld))
93
+ self.assertGreater(deltaPosition, glm.distance(t.RotationWorldInverse * glm.normalize(d), c.Forward))
94
+
95
+ def test_lookAt(self):
96
+ for _ in range(randomSamples):
97
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
98
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
99
+ t.attach(c)
100
+ d = randomDirection()
101
+ c.lookAt(d)
102
+ self.assertGreater(deltaPosition, glm.distance(glm.normalize(d), c.Forward))
103
+ self.assertGreater(deltaPosition, glm.distance(t.RotationWorld * glm.normalize(d), c.ForwardWorld))
104
+
105
+ def test_applyPosition(self):
106
+ for _ in range(randomSamples):
107
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
108
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
109
+ t.attach(c)
110
+ childWorld = c.PositionWorld
111
+ t.applyPosition()
112
+ self.assertGreater(deltaPosition, glm.distance2(glm.vec3(0), t.Position))
113
+ self.assertGreater(deltaPosition, glm.distance2(childWorld, c.PositionWorld))
114
+
115
+ addition = randomPosition()
116
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
117
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
118
+ t.attach(c)
119
+ parent = t.Position
120
+ childWorld = c.PositionWorld
121
+ t.applyPosition(addition)
122
+ self.assertGreater(deltaPosition, glm.distance2(parent + addition, t.Position))
123
+ self.assertGreater(deltaPosition, glm.distance2(childWorld, c.PositionWorld))
124
+
125
+ def test_applyRotation(self):
126
+ for _ in range(randomSamples):
127
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
128
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
129
+ t.attach(c)
130
+ childWorld = c.RotationWorld
131
+ t.applyRotation()
132
+ self.assertGreater(deltaRotation, glm.angle(t.Rotation))
133
+ self.assertGreater(deltaRotation, glm.angle(glm.inverse(c.RotationWorld) * childWorld))
134
+
135
+ addition = randomRotation()
136
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
137
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
138
+ t.attach(c)
139
+ parent = t.Rotation
140
+ childWorld = c.RotationWorld
141
+ t.applyRotation(addition)
142
+ self.assertGreater(deltaRotation, glm.angle((parent * addition) * glm.inverse(t.Rotation)))
143
+ self.assertGreater(deltaRotation, glm.angle(childWorld * glm.inverse(c.RotationWorld)))
144
+
145
+ def test_applyScale(self):
146
+ for _ in range(randomSamples):
147
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
148
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
149
+ t.attach(c)
150
+ childWorld = c.ScaleWorld
151
+ t.applyScale()
152
+ self.assertGreater(deltaPosition, glm.distance2(glm.vec3(1), t.Scale))
153
+ self.assertGreater(deltaPosition, glm.distance2(childWorld, c.ScaleWorld))
154
+
155
+ addition = randomScale()
156
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
157
+ c = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
158
+ t.attach(c)
159
+ parent = t.Scale
160
+ childWorld = c.ScaleWorld
161
+ t.applyScale(addition)
162
+ self.assertGreater(deltaPosition, glm.distance2(parent * addition, t.Scale))
163
+ self.assertGreater(deltaPosition, glm.distance2(childWorld, c.ScaleWorld))
164
+
165
+ if __name__ == '__main__':
166
+ unittest.main()
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_pose.py ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glm
2
+ import unittest
3
+ from .utils import *
4
+ from SpatialTransform import Pose, Euler
5
+
6
+
7
+ class PoseProperties(unittest.TestCase):
8
+ def test_init_default(self):
9
+ """Test default initialization."""
10
+ p = Pose()
11
+ self.assertEqual(glm.vec3(0), p.Position)
12
+ self.assertEqual(glm.quat(), p.Rotation)
13
+ self.assertEqual(glm.vec3(1), p.Scale)
14
+ self.assertEqual(glm.mat4(), p.Space)
15
+ self.assertEqual(glm.vec3(0, 0, -1), p.Forward)
16
+ self.assertEqual(glm.vec3(1, 0, 0), p.Right)
17
+ self.assertEqual(glm.vec3(0, 1, 0), p.Up)
18
+
19
+ def test_init_with_parameters(self):
20
+ """Test initialization with custom parameters."""
21
+ for _ in range(randomSamples):
22
+ position = randomPosition()
23
+ rotation = randomRotation()
24
+ scale = randomScale()
25
+
26
+ p = Pose(position=position, rotation=rotation, scale=scale)
27
+ self.assertEqual(position, p.Position)
28
+ self.assertEqual(rotation, p.Rotation)
29
+ self.assertEqual(scale, p.Scale)
30
+
31
+ def test_space_matrix(self):
32
+ """Test the Space property calculation."""
33
+ for _ in range(randomSamples):
34
+ position = randomPosition()
35
+ rotation = randomRotation()
36
+ scale = randomScale()
37
+
38
+ p = Pose(position=position, rotation=rotation, scale=scale)
39
+ expected = (glm.translate(position) * glm.scale(scale)) * glm.mat4(rotation)
40
+ self.assertEqual(expected, p.Space)
41
+
42
+ def test_space_inverse(self):
43
+ """Test the SpaceInverse property."""
44
+ for _ in range(randomSamples):
45
+ p = Pose(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
46
+ expected = glm.inverse(p.Space)
47
+ self.assertEqual(expected, p.SpaceInverse)
48
+
49
+ def test_position_setter(self):
50
+ """Test Position property setter."""
51
+ p = Pose()
52
+ for _ in range(randomSamples):
53
+ position = randomPosition()
54
+ p.Position = position
55
+ self.assertEqual(position, p.Position)
56
+
57
+ def test_rotation_setter(self):
58
+ """Test Rotation property setter."""
59
+ p = Pose()
60
+ for _ in range(randomSamples):
61
+ rotation = randomRotation()
62
+ p.Rotation = rotation
63
+ self.assertEqual(rotation, p.Rotation)
64
+ # Test directional vectors
65
+ self.assertEqual(rotation * glm.vec3(0, 0, -1), p.Forward)
66
+ self.assertEqual(rotation * glm.vec3(1, 0, 0), p.Right)
67
+ self.assertEqual(rotation * glm.vec3(0, 1, 0), p.Up)
68
+
69
+ def test_scale_setter(self):
70
+ """Test Scale property setter."""
71
+ p = Pose()
72
+ for _ in range(randomSamples):
73
+ scale = randomScale()
74
+ p.Scale = scale
75
+ self.assertEqual(scale, p.Scale)
76
+
77
+ def test_directional_vectors(self):
78
+ """Test Forward, Right, Up properties."""
79
+ for _ in range(randomSamples):
80
+ rotation = randomRotation()
81
+ p = Pose(rotation=rotation)
82
+
83
+ expected_forward = rotation * glm.vec3(0, 0, -1)
84
+ expected_right = rotation * glm.vec3(1, 0, 0)
85
+ expected_up = rotation * glm.vec3(0, 1, 0)
86
+
87
+ self.assertGreater(deltaPosition, glm.distance(expected_forward, p.Forward))
88
+ self.assertGreater(deltaPosition, glm.distance(expected_right, p.Right))
89
+ self.assertGreater(deltaPosition, glm.distance(expected_up, p.Up))
90
+
91
+
92
+ class PoseMethods(unittest.TestCase):
93
+ def test_reset(self):
94
+ """Test reset method."""
95
+ p = Pose(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
96
+ p.reset()
97
+
98
+ self.assertEqual(glm.vec3(0), p.Position)
99
+ self.assertEqual(glm.quat(), p.Rotation)
100
+ self.assertEqual(glm.vec3(1), p.Scale)
101
+
102
+ def test_duplicate(self):
103
+ """Test duplicate method."""
104
+ for _ in range(randomSamples):
105
+ original = Pose(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
106
+ duplicate = original.duplicate()
107
+
108
+ # Should be different objects
109
+ self.assertNotEqual(id(original), id(duplicate))
110
+
111
+ # But same values
112
+ self.assertEqual(original.Position, duplicate.Position)
113
+ self.assertEqual(original.Rotation, duplicate.Rotation)
114
+ self.assertEqual(original.Scale, duplicate.Scale)
115
+
116
+ def test_look_at(self):
117
+ """Test lookAt method."""
118
+ p = Pose()
119
+
120
+ # Test basic directions
121
+ directions = [
122
+ glm.vec3(1, 0, 0), # Right
123
+ glm.vec3(-1, 0, 0), # Left
124
+ glm.vec3(0, 1, 0), # Up
125
+ glm.vec3(0, -1, 0), # Down
126
+ glm.vec3(0, 0, 1), # Back
127
+ glm.vec3(0, 0, -1), # Forward
128
+ ]
129
+
130
+ for direction in directions:
131
+ p.lookAt(direction)
132
+ self.assertGreater(deltaPosition, glm.distance(glm.normalize(direction), p.Forward))
133
+
134
+ # Test random directions
135
+ for _ in range(randomSamples):
136
+ direction = randomDirection()
137
+ p.lookAt(direction)
138
+ self.assertGreater(deltaPosition, glm.distance(glm.normalize(direction), p.Forward))
139
+
140
+ def test_look_at_with_up(self):
141
+ """Test lookAt method with custom up vector."""
142
+ p = Pose()
143
+ direction = glm.vec3(1, 0, 0)
144
+ up = glm.vec3(0, 0, 1)
145
+
146
+ p.lookAt(direction, up)
147
+ self.assertGreater(deltaPosition, glm.distance(glm.normalize(direction), p.Forward))
148
+
149
+ def test_get_euler(self):
150
+ """Test getEuler method."""
151
+ for _ in range(randomSamples):
152
+ rotation = randomRotation()
153
+ p = Pose(rotation=rotation)
154
+
155
+ # Test different orders
156
+ orders = ['XYZ', 'XZY', 'YXZ', 'YZX', 'ZXY', 'ZYX']
157
+ for order in orders:
158
+ for extrinsic in [True, False]:
159
+ euler = p.getEuler(order=order, extrinsic=extrinsic)
160
+ self.assertIsInstance(euler, glm.vec3)
161
+
162
+ def test_set_euler(self):
163
+ """Test setEuler method."""
164
+ p = Pose()
165
+
166
+ for _ in range(randomSamples):
167
+ euler_degrees = glm.vec3(
168
+ random.uniform(-180, 180),
169
+ random.uniform(-180, 180),
170
+ random.uniform(-180, 180)
171
+ )
172
+
173
+ result = p.setEuler(euler_degrees)
174
+ self.assertEqual(p, result) # Should return self
175
+
176
+ # Verify rotation was set
177
+ self.assertNotEqual(glm.quat(), p.Rotation)
178
+
179
+ def test_add_euler(self):
180
+ """Test addEuler method."""
181
+ p = Pose(rotation=randomRotation())
182
+ original_rotation = p.Rotation
183
+
184
+ euler_degrees = glm.vec3(10, 20, 30)
185
+
186
+ # Test last=True
187
+ result = p.addEuler(euler_degrees, last=True)
188
+ self.assertEqual(p, result) # Should return self
189
+ self.assertNotEqual(original_rotation, p.Rotation)
190
+
191
+ # Test last=False
192
+ p.Rotation = original_rotation
193
+ p.addEuler(euler_degrees, last=False)
194
+ self.assertNotEqual(original_rotation, p.Rotation)
195
+
196
+ def test_euler_round_trip(self):
197
+ """Test setEuler and getEuler round trip."""
198
+ p = Pose()
199
+
200
+ for _ in range(100): # Use fewer samples for complex test
201
+ euler_degrees = glm.vec3(
202
+ random.uniform(-89, 89), # Avoid gimbal lock
203
+ random.uniform(-89, 89),
204
+ random.uniform(-89, 89)
205
+ )
206
+
207
+ p.setEuler(euler_degrees, order='XYZ', extrinsic=True)
208
+ retrieved_euler = p.getEuler(order='XYZ', extrinsic=True)
209
+
210
+ # Allow for small numerical differences
211
+ self.assertGreater(1.0, glm.distance(euler_degrees, retrieved_euler))
212
+
213
+
214
+ class PoseRepresentation(unittest.TestCase):
215
+ def test_repr(self):
216
+ """Test __repr__ method."""
217
+ p = Pose(position=glm.vec3(1, 2, 3), rotation=glm.quat(1, 0, 0, 0), scale=glm.vec3(2, 2, 2))
218
+ repr_str = repr(p)
219
+ self.assertIn("Pos:", repr_str)
220
+ self.assertIn("Rot:", repr_str)
221
+ self.assertIn("Scale:", repr_str)
222
+
223
+ def test_str(self):
224
+ """Test __str__ method."""
225
+ p = Pose(position=glm.vec3(1, 2, 3), rotation=glm.quat(1, 0, 0, 0), scale=glm.vec3(2, 2, 2))
226
+ str_repr = str(p)
227
+ self.assertIn("Pos:", str_repr)
228
+ self.assertIn("Rot:", str_repr)
229
+ self.assertIn("Scale:", str_repr)
230
+
231
+
232
+ if __name__ == '__main__':
233
+ unittest.main()
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_properties.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glm
2
+ import unittest
3
+ from .utils import *
4
+ from SpatialTransform import Transform
5
+
6
+
7
+ class Local (unittest.TestCase):
8
+ def test_Init(self):
9
+ t = Transform()
10
+ self.assertEqual(glm.mat4(), t.Space)
11
+ self.assertEqual(glm.vec3(0), t.Position)
12
+ self.assertEqual(glm.quat(), t.Rotation)
13
+ self.assertEqual(glm.vec3(1), t.Scale)
14
+ self.assertEqual(glm.vec3(0,0,-1), t.Forward)
15
+ self.assertEqual(glm.vec3(1,0,0), t.Right)
16
+ self.assertEqual(glm.vec3(0,1,0), t.Up)
17
+
18
+ for _ in range(randomSamples):
19
+ position = randomPosition()
20
+ rotation = randomRotation()
21
+ scale = randomScale()
22
+
23
+ expected = (glm.translate(position) * glm.scale(scale)) * glm.mat4(rotation)
24
+ result = Transform(position=position, rotation=rotation, scale=scale)
25
+
26
+ self.assertEqual(expected, result.Space)
27
+ self.assertEqual(position, result.Position)
28
+ self.assertEqual(rotation, result.Rotation)
29
+ self.assertEqual(scale, result.Scale)
30
+ self.assertEqual(rotation * glm.vec3(0,0,-1), result.Forward)
31
+ self.assertEqual(rotation * glm.vec3(1,0,0), result.Right)
32
+ self.assertEqual(rotation * glm.vec3(0,1,0), result.Up)
33
+
34
+ def test_Reset(self):
35
+ t = Transform().reset()
36
+ self.assertEqual(glm.mat4(), t.Space)
37
+ self.assertEqual(glm.vec3(0), t.Position)
38
+ self.assertEqual(glm.quat(), t.Rotation)
39
+ self.assertEqual(glm.vec3(1), t.Scale)
40
+
41
+ t = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale()).reset()
42
+ self.assertEqual(glm.mat4(), t.Space)
43
+ self.assertEqual(glm.vec3(0), t.Position)
44
+ self.assertEqual(glm.quat(), t.Rotation)
45
+ self.assertEqual(glm.vec3(1), t.Scale)
46
+
47
+ def test_Space(self):
48
+ t = Transform()
49
+ for _ in range(randomSamples):
50
+ t.Position = randomPosition()
51
+ t.Rotation = randomRotation()
52
+ t.Scale = randomScale()
53
+ space = (glm.translate(t.Position) * glm.scale(t.Scale)) * glm.mat4(t.Rotation)
54
+ self.assertEqual(space, t.Space)
55
+
56
+ def test_Position(self):
57
+ t = Transform()
58
+ for _ in range(randomSamples):
59
+ position = randomPosition()
60
+ space = glm.translate(position)
61
+ t.Position = position
62
+ self.assertEqual(position, t.Position)
63
+ self.assertEqual(space, t.Space)
64
+
65
+ def test_Rotation(self):
66
+ t = Transform()
67
+ for _ in range(randomSamples):
68
+ rotation = randomRotation()
69
+ space = glm.mat4(rotation)
70
+ t.Rotation = rotation
71
+ self.assertGreater(deltaRotation, abs(1 - glm.length(t.Rotation)))
72
+ self.assertEqual(rotation, t.Rotation)
73
+ self.assertEqual(space, t.Space)
74
+ self.assertEqual(rotation * glm.vec3(0,0,-1), t.Forward)
75
+ self.assertEqual(rotation * glm.vec3(1,0,0), t.Right)
76
+ self.assertEqual(rotation * glm.vec3(0,1,0), t.Up)
77
+
78
+ def test_Scale(self):
79
+ t = Transform()
80
+ for _ in range(randomSamples):
81
+ scale = randomScale()
82
+ space = glm.scale(scale)
83
+ t.Scale = scale
84
+ self.assertEqual(scale, t.Scale)
85
+ self.assertEqual(space, t.Space)
86
+
87
+ class World(unittest.TestCase):
88
+ def test_Init(self):
89
+ root = Transform()
90
+ child = Transform()
91
+ root.attach(child)
92
+
93
+ self.assertEqual(glm.mat4(), child.SpaceWorld)
94
+ self.assertEqual(glm.vec3(0), child.PositionWorld)
95
+ self.assertEqual(glm.quat(), child.RotationWorld)
96
+ self.assertEqual(glm.vec3(1), child.ScaleWorld)
97
+ self.assertEqual(glm.vec3(0,0,-1), child.ForwardWorld)
98
+ self.assertEqual(glm.vec3(1,0,0), child.RightWorld)
99
+ self.assertEqual(glm.vec3(0,1,0), child.UpWorld)
100
+
101
+ for _ in range(randomSamples):
102
+ root = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
103
+ child = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
104
+ root.attach(child)
105
+ self.assertEqual(root.Space * child.Space, child.SpaceWorld)
106
+ self.assertEqual(glm.inverse(root.Space * child.Space), child.SpaceWorldInverse)
107
+
108
+ def test_Reset(self):
109
+ root = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
110
+ child = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
111
+ root.attach(child)
112
+
113
+ child.reset()
114
+ self.assertEqual(child.SpaceWorld, root.SpaceWorld)
115
+ self.assertEqual(child.PositionWorld, root.PositionWorld)
116
+ self.assertEqual(child.RotationWorld, root.RotationWorld)
117
+ self.assertEqual(child.ScaleWorld, root.ScaleWorld)
118
+
119
+ root.reset()
120
+ self.assertEqual(glm.mat4(), child.SpaceWorld)
121
+ self.assertEqual(glm.vec3(0), child.PositionWorld)
122
+ self.assertEqual(glm.quat(), child.RotationWorld)
123
+ self.assertEqual(glm.vec3(1), child.ScaleWorld)
124
+
125
+ def test_Space(self):
126
+ root = Transform()
127
+ child = Transform()
128
+ root.attach(child)
129
+ for _ in range(randomSamples):
130
+ root.Position = randomPosition()
131
+ root.Rotation = randomRotation()
132
+ root.Scale = randomScale()
133
+
134
+ child.Position = randomPosition()
135
+ child.Rotation = randomRotation()
136
+ child.Scale = randomScale()
137
+
138
+ self.assertEqual(root.Space * child.Space, child.SpaceWorld)
139
+ self.assertEqual(glm.inverse(root.Space * child.Space), child.SpaceWorldInverse)
140
+
141
+ def test_Position(self):
142
+ root = Transform()
143
+ child = Transform()
144
+ root.attach(child)
145
+ for _ in range(randomSamples):
146
+ root.Position = randomPosition()
147
+ child.Position = randomPosition()
148
+ self.assertEqual(root.Space * child.Position, child.PositionWorld)
149
+
150
+ def test_Rotation(self):
151
+ root = Transform()
152
+ child = Transform()
153
+ root.attach(child)
154
+ for _ in range(randomSamples):
155
+ root.Rotation = randomRotation()
156
+ child.Rotation = randomRotation()
157
+ self.assertEqual(root.Rotation * child.Rotation, child.RotationWorld)
158
+ self.assertGreater(deltaRotation, abs(1 - glm.length(child.RotationWorld)))
159
+ self.assertEqual((root.Rotation * child.Rotation) * glm.vec3(0,0,-1), child.ForwardWorld)
160
+ self.assertEqual((root.Rotation * child.Rotation) * glm.vec3(1,0,0), child.RightWorld)
161
+ self.assertEqual((root.Rotation * child.Rotation) * glm.vec3(0,1,0), child.UpWorld)
162
+
163
+ def test_Scale(self):
164
+ root = Transform()
165
+ child = Transform()
166
+ root.attach(child)
167
+ for _ in range(randomSamples):
168
+ root.Scale = randomScale()
169
+ child.Scale = randomScale()
170
+ self.assertEqual(root.Scale * child.Scale, child.ScaleWorld)
171
+
172
+ if __name__ == '__main__':
173
+ unittest.main()
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/test_transform_extended.py ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glm
2
+ import unittest
3
+ import re
4
+ from .utils import *
5
+ from SpatialTransform import Transform, Pose
6
+
7
+
8
+ class TransformUtilities(unittest.TestCase):
9
+ def test_name_property(self):
10
+ """Test Name property getter and setter."""
11
+ t = Transform()
12
+ # Default name should be 8 random characters
13
+ self.assertEqual(8, len(t.Name))
14
+
15
+ # Test custom name
16
+ custom_name = "TestTransform"
17
+ t.Name = custom_name
18
+ self.assertEqual(custom_name, t.Name)
19
+
20
+ # Test name in constructor
21
+ t2 = Transform(name="ConstructorName")
22
+ self.assertEqual("ConstructorName", t2.Name)
23
+
24
+ def test_layout(self):
25
+ """Test layout method."""
26
+ root = Transform(name="Root")
27
+ child1 = Transform(name="Child1")
28
+ child2 = Transform(name="Child2")
29
+ grandchild = Transform(name="GrandChild")
30
+
31
+ root.attach(child1, child2)
32
+ child1.attach(grandchild)
33
+
34
+ layout = root.layout()
35
+
36
+ # Should return list of tuples [transform, index, depth]
37
+ self.assertEqual(4, len(layout))
38
+
39
+ # Check root
40
+ self.assertEqual(root, layout[0][0])
41
+ self.assertEqual(0, layout[0][1]) # index
42
+ self.assertEqual(0, layout[0][2]) # depth
43
+
44
+ # Check child1
45
+ self.assertEqual(child1, layout[1][0])
46
+ self.assertEqual(1, layout[1][1])
47
+ self.assertEqual(1, layout[1][2])
48
+
49
+ # Check grandchild
50
+ self.assertEqual(grandchild, layout[2][0])
51
+ self.assertEqual(2, layout[2][1])
52
+ self.assertEqual(2, layout[2][2])
53
+
54
+ # Check child2
55
+ self.assertEqual(child2, layout[3][0])
56
+ self.assertEqual(3, layout[3][1])
57
+ self.assertEqual(1, layout[3][2])
58
+
59
+ def test_filter(self):
60
+ """Test filter method."""
61
+ root = Transform(name="RootNode")
62
+ child1 = Transform(name="TestChild")
63
+ child2 = Transform(name="AnotherChild")
64
+ child3 = Transform(name="TestNode")
65
+
66
+ root.attach(child1, child2, child3)
67
+
68
+ # Test partial match (default)
69
+ results = root.filter("Test")
70
+ self.assertEqual(2, len(results))
71
+ self.assertIn(child1, results)
72
+ self.assertIn(child3, results)
73
+
74
+ # Test exact match
75
+ results = root.filter("TestChild", isEqual=True)
76
+ self.assertEqual(1, len(results))
77
+ self.assertEqual(child1, results[0])
78
+
79
+ # Test case sensitivity
80
+ results = root.filter("test", caseSensitive=False)
81
+ self.assertEqual(2, len(results))
82
+
83
+ results = root.filter("test", caseSensitive=True)
84
+ self.assertEqual(0, len(results))
85
+
86
+ # Test no matches
87
+ results = root.filter("NonExistent")
88
+ self.assertEqual(0, len(results))
89
+
90
+ def test_filter_regex(self):
91
+ """Test filterRegex method."""
92
+ root = Transform(name="Root")
93
+ child1 = Transform(name="Test123")
94
+ child2 = Transform(name="AnotherChild")
95
+ child3 = Transform(name="Test456")
96
+
97
+ root.attach(child1, child2, child3)
98
+
99
+ # Test regex pattern
100
+ results = root.filterRegex(r"Test\d+")
101
+ self.assertEqual(2, len(results))
102
+ self.assertIn(child1, results)
103
+ self.assertIn(child3, results)
104
+
105
+ # Test exact match regex
106
+ results = root.filterRegex("^Root$")
107
+ self.assertEqual(1, len(results))
108
+ self.assertEqual(root, results[0])
109
+
110
+ # Test no matches
111
+ results = root.filterRegex(r"Node\d+")
112
+ self.assertEqual(0, len(results))
113
+
114
+ def test_print_tree(self):
115
+ """Test printTree method (verify it doesn't crash)."""
116
+ root = Transform(name="Root")
117
+ child1 = Transform(name="Child1")
118
+ child2 = Transform(name="Child2")
119
+ grandchild = Transform(name="GrandChild")
120
+
121
+ root.attach(child1, child2)
122
+ child1.attach(grandchild)
123
+
124
+ # This should not crash
125
+ import io
126
+ import sys
127
+ captured_output = io.StringIO()
128
+ sys.stdout = captured_output
129
+ root.printTree()
130
+ sys.stdout = sys.__stdout__
131
+
132
+ output = captured_output.getvalue()
133
+ self.assertIn("Root", output)
134
+ self.assertIn("Child1", output)
135
+ self.assertIn("Child2", output)
136
+ self.assertIn("GrandChild", output)
137
+
138
+ def test_to_pose(self):
139
+ """Test toPose method."""
140
+ for _ in range(randomSamples):
141
+ position = randomPosition()
142
+ rotation = randomRotation()
143
+ scale = randomScale()
144
+
145
+ t = Transform(position=position, rotation=rotation, scale=scale)
146
+
147
+ # Test local space
148
+ pose_local = t.toPose(worldSpace=False)
149
+ self.assertEqual(position, pose_local.Position)
150
+ self.assertEqual(rotation, pose_local.Rotation)
151
+ self.assertEqual(scale, pose_local.Scale)
152
+
153
+ # Test world space with parent
154
+ parent = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
155
+ parent.attach(t)
156
+
157
+ pose_world = t.toPose(worldSpace=True)
158
+ self.assertEqual(t.PositionWorld, pose_world.Position)
159
+ self.assertEqual(t.RotationWorld, pose_world.Rotation)
160
+ self.assertEqual(t.ScaleWorld, pose_world.Scale)
161
+
162
+ def test_from_pose(self):
163
+ """Test fromPose static method."""
164
+ for _ in range(randomSamples):
165
+ pose = Pose(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
166
+
167
+ # Test without name
168
+ t = Transform.fromPose(pose)
169
+ self.assertEqual(pose.Position, t.Position)
170
+ self.assertEqual(pose.Rotation, t.Rotation)
171
+ self.assertEqual(pose.Scale, t.Scale)
172
+
173
+ # Test with name
174
+ custom_name = "FromPoseTest"
175
+ t_named = Transform.fromPose(pose, name=custom_name)
176
+ self.assertEqual(custom_name, t_named.Name)
177
+ self.assertEqual(pose.Position, t_named.Position)
178
+ self.assertEqual(pose.Rotation, t_named.Rotation)
179
+ self.assertEqual(pose.Scale, t_named.Scale)
180
+
181
+
182
+ class TransformEdgeCases(unittest.TestCase):
183
+ def test_duplicate_recursive(self):
184
+ """Test duplicate with recursive=True."""
185
+ root = Transform(name="Root", position=randomPosition(), rotation=randomRotation(), scale=randomScale())
186
+ child1 = Transform(name="Child1", position=randomPosition(), rotation=randomRotation(), scale=randomScale())
187
+ child2 = Transform(name="Child2", position=randomPosition(), rotation=randomRotation(), scale=randomScale())
188
+ grandchild = Transform(name="GrandChild", position=randomPosition(), rotation=randomRotation(), scale=randomScale())
189
+
190
+ root.attach(child1, child2)
191
+ child1.attach(grandchild)
192
+
193
+ # Test recursive duplicate
194
+ duplicate = root.duplicate(recursive=True)
195
+
196
+ # Should be different objects
197
+ self.assertNotEqual(root, duplicate)
198
+ self.assertNotEqual(child1, duplicate.Children[0])
199
+
200
+ # But same properties
201
+ self.assertEqual(root.Name, duplicate.Name)
202
+ self.assertEqual(len(root.Children), len(duplicate.Children))
203
+ self.assertEqual(child1.Name, duplicate.Children[0].Name)
204
+
205
+ # Check hierarchy integrity
206
+ self.assertEqual(duplicate, duplicate.Children[0].Parent)
207
+ self.assertEqual(1, len(duplicate.Children[0].Children)) # grandchild
208
+
209
+ def test_duplicate_non_recursive(self):
210
+ """Test duplicate with recursive=False."""
211
+ root = Transform(name="Root", position=randomPosition(), rotation=randomRotation(), scale=randomScale())
212
+ child = Transform(name="Child", position=randomPosition(), rotation=randomRotation(), scale=randomScale())
213
+
214
+ root.attach(child)
215
+
216
+ # Test non-recursive duplicate
217
+ duplicate = root.duplicate(recursive=False)
218
+
219
+ # Should be different object
220
+ self.assertNotEqual(root, duplicate)
221
+
222
+ # Same properties but no children
223
+ self.assertEqual(root.Name, duplicate.Name)
224
+ self.assertEqual(0, len(duplicate.Children))
225
+
226
+ def test_reset_recursive(self):
227
+ """Test reset with recursive=True."""
228
+ root = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
229
+ child = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
230
+
231
+ root.attach(child)
232
+
233
+ # Reset recursively
234
+ root.reset(recursive=True)
235
+
236
+ # Both should be reset
237
+ self.assertEqual(glm.vec3(0), root.Position)
238
+ self.assertEqual(glm.quat(), root.Rotation)
239
+ self.assertEqual(glm.vec3(1), root.Scale)
240
+
241
+ self.assertEqual(glm.vec3(0), child.Position)
242
+ self.assertEqual(glm.quat(), child.Rotation)
243
+ self.assertEqual(glm.vec3(1), child.Scale)
244
+
245
+ def test_world_property_setters(self):
246
+ """Test world space property setters."""
247
+ parent = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
248
+ child = Transform(position=randomPosition(), rotation=randomRotation(), scale=randomScale())
249
+ parent.attach(child)
250
+
251
+ # Test PositionWorld setter
252
+ new_world_pos = randomPosition()
253
+ child.PositionWorld = new_world_pos
254
+ self.assertGreater(deltaPosition, glm.distance2(new_world_pos, child.PositionWorld))
255
+
256
+ # Test RotationWorld setter
257
+ new_world_rot = randomRotation()
258
+ child.RotationWorld = new_world_rot
259
+ self.assertGreater(deltaRotation, glm.angle(new_world_rot * glm.inverse(child.RotationWorld)))
260
+
261
+ # Test ScaleWorld setter
262
+ new_world_scale = randomScale()
263
+ child.ScaleWorld = new_world_scale
264
+ self.assertGreater(deltaPosition, glm.distance2(new_world_scale, child.ScaleWorld))
265
+
266
+
267
+ if __name__ == '__main__':
268
+ unittest.main()
env/kimodo/lib/python3.10/site-packages/SpatialTransform/tests/utils.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import glm
2
+ import math
3
+ import random
4
+
5
+ deltaPosition = 1e-05
6
+ deltaRotation = 1e-05
7
+ deltaScale = 1e-05
8
+ randomSamples = 1000
9
+
10
+
11
+ def randomPosition():
12
+ return glm.vec3(random.random(), random.random(), random.random()) * 2 - 1
13
+
14
+ def randomDirection():
15
+ result = glm.vec3(random.random(), random.random(), random.random())
16
+ theta = result.x * math.pi * 2.0
17
+ phi = math.acos((2.0 * result.y) - 1.0)
18
+ r = pow(result.z, 1/3)
19
+
20
+ result.x = r * math.sin(phi) * math.cos(theta)
21
+ result.y = r * math.sin(phi) * math.sin(theta)
22
+ result.z = r * math.cos(phi)
23
+
24
+ return glm.normalize(result)
25
+
26
+ def randomRotation():
27
+ return glm.normalize(glm.quatLookAtRH(randomDirection(), (0,1,0)))
28
+
29
+ def randomScale():
30
+ return (glm.vec3(random.random(), random.random(), random.random()) + 0.01) * \
31
+ glm.vec3(-1 if random.random()<0.5 else 1, -1 if random.random()<0.5 else 1, -1 if random.random()<0.5 else 1)
env/kimodo/lib/python3.10/site-packages/_yaml/__init__.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This is a stub package designed to roughly emulate the _yaml
2
+ # extension module, which previously existed as a standalone module
3
+ # and has been moved into the `yaml` package namespace.
4
+ # It does not perfectly mimic its old counterpart, but should get
5
+ # close enough for anyone who's relying on it even when they shouldn't.
6
+ import yaml
7
+
8
+ # in some circumstances, the yaml module we imoprted may be from a different version, so we need
9
+ # to tread carefully when poking at it here (it may not have the attributes we expect)
10
+ if not getattr(yaml, '__with_libyaml__', False):
11
+ from sys import version_info
12
+
13
+ exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError
14
+ raise exc("No module named '_yaml'")
15
+ else:
16
+ from yaml._yaml import *
17
+ import warnings
18
+ warnings.warn(
19
+ 'The _yaml extension module is now located at yaml._yaml'
20
+ ' and its location is subject to change. To use the'
21
+ ' LibYAML-based parser and emitter, import from `yaml`:'
22
+ ' `from yaml import CLoader as Loader, CDumper as Dumper`.',
23
+ DeprecationWarning
24
+ )
25
+ del warnings
26
+ # Don't `del yaml` here because yaml is actually an existing
27
+ # namespace member of _yaml.
28
+
29
+ __name__ = '_yaml'
30
+ # If the module is top-level (i.e. not a part of any specific package)
31
+ # then the attribute should be set to ''.
32
+ # https://docs.python.org/3.8/library/types.html
33
+ __package__ = ''
env/kimodo/lib/python3.10/site-packages/_yaml/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (729 Bytes). View file
 
env/kimodo/lib/python3.10/site-packages/multipart/__init__.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This only works if using a file system, other loaders not implemented.
2
+
3
+ import importlib.util
4
+ import sys
5
+ import warnings
6
+ from pathlib import Path
7
+
8
+ for p in sys.path:
9
+ file_path = Path(p, "multipart.py")
10
+ try:
11
+ if file_path.is_file():
12
+ spec = importlib.util.spec_from_file_location("multipart", file_path)
13
+ assert spec is not None, f"{file_path} found but not loadable!"
14
+ module = importlib.util.module_from_spec(spec)
15
+ sys.modules["multipart"] = module
16
+ assert spec.loader is not None, f"{file_path} must be loadable!"
17
+ spec.loader.exec_module(module)
18
+ break
19
+ except PermissionError:
20
+ pass
21
+ else:
22
+ warnings.warn("Please use `import python_multipart` instead.", PendingDeprecationWarning, stacklevel=2)
23
+ from python_multipart import *
24
+ from python_multipart import __all__, __author__, __copyright__, __license__, __version__
env/kimodo/lib/python3.10/site-packages/multipart/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (942 Bytes). View file
 
env/kimodo/lib/python3.10/site-packages/multipart/__pycache__/decoders.cpython-310.pyc ADDED
Binary file (213 Bytes). View file
 
env/kimodo/lib/python3.10/site-packages/multipart/__pycache__/exceptions.cpython-310.pyc ADDED
Binary file (217 Bytes). View file
 
env/kimodo/lib/python3.10/site-packages/multipart/__pycache__/multipart.cpython-310.pyc ADDED
Binary file (215 Bytes). View file
 
env/kimodo/lib/python3.10/site-packages/multipart/decoders.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from python_multipart.decoders import *
env/kimodo/lib/python3.10/site-packages/multipart/exceptions.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from python_multipart.exceptions import *
env/kimodo/lib/python3.10/site-packages/multipart/multipart.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from python_multipart.multipart import *
env/kimodo/lib/python3.10/site-packages/pygments/__init__.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Pygments
3
+ ~~~~~~~~
4
+
5
+ Pygments is a syntax highlighting package written in Python.
6
+
7
+ It is a generic syntax highlighter for general use in all kinds of software
8
+ such as forum systems, wikis or other applications that need to prettify
9
+ source code. Highlights are:
10
+
11
+ * a wide range of common languages and markup formats is supported
12
+ * special attention is paid to details, increasing quality by a fair amount
13
+ * support for new languages and formats are added easily
14
+ * a number of output formats, presently HTML, LaTeX, RTF, SVG, all image
15
+ formats that PIL supports, and ANSI sequences
16
+ * it is usable as a command-line tool and as a library
17
+ * ... and it highlights even Brainfuck!
18
+
19
+ The `Pygments master branch`_ is installable with ``easy_install Pygments==dev``.
20
+
21
+ .. _Pygments master branch:
22
+ https://github.com/pygments/pygments/archive/master.zip#egg=Pygments-dev
23
+
24
+ :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
25
+ :license: BSD, see LICENSE for details.
26
+ """
27
+ from io import StringIO, BytesIO
28
+
29
+ __version__ = '2.19.2'
30
+ __docformat__ = 'restructuredtext'
31
+
32
+ __all__ = ['lex', 'format', 'highlight']
33
+
34
+
35
+ def lex(code, lexer):
36
+ """
37
+ Lex `code` with the `lexer` (must be a `Lexer` instance)
38
+ and return an iterable of tokens. Currently, this only calls
39
+ `lexer.get_tokens()`.
40
+ """
41
+ try:
42
+ return lexer.get_tokens(code)
43
+ except TypeError:
44
+ # Heuristic to catch a common mistake.
45
+ from pygments.lexer import RegexLexer
46
+ if isinstance(lexer, type) and issubclass(lexer, RegexLexer):
47
+ raise TypeError('lex() argument must be a lexer instance, '
48
+ 'not a class')
49
+ raise
50
+
51
+
52
+ def format(tokens, formatter, outfile=None): # pylint: disable=redefined-builtin
53
+ """
54
+ Format ``tokens`` (an iterable of tokens) with the formatter ``formatter``
55
+ (a `Formatter` instance).
56
+
57
+ If ``outfile`` is given and a valid file object (an object with a
58
+ ``write`` method), the result will be written to it, otherwise it
59
+ is returned as a string.
60
+ """
61
+ try:
62
+ if not outfile:
63
+ realoutfile = getattr(formatter, 'encoding', None) and BytesIO() or StringIO()
64
+ formatter.format(tokens, realoutfile)
65
+ return realoutfile.getvalue()
66
+ else:
67
+ formatter.format(tokens, outfile)
68
+ except TypeError:
69
+ # Heuristic to catch a common mistake.
70
+ from pygments.formatter import Formatter
71
+ if isinstance(formatter, type) and issubclass(formatter, Formatter):
72
+ raise TypeError('format() argument must be a formatter instance, '
73
+ 'not a class')
74
+ raise
75
+
76
+
77
+ def highlight(code, lexer, formatter, outfile=None):
78
+ """
79
+ This is the most high-level highlighting function. It combines `lex` and
80
+ `format` in one function.
81
+ """
82
+ return format(lex(code, lexer), formatter, outfile)
env/kimodo/lib/python3.10/site-packages/pygments/__main__.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.__main__
3
+ ~~~~~~~~~~~~~~~~~
4
+
5
+ Main entry point for ``python -m pygments``.
6
+
7
+ :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import sys
12
+ import pygments.cmdline
13
+
14
+ try:
15
+ sys.exit(pygments.cmdline.main(sys.argv))
16
+ except KeyboardInterrupt:
17
+ sys.exit(1)
env/kimodo/lib/python3.10/site-packages/pygments/cmdline.py ADDED
@@ -0,0 +1,668 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.cmdline
3
+ ~~~~~~~~~~~~~~~~
4
+
5
+ Command line interface.
6
+
7
+ :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import os
12
+ import sys
13
+ import shutil
14
+ import argparse
15
+ from textwrap import dedent
16
+
17
+ from pygments import __version__, highlight
18
+ from pygments.util import ClassNotFound, OptionError, docstring_headline, \
19
+ guess_decode, guess_decode_from_terminal, terminal_encoding, \
20
+ UnclosingTextIOWrapper
21
+ from pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer, \
22
+ load_lexer_from_file, get_lexer_for_filename, find_lexer_class_for_filename
23
+ from pygments.lexers.special import TextLexer
24
+ from pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter
25
+ from pygments.formatters import get_all_formatters, get_formatter_by_name, \
26
+ load_formatter_from_file, get_formatter_for_filename, find_formatter_class
27
+ from pygments.formatters.terminal import TerminalFormatter
28
+ from pygments.formatters.terminal256 import Terminal256Formatter, TerminalTrueColorFormatter
29
+ from pygments.filters import get_all_filters, find_filter_class
30
+ from pygments.styles import get_all_styles, get_style_by_name
31
+
32
+
33
+ def _parse_options(o_strs):
34
+ opts = {}
35
+ if not o_strs:
36
+ return opts
37
+ for o_str in o_strs:
38
+ if not o_str.strip():
39
+ continue
40
+ o_args = o_str.split(',')
41
+ for o_arg in o_args:
42
+ o_arg = o_arg.strip()
43
+ try:
44
+ o_key, o_val = o_arg.split('=', 1)
45
+ o_key = o_key.strip()
46
+ o_val = o_val.strip()
47
+ except ValueError:
48
+ opts[o_arg] = True
49
+ else:
50
+ opts[o_key] = o_val
51
+ return opts
52
+
53
+
54
+ def _parse_filters(f_strs):
55
+ filters = []
56
+ if not f_strs:
57
+ return filters
58
+ for f_str in f_strs:
59
+ if ':' in f_str:
60
+ fname, fopts = f_str.split(':', 1)
61
+ filters.append((fname, _parse_options([fopts])))
62
+ else:
63
+ filters.append((f_str, {}))
64
+ return filters
65
+
66
+
67
+ def _print_help(what, name):
68
+ try:
69
+ if what == 'lexer':
70
+ cls = get_lexer_by_name(name)
71
+ print(f"Help on the {cls.name} lexer:")
72
+ print(dedent(cls.__doc__))
73
+ elif what == 'formatter':
74
+ cls = find_formatter_class(name)
75
+ print(f"Help on the {cls.name} formatter:")
76
+ print(dedent(cls.__doc__))
77
+ elif what == 'filter':
78
+ cls = find_filter_class(name)
79
+ print(f"Help on the {name} filter:")
80
+ print(dedent(cls.__doc__))
81
+ return 0
82
+ except (AttributeError, ValueError):
83
+ print(f"{what} not found!", file=sys.stderr)
84
+ return 1
85
+
86
+
87
+ def _print_list(what):
88
+ if what == 'lexer':
89
+ print()
90
+ print("Lexers:")
91
+ print("~~~~~~~")
92
+
93
+ info = []
94
+ for fullname, names, exts, _ in get_all_lexers():
95
+ tup = (', '.join(names)+':', fullname,
96
+ exts and '(filenames ' + ', '.join(exts) + ')' or '')
97
+ info.append(tup)
98
+ info.sort()
99
+ for i in info:
100
+ print(('* {}\n {} {}').format(*i))
101
+
102
+ elif what == 'formatter':
103
+ print()
104
+ print("Formatters:")
105
+ print("~~~~~~~~~~~")
106
+
107
+ info = []
108
+ for cls in get_all_formatters():
109
+ doc = docstring_headline(cls)
110
+ tup = (', '.join(cls.aliases) + ':', doc, cls.filenames and
111
+ '(filenames ' + ', '.join(cls.filenames) + ')' or '')
112
+ info.append(tup)
113
+ info.sort()
114
+ for i in info:
115
+ print(('* {}\n {} {}').format(*i))
116
+
117
+ elif what == 'filter':
118
+ print()
119
+ print("Filters:")
120
+ print("~~~~~~~~")
121
+
122
+ for name in get_all_filters():
123
+ cls = find_filter_class(name)
124
+ print("* " + name + ':')
125
+ print(f" {docstring_headline(cls)}")
126
+
127
+ elif what == 'style':
128
+ print()
129
+ print("Styles:")
130
+ print("~~~~~~~")
131
+
132
+ for name in get_all_styles():
133
+ cls = get_style_by_name(name)
134
+ print("* " + name + ':')
135
+ print(f" {docstring_headline(cls)}")
136
+
137
+
138
+ def _print_list_as_json(requested_items):
139
+ import json
140
+ result = {}
141
+ if 'lexer' in requested_items:
142
+ info = {}
143
+ for fullname, names, filenames, mimetypes in get_all_lexers():
144
+ info[fullname] = {
145
+ 'aliases': names,
146
+ 'filenames': filenames,
147
+ 'mimetypes': mimetypes
148
+ }
149
+ result['lexers'] = info
150
+
151
+ if 'formatter' in requested_items:
152
+ info = {}
153
+ for cls in get_all_formatters():
154
+ doc = docstring_headline(cls)
155
+ info[cls.name] = {
156
+ 'aliases': cls.aliases,
157
+ 'filenames': cls.filenames,
158
+ 'doc': doc
159
+ }
160
+ result['formatters'] = info
161
+
162
+ if 'filter' in requested_items:
163
+ info = {}
164
+ for name in get_all_filters():
165
+ cls = find_filter_class(name)
166
+ info[name] = {
167
+ 'doc': docstring_headline(cls)
168
+ }
169
+ result['filters'] = info
170
+
171
+ if 'style' in requested_items:
172
+ info = {}
173
+ for name in get_all_styles():
174
+ cls = get_style_by_name(name)
175
+ info[name] = {
176
+ 'doc': docstring_headline(cls)
177
+ }
178
+ result['styles'] = info
179
+
180
+ json.dump(result, sys.stdout)
181
+
182
+ def main_inner(parser, argns):
183
+ if argns.help:
184
+ parser.print_help()
185
+ return 0
186
+
187
+ if argns.V:
188
+ print(f'Pygments version {__version__}, (c) 2006-2024 by Georg Brandl, Matthäus '
189
+ 'Chajdas and contributors.')
190
+ return 0
191
+
192
+ def is_only_option(opt):
193
+ return not any(v for (k, v) in vars(argns).items() if k != opt)
194
+
195
+ # handle ``pygmentize -L``
196
+ if argns.L is not None:
197
+ arg_set = set()
198
+ for k, v in vars(argns).items():
199
+ if v:
200
+ arg_set.add(k)
201
+
202
+ arg_set.discard('L')
203
+ arg_set.discard('json')
204
+
205
+ if arg_set:
206
+ parser.print_help(sys.stderr)
207
+ return 2
208
+
209
+ # print version
210
+ if not argns.json:
211
+ main(['', '-V'])
212
+ allowed_types = {'lexer', 'formatter', 'filter', 'style'}
213
+ largs = [arg.rstrip('s') for arg in argns.L]
214
+ if any(arg not in allowed_types for arg in largs):
215
+ parser.print_help(sys.stderr)
216
+ return 0
217
+ if not largs:
218
+ largs = allowed_types
219
+ if not argns.json:
220
+ for arg in largs:
221
+ _print_list(arg)
222
+ else:
223
+ _print_list_as_json(largs)
224
+ return 0
225
+
226
+ # handle ``pygmentize -H``
227
+ if argns.H:
228
+ if not is_only_option('H'):
229
+ parser.print_help(sys.stderr)
230
+ return 2
231
+ what, name = argns.H
232
+ if what not in ('lexer', 'formatter', 'filter'):
233
+ parser.print_help(sys.stderr)
234
+ return 2
235
+ return _print_help(what, name)
236
+
237
+ # parse -O options
238
+ parsed_opts = _parse_options(argns.O or [])
239
+
240
+ # parse -P options
241
+ for p_opt in argns.P or []:
242
+ try:
243
+ name, value = p_opt.split('=', 1)
244
+ except ValueError:
245
+ parsed_opts[p_opt] = True
246
+ else:
247
+ parsed_opts[name] = value
248
+
249
+ # encodings
250
+ inencoding = parsed_opts.get('inencoding', parsed_opts.get('encoding'))
251
+ outencoding = parsed_opts.get('outencoding', parsed_opts.get('encoding'))
252
+
253
+ # handle ``pygmentize -N``
254
+ if argns.N:
255
+ lexer = find_lexer_class_for_filename(argns.N)
256
+ if lexer is None:
257
+ lexer = TextLexer
258
+
259
+ print(lexer.aliases[0])
260
+ return 0
261
+
262
+ # handle ``pygmentize -C``
263
+ if argns.C:
264
+ inp = sys.stdin.buffer.read()
265
+ try:
266
+ lexer = guess_lexer(inp, inencoding=inencoding)
267
+ except ClassNotFound:
268
+ lexer = TextLexer
269
+
270
+ print(lexer.aliases[0])
271
+ return 0
272
+
273
+ # handle ``pygmentize -S``
274
+ S_opt = argns.S
275
+ a_opt = argns.a
276
+ if S_opt is not None:
277
+ f_opt = argns.f
278
+ if not f_opt:
279
+ parser.print_help(sys.stderr)
280
+ return 2
281
+ if argns.l or argns.INPUTFILE:
282
+ parser.print_help(sys.stderr)
283
+ return 2
284
+
285
+ try:
286
+ parsed_opts['style'] = S_opt
287
+ fmter = get_formatter_by_name(f_opt, **parsed_opts)
288
+ except ClassNotFound as err:
289
+ print(err, file=sys.stderr)
290
+ return 1
291
+
292
+ print(fmter.get_style_defs(a_opt or ''))
293
+ return 0
294
+
295
+ # if no -S is given, -a is not allowed
296
+ if argns.a is not None:
297
+ parser.print_help(sys.stderr)
298
+ return 2
299
+
300
+ # parse -F options
301
+ F_opts = _parse_filters(argns.F or [])
302
+
303
+ # -x: allow custom (eXternal) lexers and formatters
304
+ allow_custom_lexer_formatter = bool(argns.x)
305
+
306
+ # select lexer
307
+ lexer = None
308
+
309
+ # given by name?
310
+ lexername = argns.l
311
+ if lexername:
312
+ # custom lexer, located relative to user's cwd
313
+ if allow_custom_lexer_formatter and '.py' in lexername:
314
+ try:
315
+ filename = None
316
+ name = None
317
+ if ':' in lexername:
318
+ filename, name = lexername.rsplit(':', 1)
319
+
320
+ if '.py' in name:
321
+ # This can happen on Windows: If the lexername is
322
+ # C:\lexer.py -- return to normal load path in that case
323
+ name = None
324
+
325
+ if filename and name:
326
+ lexer = load_lexer_from_file(filename, name,
327
+ **parsed_opts)
328
+ else:
329
+ lexer = load_lexer_from_file(lexername, **parsed_opts)
330
+ except ClassNotFound as err:
331
+ print('Error:', err, file=sys.stderr)
332
+ return 1
333
+ else:
334
+ try:
335
+ lexer = get_lexer_by_name(lexername, **parsed_opts)
336
+ except (OptionError, ClassNotFound) as err:
337
+ print('Error:', err, file=sys.stderr)
338
+ return 1
339
+
340
+ # read input code
341
+ code = None
342
+
343
+ if argns.INPUTFILE:
344
+ if argns.s:
345
+ print('Error: -s option not usable when input file specified',
346
+ file=sys.stderr)
347
+ return 2
348
+
349
+ infn = argns.INPUTFILE
350
+ try:
351
+ with open(infn, 'rb') as infp:
352
+ code = infp.read()
353
+ except Exception as err:
354
+ print('Error: cannot read infile:', err, file=sys.stderr)
355
+ return 1
356
+ if not inencoding:
357
+ code, inencoding = guess_decode(code)
358
+
359
+ # do we have to guess the lexer?
360
+ if not lexer:
361
+ try:
362
+ lexer = get_lexer_for_filename(infn, code, **parsed_opts)
363
+ except ClassNotFound as err:
364
+ if argns.g:
365
+ try:
366
+ lexer = guess_lexer(code, **parsed_opts)
367
+ except ClassNotFound:
368
+ lexer = TextLexer(**parsed_opts)
369
+ else:
370
+ print('Error:', err, file=sys.stderr)
371
+ return 1
372
+ except OptionError as err:
373
+ print('Error:', err, file=sys.stderr)
374
+ return 1
375
+
376
+ elif not argns.s: # treat stdin as full file (-s support is later)
377
+ # read code from terminal, always in binary mode since we want to
378
+ # decode ourselves and be tolerant with it
379
+ code = sys.stdin.buffer.read() # use .buffer to get a binary stream
380
+ if not inencoding:
381
+ code, inencoding = guess_decode_from_terminal(code, sys.stdin)
382
+ # else the lexer will do the decoding
383
+ if not lexer:
384
+ try:
385
+ lexer = guess_lexer(code, **parsed_opts)
386
+ except ClassNotFound:
387
+ lexer = TextLexer(**parsed_opts)
388
+
389
+ else: # -s option needs a lexer with -l
390
+ if not lexer:
391
+ print('Error: when using -s a lexer has to be selected with -l',
392
+ file=sys.stderr)
393
+ return 2
394
+
395
+ # process filters
396
+ for fname, fopts in F_opts:
397
+ try:
398
+ lexer.add_filter(fname, **fopts)
399
+ except ClassNotFound as err:
400
+ print('Error:', err, file=sys.stderr)
401
+ return 1
402
+
403
+ # select formatter
404
+ outfn = argns.o
405
+ fmter = argns.f
406
+ if fmter:
407
+ # custom formatter, located relative to user's cwd
408
+ if allow_custom_lexer_formatter and '.py' in fmter:
409
+ try:
410
+ filename = None
411
+ name = None
412
+ if ':' in fmter:
413
+ # Same logic as above for custom lexer
414
+ filename, name = fmter.rsplit(':', 1)
415
+
416
+ if '.py' in name:
417
+ name = None
418
+
419
+ if filename and name:
420
+ fmter = load_formatter_from_file(filename, name,
421
+ **parsed_opts)
422
+ else:
423
+ fmter = load_formatter_from_file(fmter, **parsed_opts)
424
+ except ClassNotFound as err:
425
+ print('Error:', err, file=sys.stderr)
426
+ return 1
427
+ else:
428
+ try:
429
+ fmter = get_formatter_by_name(fmter, **parsed_opts)
430
+ except (OptionError, ClassNotFound) as err:
431
+ print('Error:', err, file=sys.stderr)
432
+ return 1
433
+
434
+ if outfn:
435
+ if not fmter:
436
+ try:
437
+ fmter = get_formatter_for_filename(outfn, **parsed_opts)
438
+ except (OptionError, ClassNotFound) as err:
439
+ print('Error:', err, file=sys.stderr)
440
+ return 1
441
+ try:
442
+ outfile = open(outfn, 'wb')
443
+ except Exception as err:
444
+ print('Error: cannot open outfile:', err, file=sys.stderr)
445
+ return 1
446
+ else:
447
+ if not fmter:
448
+ if os.environ.get('COLORTERM','') in ('truecolor', '24bit'):
449
+ fmter = TerminalTrueColorFormatter(**parsed_opts)
450
+ elif '256' in os.environ.get('TERM', ''):
451
+ fmter = Terminal256Formatter(**parsed_opts)
452
+ else:
453
+ fmter = TerminalFormatter(**parsed_opts)
454
+ outfile = sys.stdout.buffer
455
+
456
+ # determine output encoding if not explicitly selected
457
+ if not outencoding:
458
+ if outfn:
459
+ # output file? use lexer encoding for now (can still be None)
460
+ fmter.encoding = inencoding
461
+ else:
462
+ # else use terminal encoding
463
+ fmter.encoding = terminal_encoding(sys.stdout)
464
+
465
+ # provide coloring under Windows, if possible
466
+ if not outfn and sys.platform in ('win32', 'cygwin') and \
467
+ fmter.name in ('Terminal', 'Terminal256'): # pragma: no cover
468
+ # unfortunately colorama doesn't support binary streams on Py3
469
+ outfile = UnclosingTextIOWrapper(outfile, encoding=fmter.encoding)
470
+ fmter.encoding = None
471
+ try:
472
+ import colorama.initialise
473
+ except ImportError:
474
+ pass
475
+ else:
476
+ outfile = colorama.initialise.wrap_stream(
477
+ outfile, convert=None, strip=None, autoreset=False, wrap=True)
478
+
479
+ # When using the LaTeX formatter and the option `escapeinside` is
480
+ # specified, we need a special lexer which collects escaped text
481
+ # before running the chosen language lexer.
482
+ escapeinside = parsed_opts.get('escapeinside', '')
483
+ if len(escapeinside) == 2 and isinstance(fmter, LatexFormatter):
484
+ left = escapeinside[0]
485
+ right = escapeinside[1]
486
+ lexer = LatexEmbeddedLexer(left, right, lexer)
487
+
488
+ # ... and do it!
489
+ if not argns.s:
490
+ # process whole input as per normal...
491
+ try:
492
+ highlight(code, lexer, fmter, outfile)
493
+ finally:
494
+ if outfn:
495
+ outfile.close()
496
+ return 0
497
+ else:
498
+ # line by line processing of stdin (eg: for 'tail -f')...
499
+ try:
500
+ while 1:
501
+ line = sys.stdin.buffer.readline()
502
+ if not line:
503
+ break
504
+ if not inencoding:
505
+ line = guess_decode_from_terminal(line, sys.stdin)[0]
506
+ highlight(line, lexer, fmter, outfile)
507
+ if hasattr(outfile, 'flush'):
508
+ outfile.flush()
509
+ return 0
510
+ except KeyboardInterrupt: # pragma: no cover
511
+ return 0
512
+ finally:
513
+ if outfn:
514
+ outfile.close()
515
+
516
+
517
+ class HelpFormatter(argparse.HelpFormatter):
518
+ def __init__(self, prog, indent_increment=2, max_help_position=16, width=None):
519
+ if width is None:
520
+ try:
521
+ width = shutil.get_terminal_size().columns - 2
522
+ except Exception:
523
+ pass
524
+ argparse.HelpFormatter.__init__(self, prog, indent_increment,
525
+ max_help_position, width)
526
+
527
+
528
+ def main(args=sys.argv):
529
+ """
530
+ Main command line entry point.
531
+ """
532
+ desc = "Highlight an input file and write the result to an output file."
533
+ parser = argparse.ArgumentParser(description=desc, add_help=False,
534
+ formatter_class=HelpFormatter)
535
+
536
+ operation = parser.add_argument_group('Main operation')
537
+ lexersel = operation.add_mutually_exclusive_group()
538
+ lexersel.add_argument(
539
+ '-l', metavar='LEXER',
540
+ help='Specify the lexer to use. (Query names with -L.) If not '
541
+ 'given and -g is not present, the lexer is guessed from the filename.')
542
+ lexersel.add_argument(
543
+ '-g', action='store_true',
544
+ help='Guess the lexer from the file contents, or pass through '
545
+ 'as plain text if nothing can be guessed.')
546
+ operation.add_argument(
547
+ '-F', metavar='FILTER[:options]', action='append',
548
+ help='Add a filter to the token stream. (Query names with -L.) '
549
+ 'Filter options are given after a colon if necessary.')
550
+ operation.add_argument(
551
+ '-f', metavar='FORMATTER',
552
+ help='Specify the formatter to use. (Query names with -L.) '
553
+ 'If not given, the formatter is guessed from the output filename, '
554
+ 'and defaults to the terminal formatter if the output is to the '
555
+ 'terminal or an unknown file extension.')
556
+ operation.add_argument(
557
+ '-O', metavar='OPTION=value[,OPTION=value,...]', action='append',
558
+ help='Give options to the lexer and formatter as a comma-separated '
559
+ 'list of key-value pairs. '
560
+ 'Example: `-O bg=light,python=cool`.')
561
+ operation.add_argument(
562
+ '-P', metavar='OPTION=value', action='append',
563
+ help='Give a single option to the lexer and formatter - with this '
564
+ 'you can pass options whose value contains commas and equal signs. '
565
+ 'Example: `-P "heading=Pygments, the Python highlighter"`.')
566
+ operation.add_argument(
567
+ '-o', metavar='OUTPUTFILE',
568
+ help='Where to write the output. Defaults to standard output.')
569
+
570
+ operation.add_argument(
571
+ 'INPUTFILE', nargs='?',
572
+ help='Where to read the input. Defaults to standard input.')
573
+
574
+ flags = parser.add_argument_group('Operation flags')
575
+ flags.add_argument(
576
+ '-v', action='store_true',
577
+ help='Print a detailed traceback on unhandled exceptions, which '
578
+ 'is useful for debugging and bug reports.')
579
+ flags.add_argument(
580
+ '-s', action='store_true',
581
+ help='Process lines one at a time until EOF, rather than waiting to '
582
+ 'process the entire file. This only works for stdin, only for lexers '
583
+ 'with no line-spanning constructs, and is intended for streaming '
584
+ 'input such as you get from `tail -f`. '
585
+ 'Example usage: `tail -f sql.log | pygmentize -s -l sql`.')
586
+ flags.add_argument(
587
+ '-x', action='store_true',
588
+ help='Allow custom lexers and formatters to be loaded from a .py file '
589
+ 'relative to the current working directory. For example, '
590
+ '`-l ./customlexer.py -x`. By default, this option expects a file '
591
+ 'with a class named CustomLexer or CustomFormatter; you can also '
592
+ 'specify your own class name with a colon (`-l ./lexer.py:MyLexer`). '
593
+ 'Users should be very careful not to use this option with untrusted '
594
+ 'files, because it will import and run them.')
595
+ flags.add_argument('--json', help='Output as JSON. This can '
596
+ 'be only used in conjunction with -L.',
597
+ default=False,
598
+ action='store_true')
599
+
600
+ special_modes_group = parser.add_argument_group(
601
+ 'Special modes - do not do any highlighting')
602
+ special_modes = special_modes_group.add_mutually_exclusive_group()
603
+ special_modes.add_argument(
604
+ '-S', metavar='STYLE -f formatter',
605
+ help='Print style definitions for STYLE for a formatter '
606
+ 'given with -f. The argument given by -a is formatter '
607
+ 'dependent.')
608
+ special_modes.add_argument(
609
+ '-L', nargs='*', metavar='WHAT',
610
+ help='List lexers, formatters, styles or filters -- '
611
+ 'give additional arguments for the thing(s) you want to list '
612
+ '(e.g. "styles"), or omit them to list everything.')
613
+ special_modes.add_argument(
614
+ '-N', metavar='FILENAME',
615
+ help='Guess and print out a lexer name based solely on the given '
616
+ 'filename. Does not take input or highlight anything. If no specific '
617
+ 'lexer can be determined, "text" is printed.')
618
+ special_modes.add_argument(
619
+ '-C', action='store_true',
620
+ help='Like -N, but print out a lexer name based solely on '
621
+ 'a given content from standard input.')
622
+ special_modes.add_argument(
623
+ '-H', action='store', nargs=2, metavar=('NAME', 'TYPE'),
624
+ help='Print detailed help for the object <name> of type <type>, '
625
+ 'where <type> is one of "lexer", "formatter" or "filter".')
626
+ special_modes.add_argument(
627
+ '-V', action='store_true',
628
+ help='Print the package version.')
629
+ special_modes.add_argument(
630
+ '-h', '--help', action='store_true',
631
+ help='Print this help.')
632
+ special_modes_group.add_argument(
633
+ '-a', metavar='ARG',
634
+ help='Formatter-specific additional argument for the -S (print '
635
+ 'style sheet) mode.')
636
+
637
+ argns = parser.parse_args(args[1:])
638
+
639
+ try:
640
+ return main_inner(parser, argns)
641
+ except BrokenPipeError:
642
+ # someone closed our stdout, e.g. by quitting a pager.
643
+ return 0
644
+ except Exception:
645
+ if argns.v:
646
+ print(file=sys.stderr)
647
+ print('*' * 65, file=sys.stderr)
648
+ print('An unhandled exception occurred while highlighting.',
649
+ file=sys.stderr)
650
+ print('Please report the whole traceback to the issue tracker at',
651
+ file=sys.stderr)
652
+ print('<https://github.com/pygments/pygments/issues>.',
653
+ file=sys.stderr)
654
+ print('*' * 65, file=sys.stderr)
655
+ print(file=sys.stderr)
656
+ raise
657
+ import traceback
658
+ info = traceback.format_exception(*sys.exc_info())
659
+ msg = info[-1].strip()
660
+ if len(info) >= 3:
661
+ # extract relevant file and position info
662
+ msg += '\n (f{})'.format(info[-2].split('\n')[0].strip()[1:])
663
+ print(file=sys.stderr)
664
+ print('*** Error while highlighting:', file=sys.stderr)
665
+ print(msg, file=sys.stderr)
666
+ print('*** If this is a bug you want to report, please rerun with -v.',
667
+ file=sys.stderr)
668
+ return 1
env/kimodo/lib/python3.10/site-packages/pygments/console.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.console
3
+ ~~~~~~~~~~~~~~~~
4
+
5
+ Format colored console output.
6
+
7
+ :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ esc = "\x1b["
12
+
13
+ codes = {}
14
+ codes[""] = ""
15
+ codes["reset"] = esc + "39;49;00m"
16
+
17
+ codes["bold"] = esc + "01m"
18
+ codes["faint"] = esc + "02m"
19
+ codes["standout"] = esc + "03m"
20
+ codes["underline"] = esc + "04m"
21
+ codes["blink"] = esc + "05m"
22
+ codes["overline"] = esc + "06m"
23
+
24
+ dark_colors = ["black", "red", "green", "yellow", "blue",
25
+ "magenta", "cyan", "gray"]
26
+ light_colors = ["brightblack", "brightred", "brightgreen", "brightyellow", "brightblue",
27
+ "brightmagenta", "brightcyan", "white"]
28
+
29
+ x = 30
30
+ for dark, light in zip(dark_colors, light_colors):
31
+ codes[dark] = esc + "%im" % x
32
+ codes[light] = esc + "%im" % (60 + x)
33
+ x += 1
34
+
35
+ del dark, light, x
36
+
37
+ codes["white"] = codes["bold"]
38
+
39
+
40
+ def reset_color():
41
+ return codes["reset"]
42
+
43
+
44
+ def colorize(color_key, text):
45
+ return codes[color_key] + text + codes["reset"]
46
+
47
+
48
+ def ansiformat(attr, text):
49
+ """
50
+ Format ``text`` with a color and/or some attributes::
51
+
52
+ color normal color
53
+ *color* bold color
54
+ _color_ underlined color
55
+ +color+ blinking color
56
+ """
57
+ result = []
58
+ if attr[:1] == attr[-1:] == '+':
59
+ result.append(codes['blink'])
60
+ attr = attr[1:-1]
61
+ if attr[:1] == attr[-1:] == '*':
62
+ result.append(codes['bold'])
63
+ attr = attr[1:-1]
64
+ if attr[:1] == attr[-1:] == '_':
65
+ result.append(codes['underline'])
66
+ attr = attr[1:-1]
67
+ result.append(codes[attr])
68
+ result.append(text)
69
+ result.append(codes['reset'])
70
+ return ''.join(result)
env/kimodo/lib/python3.10/site-packages/pygments/filter.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.filter
3
+ ~~~~~~~~~~~~~~~
4
+
5
+ Module that implements the default filter.
6
+
7
+ :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+
12
+ def apply_filters(stream, filters, lexer=None):
13
+ """
14
+ Use this method to apply an iterable of filters to
15
+ a stream. If lexer is given it's forwarded to the
16
+ filter, otherwise the filter receives `None`.
17
+ """
18
+ def _apply(filter_, stream):
19
+ yield from filter_.filter(lexer, stream)
20
+ for filter_ in filters:
21
+ stream = _apply(filter_, stream)
22
+ return stream
23
+
24
+
25
+ def simplefilter(f):
26
+ """
27
+ Decorator that converts a function into a filter::
28
+
29
+ @simplefilter
30
+ def lowercase(self, lexer, stream, options):
31
+ for ttype, value in stream:
32
+ yield ttype, value.lower()
33
+ """
34
+ return type(f.__name__, (FunctionFilter,), {
35
+ '__module__': getattr(f, '__module__'),
36
+ '__doc__': f.__doc__,
37
+ 'function': f,
38
+ })
39
+
40
+
41
+ class Filter:
42
+ """
43
+ Default filter. Subclass this class or use the `simplefilter`
44
+ decorator to create own filters.
45
+ """
46
+
47
+ def __init__(self, **options):
48
+ self.options = options
49
+
50
+ def filter(self, lexer, stream):
51
+ raise NotImplementedError()
52
+
53
+
54
+ class FunctionFilter(Filter):
55
+ """
56
+ Abstract class used by `simplefilter` to create simple
57
+ function filters on the fly. The `simplefilter` decorator
58
+ automatically creates subclasses of this class for
59
+ functions passed to it.
60
+ """
61
+ function = None
62
+
63
+ def __init__(self, **options):
64
+ if not hasattr(self, 'function'):
65
+ raise TypeError(f'{self.__class__.__name__!r} used without bound function')
66
+ Filter.__init__(self, **options)
67
+
68
+ def filter(self, lexer, stream):
69
+ # pylint: disable=not-callable
70
+ yield from self.function(lexer, stream, self.options)
env/kimodo/lib/python3.10/site-packages/pygments/filters/__init__.py ADDED
@@ -0,0 +1,940 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.filters
3
+ ~~~~~~~~~~~~~~~~
4
+
5
+ Module containing filter lookup functions and default
6
+ filters.
7
+
8
+ :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
9
+ :license: BSD, see LICENSE for details.
10
+ """
11
+
12
+ import re
13
+
14
+ from pygments.token import String, Comment, Keyword, Name, Error, Whitespace, \
15
+ string_to_tokentype
16
+ from pygments.filter import Filter
17
+ from pygments.util import get_list_opt, get_int_opt, get_bool_opt, \
18
+ get_choice_opt, ClassNotFound, OptionError
19
+ from pygments.plugin import find_plugin_filters
20
+
21
+
22
+ def find_filter_class(filtername):
23
+ """Lookup a filter by name. Return None if not found."""
24
+ if filtername in FILTERS:
25
+ return FILTERS[filtername]
26
+ for name, cls in find_plugin_filters():
27
+ if name == filtername:
28
+ return cls
29
+ return None
30
+
31
+
32
+ def get_filter_by_name(filtername, **options):
33
+ """Return an instantiated filter.
34
+
35
+ Options are passed to the filter initializer if wanted.
36
+ Raise a ClassNotFound if not found.
37
+ """
38
+ cls = find_filter_class(filtername)
39
+ if cls:
40
+ return cls(**options)
41
+ else:
42
+ raise ClassNotFound(f'filter {filtername!r} not found')
43
+
44
+
45
+ def get_all_filters():
46
+ """Return a generator of all filter names."""
47
+ yield from FILTERS
48
+ for name, _ in find_plugin_filters():
49
+ yield name
50
+
51
+
52
+ def _replace_special(ttype, value, regex, specialttype,
53
+ replacefunc=lambda x: x):
54
+ last = 0
55
+ for match in regex.finditer(value):
56
+ start, end = match.start(), match.end()
57
+ if start != last:
58
+ yield ttype, value[last:start]
59
+ yield specialttype, replacefunc(value[start:end])
60
+ last = end
61
+ if last != len(value):
62
+ yield ttype, value[last:]
63
+
64
+
65
+ class CodeTagFilter(Filter):
66
+ """Highlight special code tags in comments and docstrings.
67
+
68
+ Options accepted:
69
+
70
+ `codetags` : list of strings
71
+ A list of strings that are flagged as code tags. The default is to
72
+ highlight ``XXX``, ``TODO``, ``FIXME``, ``BUG`` and ``NOTE``.
73
+
74
+ .. versionchanged:: 2.13
75
+ Now recognizes ``FIXME`` by default.
76
+ """
77
+
78
+ def __init__(self, **options):
79
+ Filter.__init__(self, **options)
80
+ tags = get_list_opt(options, 'codetags',
81
+ ['XXX', 'TODO', 'FIXME', 'BUG', 'NOTE'])
82
+ self.tag_re = re.compile(r'\b({})\b'.format('|'.join([
83
+ re.escape(tag) for tag in tags if tag
84
+ ])))
85
+
86
+ def filter(self, lexer, stream):
87
+ regex = self.tag_re
88
+ for ttype, value in stream:
89
+ if ttype in String.Doc or \
90
+ ttype in Comment and \
91
+ ttype not in Comment.Preproc:
92
+ yield from _replace_special(ttype, value, regex, Comment.Special)
93
+ else:
94
+ yield ttype, value
95
+
96
+
97
+ class SymbolFilter(Filter):
98
+ """Convert mathematical symbols such as \\<longrightarrow> in Isabelle
99
+ or \\longrightarrow in LaTeX into Unicode characters.
100
+
101
+ This is mostly useful for HTML or console output when you want to
102
+ approximate the source rendering you'd see in an IDE.
103
+
104
+ Options accepted:
105
+
106
+ `lang` : string
107
+ The symbol language. Must be one of ``'isabelle'`` or
108
+ ``'latex'``. The default is ``'isabelle'``.
109
+ """
110
+
111
+ latex_symbols = {
112
+ '\\alpha' : '\U000003b1',
113
+ '\\beta' : '\U000003b2',
114
+ '\\gamma' : '\U000003b3',
115
+ '\\delta' : '\U000003b4',
116
+ '\\varepsilon' : '\U000003b5',
117
+ '\\zeta' : '\U000003b6',
118
+ '\\eta' : '\U000003b7',
119
+ '\\vartheta' : '\U000003b8',
120
+ '\\iota' : '\U000003b9',
121
+ '\\kappa' : '\U000003ba',
122
+ '\\lambda' : '\U000003bb',
123
+ '\\mu' : '\U000003bc',
124
+ '\\nu' : '\U000003bd',
125
+ '\\xi' : '\U000003be',
126
+ '\\pi' : '\U000003c0',
127
+ '\\varrho' : '\U000003c1',
128
+ '\\sigma' : '\U000003c3',
129
+ '\\tau' : '\U000003c4',
130
+ '\\upsilon' : '\U000003c5',
131
+ '\\varphi' : '\U000003c6',
132
+ '\\chi' : '\U000003c7',
133
+ '\\psi' : '\U000003c8',
134
+ '\\omega' : '\U000003c9',
135
+ '\\Gamma' : '\U00000393',
136
+ '\\Delta' : '\U00000394',
137
+ '\\Theta' : '\U00000398',
138
+ '\\Lambda' : '\U0000039b',
139
+ '\\Xi' : '\U0000039e',
140
+ '\\Pi' : '\U000003a0',
141
+ '\\Sigma' : '\U000003a3',
142
+ '\\Upsilon' : '\U000003a5',
143
+ '\\Phi' : '\U000003a6',
144
+ '\\Psi' : '\U000003a8',
145
+ '\\Omega' : '\U000003a9',
146
+ '\\leftarrow' : '\U00002190',
147
+ '\\longleftarrow' : '\U000027f5',
148
+ '\\rightarrow' : '\U00002192',
149
+ '\\longrightarrow' : '\U000027f6',
150
+ '\\Leftarrow' : '\U000021d0',
151
+ '\\Longleftarrow' : '\U000027f8',
152
+ '\\Rightarrow' : '\U000021d2',
153
+ '\\Longrightarrow' : '\U000027f9',
154
+ '\\leftrightarrow' : '\U00002194',
155
+ '\\longleftrightarrow' : '\U000027f7',
156
+ '\\Leftrightarrow' : '\U000021d4',
157
+ '\\Longleftrightarrow' : '\U000027fa',
158
+ '\\mapsto' : '\U000021a6',
159
+ '\\longmapsto' : '\U000027fc',
160
+ '\\relbar' : '\U00002500',
161
+ '\\Relbar' : '\U00002550',
162
+ '\\hookleftarrow' : '\U000021a9',
163
+ '\\hookrightarrow' : '\U000021aa',
164
+ '\\leftharpoondown' : '\U000021bd',
165
+ '\\rightharpoondown' : '\U000021c1',
166
+ '\\leftharpoonup' : '\U000021bc',
167
+ '\\rightharpoonup' : '\U000021c0',
168
+ '\\rightleftharpoons' : '\U000021cc',
169
+ '\\leadsto' : '\U0000219d',
170
+ '\\downharpoonleft' : '\U000021c3',
171
+ '\\downharpoonright' : '\U000021c2',
172
+ '\\upharpoonleft' : '\U000021bf',
173
+ '\\upharpoonright' : '\U000021be',
174
+ '\\restriction' : '\U000021be',
175
+ '\\uparrow' : '\U00002191',
176
+ '\\Uparrow' : '\U000021d1',
177
+ '\\downarrow' : '\U00002193',
178
+ '\\Downarrow' : '\U000021d3',
179
+ '\\updownarrow' : '\U00002195',
180
+ '\\Updownarrow' : '\U000021d5',
181
+ '\\langle' : '\U000027e8',
182
+ '\\rangle' : '\U000027e9',
183
+ '\\lceil' : '\U00002308',
184
+ '\\rceil' : '\U00002309',
185
+ '\\lfloor' : '\U0000230a',
186
+ '\\rfloor' : '\U0000230b',
187
+ '\\flqq' : '\U000000ab',
188
+ '\\frqq' : '\U000000bb',
189
+ '\\bot' : '\U000022a5',
190
+ '\\top' : '\U000022a4',
191
+ '\\wedge' : '\U00002227',
192
+ '\\bigwedge' : '\U000022c0',
193
+ '\\vee' : '\U00002228',
194
+ '\\bigvee' : '\U000022c1',
195
+ '\\forall' : '\U00002200',
196
+ '\\exists' : '\U00002203',
197
+ '\\nexists' : '\U00002204',
198
+ '\\neg' : '\U000000ac',
199
+ '\\Box' : '\U000025a1',
200
+ '\\Diamond' : '\U000025c7',
201
+ '\\vdash' : '\U000022a2',
202
+ '\\models' : '\U000022a8',
203
+ '\\dashv' : '\U000022a3',
204
+ '\\surd' : '\U0000221a',
205
+ '\\le' : '\U00002264',
206
+ '\\ge' : '\U00002265',
207
+ '\\ll' : '\U0000226a',
208
+ '\\gg' : '\U0000226b',
209
+ '\\lesssim' : '\U00002272',
210
+ '\\gtrsim' : '\U00002273',
211
+ '\\lessapprox' : '\U00002a85',
212
+ '\\gtrapprox' : '\U00002a86',
213
+ '\\in' : '\U00002208',
214
+ '\\notin' : '\U00002209',
215
+ '\\subset' : '\U00002282',
216
+ '\\supset' : '\U00002283',
217
+ '\\subseteq' : '\U00002286',
218
+ '\\supseteq' : '\U00002287',
219
+ '\\sqsubset' : '\U0000228f',
220
+ '\\sqsupset' : '\U00002290',
221
+ '\\sqsubseteq' : '\U00002291',
222
+ '\\sqsupseteq' : '\U00002292',
223
+ '\\cap' : '\U00002229',
224
+ '\\bigcap' : '\U000022c2',
225
+ '\\cup' : '\U0000222a',
226
+ '\\bigcup' : '\U000022c3',
227
+ '\\sqcup' : '\U00002294',
228
+ '\\bigsqcup' : '\U00002a06',
229
+ '\\sqcap' : '\U00002293',
230
+ '\\Bigsqcap' : '\U00002a05',
231
+ '\\setminus' : '\U00002216',
232
+ '\\propto' : '\U0000221d',
233
+ '\\uplus' : '\U0000228e',
234
+ '\\bigplus' : '\U00002a04',
235
+ '\\sim' : '\U0000223c',
236
+ '\\doteq' : '\U00002250',
237
+ '\\simeq' : '\U00002243',
238
+ '\\approx' : '\U00002248',
239
+ '\\asymp' : '\U0000224d',
240
+ '\\cong' : '\U00002245',
241
+ '\\equiv' : '\U00002261',
242
+ '\\Join' : '\U000022c8',
243
+ '\\bowtie' : '\U00002a1d',
244
+ '\\prec' : '\U0000227a',
245
+ '\\succ' : '\U0000227b',
246
+ '\\preceq' : '\U0000227c',
247
+ '\\succeq' : '\U0000227d',
248
+ '\\parallel' : '\U00002225',
249
+ '\\mid' : '\U000000a6',
250
+ '\\pm' : '\U000000b1',
251
+ '\\mp' : '\U00002213',
252
+ '\\times' : '\U000000d7',
253
+ '\\div' : '\U000000f7',
254
+ '\\cdot' : '\U000022c5',
255
+ '\\star' : '\U000022c6',
256
+ '\\circ' : '\U00002218',
257
+ '\\dagger' : '\U00002020',
258
+ '\\ddagger' : '\U00002021',
259
+ '\\lhd' : '\U000022b2',
260
+ '\\rhd' : '\U000022b3',
261
+ '\\unlhd' : '\U000022b4',
262
+ '\\unrhd' : '\U000022b5',
263
+ '\\triangleleft' : '\U000025c3',
264
+ '\\triangleright' : '\U000025b9',
265
+ '\\triangle' : '\U000025b3',
266
+ '\\triangleq' : '\U0000225c',
267
+ '\\oplus' : '\U00002295',
268
+ '\\bigoplus' : '\U00002a01',
269
+ '\\otimes' : '\U00002297',
270
+ '\\bigotimes' : '\U00002a02',
271
+ '\\odot' : '\U00002299',
272
+ '\\bigodot' : '\U00002a00',
273
+ '\\ominus' : '\U00002296',
274
+ '\\oslash' : '\U00002298',
275
+ '\\dots' : '\U00002026',
276
+ '\\cdots' : '\U000022ef',
277
+ '\\sum' : '\U00002211',
278
+ '\\prod' : '\U0000220f',
279
+ '\\coprod' : '\U00002210',
280
+ '\\infty' : '\U0000221e',
281
+ '\\int' : '\U0000222b',
282
+ '\\oint' : '\U0000222e',
283
+ '\\clubsuit' : '\U00002663',
284
+ '\\diamondsuit' : '\U00002662',
285
+ '\\heartsuit' : '\U00002661',
286
+ '\\spadesuit' : '\U00002660',
287
+ '\\aleph' : '\U00002135',
288
+ '\\emptyset' : '\U00002205',
289
+ '\\nabla' : '\U00002207',
290
+ '\\partial' : '\U00002202',
291
+ '\\flat' : '\U0000266d',
292
+ '\\natural' : '\U0000266e',
293
+ '\\sharp' : '\U0000266f',
294
+ '\\angle' : '\U00002220',
295
+ '\\copyright' : '\U000000a9',
296
+ '\\textregistered' : '\U000000ae',
297
+ '\\textonequarter' : '\U000000bc',
298
+ '\\textonehalf' : '\U000000bd',
299
+ '\\textthreequarters' : '\U000000be',
300
+ '\\textordfeminine' : '\U000000aa',
301
+ '\\textordmasculine' : '\U000000ba',
302
+ '\\euro' : '\U000020ac',
303
+ '\\pounds' : '\U000000a3',
304
+ '\\yen' : '\U000000a5',
305
+ '\\textcent' : '\U000000a2',
306
+ '\\textcurrency' : '\U000000a4',
307
+ '\\textdegree' : '\U000000b0',
308
+ }
309
+
310
+ isabelle_symbols = {
311
+ '\\<zero>' : '\U0001d7ec',
312
+ '\\<one>' : '\U0001d7ed',
313
+ '\\<two>' : '\U0001d7ee',
314
+ '\\<three>' : '\U0001d7ef',
315
+ '\\<four>' : '\U0001d7f0',
316
+ '\\<five>' : '\U0001d7f1',
317
+ '\\<six>' : '\U0001d7f2',
318
+ '\\<seven>' : '\U0001d7f3',
319
+ '\\<eight>' : '\U0001d7f4',
320
+ '\\<nine>' : '\U0001d7f5',
321
+ '\\<A>' : '\U0001d49c',
322
+ '\\<B>' : '\U0000212c',
323
+ '\\<C>' : '\U0001d49e',
324
+ '\\<D>' : '\U0001d49f',
325
+ '\\<E>' : '\U00002130',
326
+ '\\<F>' : '\U00002131',
327
+ '\\<G>' : '\U0001d4a2',
328
+ '\\<H>' : '\U0000210b',
329
+ '\\<I>' : '\U00002110',
330
+ '\\<J>' : '\U0001d4a5',
331
+ '\\<K>' : '\U0001d4a6',
332
+ '\\<L>' : '\U00002112',
333
+ '\\<M>' : '\U00002133',
334
+ '\\<N>' : '\U0001d4a9',
335
+ '\\<O>' : '\U0001d4aa',
336
+ '\\<P>' : '\U0001d4ab',
337
+ '\\<Q>' : '\U0001d4ac',
338
+ '\\<R>' : '\U0000211b',
339
+ '\\<S>' : '\U0001d4ae',
340
+ '\\<T>' : '\U0001d4af',
341
+ '\\<U>' : '\U0001d4b0',
342
+ '\\<V>' : '\U0001d4b1',
343
+ '\\<W>' : '\U0001d4b2',
344
+ '\\<X>' : '\U0001d4b3',
345
+ '\\<Y>' : '\U0001d4b4',
346
+ '\\<Z>' : '\U0001d4b5',
347
+ '\\<a>' : '\U0001d5ba',
348
+ '\\<b>' : '\U0001d5bb',
349
+ '\\<c>' : '\U0001d5bc',
350
+ '\\<d>' : '\U0001d5bd',
351
+ '\\<e>' : '\U0001d5be',
352
+ '\\<f>' : '\U0001d5bf',
353
+ '\\<g>' : '\U0001d5c0',
354
+ '\\<h>' : '\U0001d5c1',
355
+ '\\<i>' : '\U0001d5c2',
356
+ '\\<j>' : '\U0001d5c3',
357
+ '\\<k>' : '\U0001d5c4',
358
+ '\\<l>' : '\U0001d5c5',
359
+ '\\<m>' : '\U0001d5c6',
360
+ '\\<n>' : '\U0001d5c7',
361
+ '\\<o>' : '\U0001d5c8',
362
+ '\\<p>' : '\U0001d5c9',
363
+ '\\<q>' : '\U0001d5ca',
364
+ '\\<r>' : '\U0001d5cb',
365
+ '\\<s>' : '\U0001d5cc',
366
+ '\\<t>' : '\U0001d5cd',
367
+ '\\<u>' : '\U0001d5ce',
368
+ '\\<v>' : '\U0001d5cf',
369
+ '\\<w>' : '\U0001d5d0',
370
+ '\\<x>' : '\U0001d5d1',
371
+ '\\<y>' : '\U0001d5d2',
372
+ '\\<z>' : '\U0001d5d3',
373
+ '\\<AA>' : '\U0001d504',
374
+ '\\<BB>' : '\U0001d505',
375
+ '\\<CC>' : '\U0000212d',
376
+ '\\<DD>' : '\U0001d507',
377
+ '\\<EE>' : '\U0001d508',
378
+ '\\<FF>' : '\U0001d509',
379
+ '\\<GG>' : '\U0001d50a',
380
+ '\\<HH>' : '\U0000210c',
381
+ '\\<II>' : '\U00002111',
382
+ '\\<JJ>' : '\U0001d50d',
383
+ '\\<KK>' : '\U0001d50e',
384
+ '\\<LL>' : '\U0001d50f',
385
+ '\\<MM>' : '\U0001d510',
386
+ '\\<NN>' : '\U0001d511',
387
+ '\\<OO>' : '\U0001d512',
388
+ '\\<PP>' : '\U0001d513',
389
+ '\\<QQ>' : '\U0001d514',
390
+ '\\<RR>' : '\U0000211c',
391
+ '\\<SS>' : '\U0001d516',
392
+ '\\<TT>' : '\U0001d517',
393
+ '\\<UU>' : '\U0001d518',
394
+ '\\<VV>' : '\U0001d519',
395
+ '\\<WW>' : '\U0001d51a',
396
+ '\\<XX>' : '\U0001d51b',
397
+ '\\<YY>' : '\U0001d51c',
398
+ '\\<ZZ>' : '\U00002128',
399
+ '\\<aa>' : '\U0001d51e',
400
+ '\\<bb>' : '\U0001d51f',
401
+ '\\<cc>' : '\U0001d520',
402
+ '\\<dd>' : '\U0001d521',
403
+ '\\<ee>' : '\U0001d522',
404
+ '\\<ff>' : '\U0001d523',
405
+ '\\<gg>' : '\U0001d524',
406
+ '\\<hh>' : '\U0001d525',
407
+ '\\<ii>' : '\U0001d526',
408
+ '\\<jj>' : '\U0001d527',
409
+ '\\<kk>' : '\U0001d528',
410
+ '\\<ll>' : '\U0001d529',
411
+ '\\<mm>' : '\U0001d52a',
412
+ '\\<nn>' : '\U0001d52b',
413
+ '\\<oo>' : '\U0001d52c',
414
+ '\\<pp>' : '\U0001d52d',
415
+ '\\<qq>' : '\U0001d52e',
416
+ '\\<rr>' : '\U0001d52f',
417
+ '\\<ss>' : '\U0001d530',
418
+ '\\<tt>' : '\U0001d531',
419
+ '\\<uu>' : '\U0001d532',
420
+ '\\<vv>' : '\U0001d533',
421
+ '\\<ww>' : '\U0001d534',
422
+ '\\<xx>' : '\U0001d535',
423
+ '\\<yy>' : '\U0001d536',
424
+ '\\<zz>' : '\U0001d537',
425
+ '\\<alpha>' : '\U000003b1',
426
+ '\\<beta>' : '\U000003b2',
427
+ '\\<gamma>' : '\U000003b3',
428
+ '\\<delta>' : '\U000003b4',
429
+ '\\<epsilon>' : '\U000003b5',
430
+ '\\<zeta>' : '\U000003b6',
431
+ '\\<eta>' : '\U000003b7',
432
+ '\\<theta>' : '\U000003b8',
433
+ '\\<iota>' : '\U000003b9',
434
+ '\\<kappa>' : '\U000003ba',
435
+ '\\<lambda>' : '\U000003bb',
436
+ '\\<mu>' : '\U000003bc',
437
+ '\\<nu>' : '\U000003bd',
438
+ '\\<xi>' : '\U000003be',
439
+ '\\<pi>' : '\U000003c0',
440
+ '\\<rho>' : '\U000003c1',
441
+ '\\<sigma>' : '\U000003c3',
442
+ '\\<tau>' : '\U000003c4',
443
+ '\\<upsilon>' : '\U000003c5',
444
+ '\\<phi>' : '\U000003c6',
445
+ '\\<chi>' : '\U000003c7',
446
+ '\\<psi>' : '\U000003c8',
447
+ '\\<omega>' : '\U000003c9',
448
+ '\\<Gamma>' : '\U00000393',
449
+ '\\<Delta>' : '\U00000394',
450
+ '\\<Theta>' : '\U00000398',
451
+ '\\<Lambda>' : '\U0000039b',
452
+ '\\<Xi>' : '\U0000039e',
453
+ '\\<Pi>' : '\U000003a0',
454
+ '\\<Sigma>' : '\U000003a3',
455
+ '\\<Upsilon>' : '\U000003a5',
456
+ '\\<Phi>' : '\U000003a6',
457
+ '\\<Psi>' : '\U000003a8',
458
+ '\\<Omega>' : '\U000003a9',
459
+ '\\<bool>' : '\U0001d539',
460
+ '\\<complex>' : '\U00002102',
461
+ '\\<nat>' : '\U00002115',
462
+ '\\<rat>' : '\U0000211a',
463
+ '\\<real>' : '\U0000211d',
464
+ '\\<int>' : '\U00002124',
465
+ '\\<leftarrow>' : '\U00002190',
466
+ '\\<longleftarrow>' : '\U000027f5',
467
+ '\\<rightarrow>' : '\U00002192',
468
+ '\\<longrightarrow>' : '\U000027f6',
469
+ '\\<Leftarrow>' : '\U000021d0',
470
+ '\\<Longleftarrow>' : '\U000027f8',
471
+ '\\<Rightarrow>' : '\U000021d2',
472
+ '\\<Longrightarrow>' : '\U000027f9',
473
+ '\\<leftrightarrow>' : '\U00002194',
474
+ '\\<longleftrightarrow>' : '\U000027f7',
475
+ '\\<Leftrightarrow>' : '\U000021d4',
476
+ '\\<Longleftrightarrow>' : '\U000027fa',
477
+ '\\<mapsto>' : '\U000021a6',
478
+ '\\<longmapsto>' : '\U000027fc',
479
+ '\\<midarrow>' : '\U00002500',
480
+ '\\<Midarrow>' : '\U00002550',
481
+ '\\<hookleftarrow>' : '\U000021a9',
482
+ '\\<hookrightarrow>' : '\U000021aa',
483
+ '\\<leftharpoondown>' : '\U000021bd',
484
+ '\\<rightharpoondown>' : '\U000021c1',
485
+ '\\<leftharpoonup>' : '\U000021bc',
486
+ '\\<rightharpoonup>' : '\U000021c0',
487
+ '\\<rightleftharpoons>' : '\U000021cc',
488
+ '\\<leadsto>' : '\U0000219d',
489
+ '\\<downharpoonleft>' : '\U000021c3',
490
+ '\\<downharpoonright>' : '\U000021c2',
491
+ '\\<upharpoonleft>' : '\U000021bf',
492
+ '\\<upharpoonright>' : '\U000021be',
493
+ '\\<restriction>' : '\U000021be',
494
+ '\\<Colon>' : '\U00002237',
495
+ '\\<up>' : '\U00002191',
496
+ '\\<Up>' : '\U000021d1',
497
+ '\\<down>' : '\U00002193',
498
+ '\\<Down>' : '\U000021d3',
499
+ '\\<updown>' : '\U00002195',
500
+ '\\<Updown>' : '\U000021d5',
501
+ '\\<langle>' : '\U000027e8',
502
+ '\\<rangle>' : '\U000027e9',
503
+ '\\<lceil>' : '\U00002308',
504
+ '\\<rceil>' : '\U00002309',
505
+ '\\<lfloor>' : '\U0000230a',
506
+ '\\<rfloor>' : '\U0000230b',
507
+ '\\<lparr>' : '\U00002987',
508
+ '\\<rparr>' : '\U00002988',
509
+ '\\<lbrakk>' : '\U000027e6',
510
+ '\\<rbrakk>' : '\U000027e7',
511
+ '\\<lbrace>' : '\U00002983',
512
+ '\\<rbrace>' : '\U00002984',
513
+ '\\<guillemotleft>' : '\U000000ab',
514
+ '\\<guillemotright>' : '\U000000bb',
515
+ '\\<bottom>' : '\U000022a5',
516
+ '\\<top>' : '\U000022a4',
517
+ '\\<and>' : '\U00002227',
518
+ '\\<And>' : '\U000022c0',
519
+ '\\<or>' : '\U00002228',
520
+ '\\<Or>' : '\U000022c1',
521
+ '\\<forall>' : '\U00002200',
522
+ '\\<exists>' : '\U00002203',
523
+ '\\<nexists>' : '\U00002204',
524
+ '\\<not>' : '\U000000ac',
525
+ '\\<box>' : '\U000025a1',
526
+ '\\<diamond>' : '\U000025c7',
527
+ '\\<turnstile>' : '\U000022a2',
528
+ '\\<Turnstile>' : '\U000022a8',
529
+ '\\<tturnstile>' : '\U000022a9',
530
+ '\\<TTurnstile>' : '\U000022ab',
531
+ '\\<stileturn>' : '\U000022a3',
532
+ '\\<surd>' : '\U0000221a',
533
+ '\\<le>' : '\U00002264',
534
+ '\\<ge>' : '\U00002265',
535
+ '\\<lless>' : '\U0000226a',
536
+ '\\<ggreater>' : '\U0000226b',
537
+ '\\<lesssim>' : '\U00002272',
538
+ '\\<greatersim>' : '\U00002273',
539
+ '\\<lessapprox>' : '\U00002a85',
540
+ '\\<greaterapprox>' : '\U00002a86',
541
+ '\\<in>' : '\U00002208',
542
+ '\\<notin>' : '\U00002209',
543
+ '\\<subset>' : '\U00002282',
544
+ '\\<supset>' : '\U00002283',
545
+ '\\<subseteq>' : '\U00002286',
546
+ '\\<supseteq>' : '\U00002287',
547
+ '\\<sqsubset>' : '\U0000228f',
548
+ '\\<sqsupset>' : '\U00002290',
549
+ '\\<sqsubseteq>' : '\U00002291',
550
+ '\\<sqsupseteq>' : '\U00002292',
551
+ '\\<inter>' : '\U00002229',
552
+ '\\<Inter>' : '\U000022c2',
553
+ '\\<union>' : '\U0000222a',
554
+ '\\<Union>' : '\U000022c3',
555
+ '\\<squnion>' : '\U00002294',
556
+ '\\<Squnion>' : '\U00002a06',
557
+ '\\<sqinter>' : '\U00002293',
558
+ '\\<Sqinter>' : '\U00002a05',
559
+ '\\<setminus>' : '\U00002216',
560
+ '\\<propto>' : '\U0000221d',
561
+ '\\<uplus>' : '\U0000228e',
562
+ '\\<Uplus>' : '\U00002a04',
563
+ '\\<noteq>' : '\U00002260',
564
+ '\\<sim>' : '\U0000223c',
565
+ '\\<doteq>' : '\U00002250',
566
+ '\\<simeq>' : '\U00002243',
567
+ '\\<approx>' : '\U00002248',
568
+ '\\<asymp>' : '\U0000224d',
569
+ '\\<cong>' : '\U00002245',
570
+ '\\<smile>' : '\U00002323',
571
+ '\\<equiv>' : '\U00002261',
572
+ '\\<frown>' : '\U00002322',
573
+ '\\<Join>' : '\U000022c8',
574
+ '\\<bowtie>' : '\U00002a1d',
575
+ '\\<prec>' : '\U0000227a',
576
+ '\\<succ>' : '\U0000227b',
577
+ '\\<preceq>' : '\U0000227c',
578
+ '\\<succeq>' : '\U0000227d',
579
+ '\\<parallel>' : '\U00002225',
580
+ '\\<bar>' : '\U000000a6',
581
+ '\\<plusminus>' : '\U000000b1',
582
+ '\\<minusplus>' : '\U00002213',
583
+ '\\<times>' : '\U000000d7',
584
+ '\\<div>' : '\U000000f7',
585
+ '\\<cdot>' : '\U000022c5',
586
+ '\\<star>' : '\U000022c6',
587
+ '\\<bullet>' : '\U00002219',
588
+ '\\<circ>' : '\U00002218',
589
+ '\\<dagger>' : '\U00002020',
590
+ '\\<ddagger>' : '\U00002021',
591
+ '\\<lhd>' : '\U000022b2',
592
+ '\\<rhd>' : '\U000022b3',
593
+ '\\<unlhd>' : '\U000022b4',
594
+ '\\<unrhd>' : '\U000022b5',
595
+ '\\<triangleleft>' : '\U000025c3',
596
+ '\\<triangleright>' : '\U000025b9',
597
+ '\\<triangle>' : '\U000025b3',
598
+ '\\<triangleq>' : '\U0000225c',
599
+ '\\<oplus>' : '\U00002295',
600
+ '\\<Oplus>' : '\U00002a01',
601
+ '\\<otimes>' : '\U00002297',
602
+ '\\<Otimes>' : '\U00002a02',
603
+ '\\<odot>' : '\U00002299',
604
+ '\\<Odot>' : '\U00002a00',
605
+ '\\<ominus>' : '\U00002296',
606
+ '\\<oslash>' : '\U00002298',
607
+ '\\<dots>' : '\U00002026',
608
+ '\\<cdots>' : '\U000022ef',
609
+ '\\<Sum>' : '\U00002211',
610
+ '\\<Prod>' : '\U0000220f',
611
+ '\\<Coprod>' : '\U00002210',
612
+ '\\<infinity>' : '\U0000221e',
613
+ '\\<integral>' : '\U0000222b',
614
+ '\\<ointegral>' : '\U0000222e',
615
+ '\\<clubsuit>' : '\U00002663',
616
+ '\\<diamondsuit>' : '\U00002662',
617
+ '\\<heartsuit>' : '\U00002661',
618
+ '\\<spadesuit>' : '\U00002660',
619
+ '\\<aleph>' : '\U00002135',
620
+ '\\<emptyset>' : '\U00002205',
621
+ '\\<nabla>' : '\U00002207',
622
+ '\\<partial>' : '\U00002202',
623
+ '\\<flat>' : '\U0000266d',
624
+ '\\<natural>' : '\U0000266e',
625
+ '\\<sharp>' : '\U0000266f',
626
+ '\\<angle>' : '\U00002220',
627
+ '\\<copyright>' : '\U000000a9',
628
+ '\\<registered>' : '\U000000ae',
629
+ '\\<hyphen>' : '\U000000ad',
630
+ '\\<inverse>' : '\U000000af',
631
+ '\\<onequarter>' : '\U000000bc',
632
+ '\\<onehalf>' : '\U000000bd',
633
+ '\\<threequarters>' : '\U000000be',
634
+ '\\<ordfeminine>' : '\U000000aa',
635
+ '\\<ordmasculine>' : '\U000000ba',
636
+ '\\<section>' : '\U000000a7',
637
+ '\\<paragraph>' : '\U000000b6',
638
+ '\\<exclamdown>' : '\U000000a1',
639
+ '\\<questiondown>' : '\U000000bf',
640
+ '\\<euro>' : '\U000020ac',
641
+ '\\<pounds>' : '\U000000a3',
642
+ '\\<yen>' : '\U000000a5',
643
+ '\\<cent>' : '\U000000a2',
644
+ '\\<currency>' : '\U000000a4',
645
+ '\\<degree>' : '\U000000b0',
646
+ '\\<amalg>' : '\U00002a3f',
647
+ '\\<mho>' : '\U00002127',
648
+ '\\<lozenge>' : '\U000025ca',
649
+ '\\<wp>' : '\U00002118',
650
+ '\\<wrong>' : '\U00002240',
651
+ '\\<struct>' : '\U000022c4',
652
+ '\\<acute>' : '\U000000b4',
653
+ '\\<index>' : '\U00000131',
654
+ '\\<dieresis>' : '\U000000a8',
655
+ '\\<cedilla>' : '\U000000b8',
656
+ '\\<hungarumlaut>' : '\U000002dd',
657
+ '\\<some>' : '\U000003f5',
658
+ '\\<newline>' : '\U000023ce',
659
+ '\\<open>' : '\U00002039',
660
+ '\\<close>' : '\U0000203a',
661
+ '\\<here>' : '\U00002302',
662
+ '\\<^sub>' : '\U000021e9',
663
+ '\\<^sup>' : '\U000021e7',
664
+ '\\<^bold>' : '\U00002759',
665
+ '\\<^bsub>' : '\U000021d8',
666
+ '\\<^esub>' : '\U000021d9',
667
+ '\\<^bsup>' : '\U000021d7',
668
+ '\\<^esup>' : '\U000021d6',
669
+ }
670
+
671
+ lang_map = {'isabelle' : isabelle_symbols, 'latex' : latex_symbols}
672
+
673
+ def __init__(self, **options):
674
+ Filter.__init__(self, **options)
675
+ lang = get_choice_opt(options, 'lang',
676
+ ['isabelle', 'latex'], 'isabelle')
677
+ self.symbols = self.lang_map[lang]
678
+
679
+ def filter(self, lexer, stream):
680
+ for ttype, value in stream:
681
+ if value in self.symbols:
682
+ yield ttype, self.symbols[value]
683
+ else:
684
+ yield ttype, value
685
+
686
+
687
+ class KeywordCaseFilter(Filter):
688
+ """Convert keywords to lowercase or uppercase or capitalize them, which
689
+ means first letter uppercase, rest lowercase.
690
+
691
+ This can be useful e.g. if you highlight Pascal code and want to adapt the
692
+ code to your styleguide.
693
+
694
+ Options accepted:
695
+
696
+ `case` : string
697
+ The casing to convert keywords to. Must be one of ``'lower'``,
698
+ ``'upper'`` or ``'capitalize'``. The default is ``'lower'``.
699
+ """
700
+
701
+ def __init__(self, **options):
702
+ Filter.__init__(self, **options)
703
+ case = get_choice_opt(options, 'case',
704
+ ['lower', 'upper', 'capitalize'], 'lower')
705
+ self.convert = getattr(str, case)
706
+
707
+ def filter(self, lexer, stream):
708
+ for ttype, value in stream:
709
+ if ttype in Keyword:
710
+ yield ttype, self.convert(value)
711
+ else:
712
+ yield ttype, value
713
+
714
+
715
+ class NameHighlightFilter(Filter):
716
+ """Highlight a normal Name (and Name.*) token with a different token type.
717
+
718
+ Example::
719
+
720
+ filter = NameHighlightFilter(
721
+ names=['foo', 'bar', 'baz'],
722
+ tokentype=Name.Function,
723
+ )
724
+
725
+ This would highlight the names "foo", "bar" and "baz"
726
+ as functions. `Name.Function` is the default token type.
727
+
728
+ Options accepted:
729
+
730
+ `names` : list of strings
731
+ A list of names that should be given the different token type.
732
+ There is no default.
733
+ `tokentype` : TokenType or string
734
+ A token type or a string containing a token type name that is
735
+ used for highlighting the strings in `names`. The default is
736
+ `Name.Function`.
737
+ """
738
+
739
+ def __init__(self, **options):
740
+ Filter.__init__(self, **options)
741
+ self.names = set(get_list_opt(options, 'names', []))
742
+ tokentype = options.get('tokentype')
743
+ if tokentype:
744
+ self.tokentype = string_to_tokentype(tokentype)
745
+ else:
746
+ self.tokentype = Name.Function
747
+
748
+ def filter(self, lexer, stream):
749
+ for ttype, value in stream:
750
+ if ttype in Name and value in self.names:
751
+ yield self.tokentype, value
752
+ else:
753
+ yield ttype, value
754
+
755
+
756
+ class ErrorToken(Exception):
757
+ pass
758
+
759
+
760
+ class RaiseOnErrorTokenFilter(Filter):
761
+ """Raise an exception when the lexer generates an error token.
762
+
763
+ Options accepted:
764
+
765
+ `excclass` : Exception class
766
+ The exception class to raise.
767
+ The default is `pygments.filters.ErrorToken`.
768
+
769
+ .. versionadded:: 0.8
770
+ """
771
+
772
+ def __init__(self, **options):
773
+ Filter.__init__(self, **options)
774
+ self.exception = options.get('excclass', ErrorToken)
775
+ try:
776
+ # issubclass() will raise TypeError if first argument is not a class
777
+ if not issubclass(self.exception, Exception):
778
+ raise TypeError
779
+ except TypeError:
780
+ raise OptionError('excclass option is not an exception class')
781
+
782
+ def filter(self, lexer, stream):
783
+ for ttype, value in stream:
784
+ if ttype is Error:
785
+ raise self.exception(value)
786
+ yield ttype, value
787
+
788
+
789
+ class VisibleWhitespaceFilter(Filter):
790
+ """Convert tabs, newlines and/or spaces to visible characters.
791
+
792
+ Options accepted:
793
+
794
+ `spaces` : string or bool
795
+ If this is a one-character string, spaces will be replaces by this string.
796
+ If it is another true value, spaces will be replaced by ``·`` (unicode
797
+ MIDDLE DOT). If it is a false value, spaces will not be replaced. The
798
+ default is ``False``.
799
+ `tabs` : string or bool
800
+ The same as for `spaces`, but the default replacement character is ``»``
801
+ (unicode RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK). The default value
802
+ is ``False``. Note: this will not work if the `tabsize` option for the
803
+ lexer is nonzero, as tabs will already have been expanded then.
804
+ `tabsize` : int
805
+ If tabs are to be replaced by this filter (see the `tabs` option), this
806
+ is the total number of characters that a tab should be expanded to.
807
+ The default is ``8``.
808
+ `newlines` : string or bool
809
+ The same as for `spaces`, but the default replacement character is ``¶``
810
+ (unicode PILCROW SIGN). The default value is ``False``.
811
+ `wstokentype` : bool
812
+ If true, give whitespace the special `Whitespace` token type. This allows
813
+ styling the visible whitespace differently (e.g. greyed out), but it can
814
+ disrupt background colors. The default is ``True``.
815
+
816
+ .. versionadded:: 0.8
817
+ """
818
+
819
+ def __init__(self, **options):
820
+ Filter.__init__(self, **options)
821
+ for name, default in [('spaces', '·'),
822
+ ('tabs', '»'),
823
+ ('newlines', '¶')]:
824
+ opt = options.get(name, False)
825
+ if isinstance(opt, str) and len(opt) == 1:
826
+ setattr(self, name, opt)
827
+ else:
828
+ setattr(self, name, (opt and default or ''))
829
+ tabsize = get_int_opt(options, 'tabsize', 8)
830
+ if self.tabs:
831
+ self.tabs += ' ' * (tabsize - 1)
832
+ if self.newlines:
833
+ self.newlines += '\n'
834
+ self.wstt = get_bool_opt(options, 'wstokentype', True)
835
+
836
+ def filter(self, lexer, stream):
837
+ if self.wstt:
838
+ spaces = self.spaces or ' '
839
+ tabs = self.tabs or '\t'
840
+ newlines = self.newlines or '\n'
841
+ regex = re.compile(r'\s')
842
+
843
+ def replacefunc(wschar):
844
+ if wschar == ' ':
845
+ return spaces
846
+ elif wschar == '\t':
847
+ return tabs
848
+ elif wschar == '\n':
849
+ return newlines
850
+ return wschar
851
+
852
+ for ttype, value in stream:
853
+ yield from _replace_special(ttype, value, regex, Whitespace,
854
+ replacefunc)
855
+ else:
856
+ spaces, tabs, newlines = self.spaces, self.tabs, self.newlines
857
+ # simpler processing
858
+ for ttype, value in stream:
859
+ if spaces:
860
+ value = value.replace(' ', spaces)
861
+ if tabs:
862
+ value = value.replace('\t', tabs)
863
+ if newlines:
864
+ value = value.replace('\n', newlines)
865
+ yield ttype, value
866
+
867
+
868
+ class GobbleFilter(Filter):
869
+ """Gobbles source code lines (eats initial characters).
870
+
871
+ This filter drops the first ``n`` characters off every line of code. This
872
+ may be useful when the source code fed to the lexer is indented by a fixed
873
+ amount of space that isn't desired in the output.
874
+
875
+ Options accepted:
876
+
877
+ `n` : int
878
+ The number of characters to gobble.
879
+
880
+ .. versionadded:: 1.2
881
+ """
882
+ def __init__(self, **options):
883
+ Filter.__init__(self, **options)
884
+ self.n = get_int_opt(options, 'n', 0)
885
+
886
+ def gobble(self, value, left):
887
+ if left < len(value):
888
+ return value[left:], 0
889
+ else:
890
+ return '', left - len(value)
891
+
892
+ def filter(self, lexer, stream):
893
+ n = self.n
894
+ left = n # How many characters left to gobble.
895
+ for ttype, value in stream:
896
+ # Remove ``left`` tokens from first line, ``n`` from all others.
897
+ parts = value.split('\n')
898
+ (parts[0], left) = self.gobble(parts[0], left)
899
+ for i in range(1, len(parts)):
900
+ (parts[i], left) = self.gobble(parts[i], n)
901
+ value = '\n'.join(parts)
902
+
903
+ if value != '':
904
+ yield ttype, value
905
+
906
+
907
+ class TokenMergeFilter(Filter):
908
+ """Merges consecutive tokens with the same token type in the output
909
+ stream of a lexer.
910
+
911
+ .. versionadded:: 1.2
912
+ """
913
+ def __init__(self, **options):
914
+ Filter.__init__(self, **options)
915
+
916
+ def filter(self, lexer, stream):
917
+ current_type = None
918
+ current_value = None
919
+ for ttype, value in stream:
920
+ if ttype is current_type:
921
+ current_value += value
922
+ else:
923
+ if current_type is not None:
924
+ yield current_type, current_value
925
+ current_type = ttype
926
+ current_value = value
927
+ if current_type is not None:
928
+ yield current_type, current_value
929
+
930
+
931
+ FILTERS = {
932
+ 'codetagify': CodeTagFilter,
933
+ 'keywordcase': KeywordCaseFilter,
934
+ 'highlight': NameHighlightFilter,
935
+ 'raiseonerror': RaiseOnErrorTokenFilter,
936
+ 'whitespace': VisibleWhitespaceFilter,
937
+ 'gobble': GobbleFilter,
938
+ 'tokenmerge': TokenMergeFilter,
939
+ 'symbols': SymbolFilter,
940
+ }
env/kimodo/lib/python3.10/site-packages/pygments/filters/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (29.5 kB). View file
 
env/kimodo/lib/python3.10/site-packages/pygments/formatter.py ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.formatter
3
+ ~~~~~~~~~~~~~~~~~~
4
+
5
+ Base formatter class.
6
+
7
+ :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import codecs
12
+
13
+ from pygments.util import get_bool_opt
14
+ from pygments.styles import get_style_by_name
15
+
16
+ __all__ = ['Formatter']
17
+
18
+
19
+ def _lookup_style(style):
20
+ if isinstance(style, str):
21
+ return get_style_by_name(style)
22
+ return style
23
+
24
+
25
+ class Formatter:
26
+ """
27
+ Converts a token stream to text.
28
+
29
+ Formatters should have attributes to help selecting them. These
30
+ are similar to the corresponding :class:`~pygments.lexer.Lexer`
31
+ attributes.
32
+
33
+ .. autoattribute:: name
34
+ :no-value:
35
+
36
+ .. autoattribute:: aliases
37
+ :no-value:
38
+
39
+ .. autoattribute:: filenames
40
+ :no-value:
41
+
42
+ You can pass options as keyword arguments to the constructor.
43
+ All formatters accept these basic options:
44
+
45
+ ``style``
46
+ The style to use, can be a string or a Style subclass
47
+ (default: "default"). Not used by e.g. the
48
+ TerminalFormatter.
49
+ ``full``
50
+ Tells the formatter to output a "full" document, i.e.
51
+ a complete self-contained document. This doesn't have
52
+ any effect for some formatters (default: false).
53
+ ``title``
54
+ If ``full`` is true, the title that should be used to
55
+ caption the document (default: '').
56
+ ``encoding``
57
+ If given, must be an encoding name. This will be used to
58
+ convert the Unicode token strings to byte strings in the
59
+ output. If it is "" or None, Unicode strings will be written
60
+ to the output file, which most file-like objects do not
61
+ support (default: None).
62
+ ``outencoding``
63
+ Overrides ``encoding`` if given.
64
+
65
+ """
66
+
67
+ #: Full name for the formatter, in human-readable form.
68
+ name = None
69
+
70
+ #: A list of short, unique identifiers that can be used to lookup
71
+ #: the formatter from a list, e.g. using :func:`.get_formatter_by_name()`.
72
+ aliases = []
73
+
74
+ #: A list of fnmatch patterns that match filenames for which this
75
+ #: formatter can produce output. The patterns in this list should be unique
76
+ #: among all formatters.
77
+ filenames = []
78
+
79
+ #: If True, this formatter outputs Unicode strings when no encoding
80
+ #: option is given.
81
+ unicodeoutput = True
82
+
83
+ def __init__(self, **options):
84
+ """
85
+ As with lexers, this constructor takes arbitrary optional arguments,
86
+ and if you override it, you should first process your own options, then
87
+ call the base class implementation.
88
+ """
89
+ self.style = _lookup_style(options.get('style', 'default'))
90
+ self.full = get_bool_opt(options, 'full', False)
91
+ self.title = options.get('title', '')
92
+ self.encoding = options.get('encoding', None) or None
93
+ if self.encoding in ('guess', 'chardet'):
94
+ # can happen for e.g. pygmentize -O encoding=guess
95
+ self.encoding = 'utf-8'
96
+ self.encoding = options.get('outencoding') or self.encoding
97
+ self.options = options
98
+
99
+ def get_style_defs(self, arg=''):
100
+ """
101
+ This method must return statements or declarations suitable to define
102
+ the current style for subsequent highlighted text (e.g. CSS classes
103
+ in the `HTMLFormatter`).
104
+
105
+ The optional argument `arg` can be used to modify the generation and
106
+ is formatter dependent (it is standardized because it can be given on
107
+ the command line).
108
+
109
+ This method is called by the ``-S`` :doc:`command-line option <cmdline>`,
110
+ the `arg` is then given by the ``-a`` option.
111
+ """
112
+ return ''
113
+
114
+ def format(self, tokensource, outfile):
115
+ """
116
+ This method must format the tokens from the `tokensource` iterable and
117
+ write the formatted version to the file object `outfile`.
118
+
119
+ Formatter options can control how exactly the tokens are converted.
120
+ """
121
+ if self.encoding:
122
+ # wrap the outfile in a StreamWriter
123
+ outfile = codecs.lookup(self.encoding)[3](outfile)
124
+ return self.format_unencoded(tokensource, outfile)
125
+
126
+ # Allow writing Formatter[str] or Formatter[bytes]. That's equivalent to
127
+ # Formatter. This helps when using third-party type stubs from typeshed.
128
+ def __class_getitem__(cls, name):
129
+ return cls
env/kimodo/lib/python3.10/site-packages/pygments/formatters/__init__.py ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ pygments.formatters
3
+ ~~~~~~~~~~~~~~~~~~~
4
+
5
+ Pygments formatters.
6
+
7
+ :copyright: Copyright 2006-2025 by the Pygments team, see AUTHORS.
8
+ :license: BSD, see LICENSE for details.
9
+ """
10
+
11
+ import re
12
+ import sys
13
+ import types
14
+ import fnmatch
15
+ from os.path import basename
16
+
17
+ from pygments.formatters._mapping import FORMATTERS
18
+ from pygments.plugin import find_plugin_formatters
19
+ from pygments.util import ClassNotFound
20
+
21
+ __all__ = ['get_formatter_by_name', 'get_formatter_for_filename',
22
+ 'get_all_formatters', 'load_formatter_from_file'] + list(FORMATTERS)
23
+
24
+ _formatter_cache = {} # classes by name
25
+ _pattern_cache = {}
26
+
27
+
28
+ def _fn_matches(fn, glob):
29
+ """Return whether the supplied file name fn matches pattern filename."""
30
+ if glob not in _pattern_cache:
31
+ pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob))
32
+ return pattern.match(fn)
33
+ return _pattern_cache[glob].match(fn)
34
+
35
+
36
+ def _load_formatters(module_name):
37
+ """Load a formatter (and all others in the module too)."""
38
+ mod = __import__(module_name, None, None, ['__all__'])
39
+ for formatter_name in mod.__all__:
40
+ cls = getattr(mod, formatter_name)
41
+ _formatter_cache[cls.name] = cls
42
+
43
+
44
+ def get_all_formatters():
45
+ """Return a generator for all formatter classes."""
46
+ # NB: this returns formatter classes, not info like get_all_lexers().
47
+ for info in FORMATTERS.values():
48
+ if info[1] not in _formatter_cache:
49
+ _load_formatters(info[0])
50
+ yield _formatter_cache[info[1]]
51
+ for _, formatter in find_plugin_formatters():
52
+ yield formatter
53
+
54
+
55
+ def find_formatter_class(alias):
56
+ """Lookup a formatter by alias.
57
+
58
+ Returns None if not found.
59
+ """
60
+ for module_name, name, aliases, _, _ in FORMATTERS.values():
61
+ if alias in aliases:
62
+ if name not in _formatter_cache:
63
+ _load_formatters(module_name)
64
+ return _formatter_cache[name]
65
+ for _, cls in find_plugin_formatters():
66
+ if alias in cls.aliases:
67
+ return cls
68
+
69
+
70
+ def get_formatter_by_name(_alias, **options):
71
+ """
72
+ Return an instance of a :class:`.Formatter` subclass that has `alias` in its
73
+ aliases list. The formatter is given the `options` at its instantiation.
74
+
75
+ Will raise :exc:`pygments.util.ClassNotFound` if no formatter with that
76
+ alias is found.
77
+ """
78
+ cls = find_formatter_class(_alias)
79
+ if cls is None:
80
+ raise ClassNotFound(f"no formatter found for name {_alias!r}")
81
+ return cls(**options)
82
+
83
+
84
+ def load_formatter_from_file(filename, formattername="CustomFormatter", **options):
85
+ """
86
+ Return a `Formatter` subclass instance loaded from the provided file, relative
87
+ to the current directory.
88
+
89
+ The file is expected to contain a Formatter class named ``formattername``
90
+ (by default, CustomFormatter). Users should be very careful with the input, because
91
+ this method is equivalent to running ``eval()`` on the input file. The formatter is
92
+ given the `options` at its instantiation.
93
+
94
+ :exc:`pygments.util.ClassNotFound` is raised if there are any errors loading
95
+ the formatter.
96
+
97
+ .. versionadded:: 2.2
98
+ """
99
+ try:
100
+ # This empty dict will contain the namespace for the exec'd file
101
+ custom_namespace = {}
102
+ with open(filename, 'rb') as f:
103
+ exec(f.read(), custom_namespace)
104
+ # Retrieve the class `formattername` from that namespace
105
+ if formattername not in custom_namespace:
106
+ raise ClassNotFound(f'no valid {formattername} class found in {filename}')
107
+ formatter_class = custom_namespace[formattername]
108
+ # And finally instantiate it with the options
109
+ return formatter_class(**options)
110
+ except OSError as err:
111
+ raise ClassNotFound(f'cannot read {filename}: {err}')
112
+ except ClassNotFound:
113
+ raise
114
+ except Exception as err:
115
+ raise ClassNotFound(f'error when loading custom formatter: {err}')
116
+
117
+
118
+ def get_formatter_for_filename(fn, **options):
119
+ """
120
+ Return a :class:`.Formatter` subclass instance that has a filename pattern
121
+ matching `fn`. The formatter is given the `options` at its instantiation.
122
+
123
+ Will raise :exc:`pygments.util.ClassNotFound` if no formatter for that filename
124
+ is found.
125
+ """
126
+ fn = basename(fn)
127
+ for modname, name, _, filenames, _ in FORMATTERS.values():
128
+ for filename in filenames:
129
+ if _fn_matches(fn, filename):
130
+ if name not in _formatter_cache:
131
+ _load_formatters(modname)
132
+ return _formatter_cache[name](**options)
133
+ for _name, cls in find_plugin_formatters():
134
+ for filename in cls.filenames:
135
+ if _fn_matches(fn, filename):
136
+ return cls(**options)
137
+ raise ClassNotFound(f"no formatter found for file name {fn!r}")
138
+
139
+
140
+ class _automodule(types.ModuleType):
141
+ """Automatically import formatters."""
142
+
143
+ def __getattr__(self, name):
144
+ info = FORMATTERS.get(name)
145
+ if info:
146
+ _load_formatters(info[0])
147
+ cls = _formatter_cache[info[1]]
148
+ setattr(self, name, cls)
149
+ return cls
150
+ raise AttributeError(name)
151
+
152
+
153
+ oldmod = sys.modules[__name__]
154
+ newmod = _automodule(__name__)
155
+ newmod.__dict__.update(oldmod.__dict__)
156
+ sys.modules[__name__] = newmod
157
+ del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types
env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/__init__.cpython-310.pyc ADDED
Binary file (4.96 kB). View file
 
env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/_mapping.cpython-310.pyc ADDED
Binary file (3.98 kB). View file
 
env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/bbcode.cpython-310.pyc ADDED
Binary file (3.05 kB). View file
 
env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/groff.cpython-310.pyc ADDED
Binary file (4.38 kB). View file
 
env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/html.cpython-310.pyc ADDED
Binary file (29.5 kB). View file
 
env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/img.cpython-310.pyc ADDED
Binary file (18.4 kB). View file
 
env/kimodo/lib/python3.10/site-packages/pygments/formatters/__pycache__/irc.cpython-310.pyc ADDED
Binary file (4.04 kB). View file