cartesian-tree 0.2.1__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,12 @@
1
+ """A library for calculating Cartesian poses in different coordinate systems."""
2
+
3
+ from .helper import RPY, Position, Quaternion
4
+ from .lib import Frame, Pose
5
+
6
+ __all__ = [
7
+ "RPY",
8
+ "Frame",
9
+ "Pose",
10
+ "Position",
11
+ "Quaternion",
12
+ ]
@@ -0,0 +1,194 @@
1
+ """Defines helper classes for a more pythonic API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from cartesian_tree import _cartesian_tree as _core # type: ignore[attr-defined]
8
+
9
+
10
+ class RPY:
11
+ """Defines a roll-pitch-yaw angle representation."""
12
+
13
+ def __init__(self, roll: float, pitch: float, yaw: float) -> None:
14
+ """Initializes the roll-pitch-yaw angles.
15
+
16
+ Args:
17
+ roll: The roll angle in radians.
18
+ pitch: The pitch angle in radians.
19
+ yaw: The yaw angle in radians
20
+ """
21
+ self._core_rpy = _core.RPY(roll, pitch, yaw)
22
+
23
+ @property
24
+ def roll(self) -> float:
25
+ """The roll angle in radians."""
26
+ return self._core_rpy.roll
27
+
28
+ @property
29
+ def pitch(self) -> float:
30
+ """The pitch angle in radians."""
31
+ return self._core_rpy.pitch
32
+
33
+ @property
34
+ def yaw(self) -> float:
35
+ """The yaw angle in radians."""
36
+ return self._core_rpy.yaw
37
+
38
+ @property
39
+ def _binding_structure(self) -> Any:
40
+ return self._core_rpy
41
+
42
+ def to_list(self) -> list[float]:
43
+ """Returns the angles as list.
44
+
45
+ Returns:
46
+ The angle as list.
47
+ """
48
+ return [self.roll, self.pitch, self.yaw]
49
+
50
+ def to_tuple(self) -> tuple[float, float, float]:
51
+ """Returns the angles as tuple.
52
+
53
+ Returns:
54
+ The angles as tuple.
55
+ """
56
+ return (self.roll, self.pitch, self.yaw)
57
+
58
+ def to_quaternion(self) -> Quaternion:
59
+ """Returns the angles as quaternion.
60
+
61
+ Returns:
62
+ The angles as quaternion.
63
+ """
64
+ return Quaternion(*self._core_rpy.to_quaternion().to_tuple())
65
+
66
+ def __str__(self) -> str:
67
+ return self._core_rpy.__str__()
68
+
69
+ def __repr__(self) -> str:
70
+ return self._core_rpy.__repr__()
71
+
72
+
73
+ class Quaternion:
74
+ """Defines a quaternion."""
75
+
76
+ def __init__(self, x: float, y: float, z: float, w: float) -> None:
77
+ """Initializes the quaternion.
78
+
79
+ Args:
80
+ x: The x value.
81
+ y: The y value.
82
+ z: The z value.
83
+ w: The w value.
84
+ """
85
+ self._core_quaternion = _core.Quaternion(x, y, z, w)
86
+
87
+ @property
88
+ def x(self) -> float:
89
+ """The x value."""
90
+ return self._core_quaternion.x
91
+
92
+ @property
93
+ def y(self) -> float:
94
+ """The y value."""
95
+ return self._core_quaternion.y
96
+
97
+ @property
98
+ def z(self) -> float:
99
+ """The z value."""
100
+ return self._core_quaternion.z
101
+
102
+ @property
103
+ def w(self) -> float:
104
+ """The z value."""
105
+ return self._core_quaternion.w
106
+
107
+ @property
108
+ def _binding_structure(self) -> Any:
109
+ return self._core_quaternion
110
+
111
+ def to_list(self) -> list[float]:
112
+ """Returns the quaternion as list in the form x,y,z and w.
113
+
114
+ Returns:
115
+ The quaternion as list.
116
+ """
117
+ return [self.x, self.y, self.z, self.w]
118
+
119
+ def to_tuple(self) -> tuple[float, float, float, float]:
120
+ """Returns the quaternion as tuple in the form x,y,z and w.
121
+
122
+ Returns:
123
+ The quaternion as tuple.
124
+ """
125
+ return (self.x, self.y, self.z, self.w)
126
+
127
+ def to_rpy(self) -> RPY:
128
+ """Returns the angles as quaternion.
129
+
130
+ Returns:
131
+ The angles as quaternion.
132
+ """
133
+ return RPY(*self._core_quaternion.to_rpy().to_tuple())
134
+
135
+ def __str__(self) -> str:
136
+ return self._core_quaternion.__str__()
137
+
138
+ def __repr__(self) -> str:
139
+ return self._core_quaternion.__repr__()
140
+
141
+
142
+ class Position:
143
+ """Defines a position."""
144
+
145
+ def __init__(self, x: float, y: float, z: float) -> None:
146
+ """Initializes the position in meter.
147
+
148
+ Args:
149
+ x: The x value in meter.
150
+ y: The y value in meter.
151
+ z: The z value in meter.
152
+ """
153
+ self._core_position = _core.Position(x, y, z)
154
+
155
+ @property
156
+ def x(self) -> float:
157
+ """The x value in meter."""
158
+ return self._core_position.x
159
+
160
+ @property
161
+ def y(self) -> float:
162
+ """The y value in meter."""
163
+ return self._core_position.y
164
+
165
+ @property
166
+ def z(self) -> float:
167
+ """The z value in meter."""
168
+ return self._core_position.z
169
+
170
+ @property
171
+ def _binding_structure(self) -> Any:
172
+ return self._core_position
173
+
174
+ def to_list(self) -> list[float]:
175
+ """Returns the position as list.
176
+
177
+ Returns:
178
+ The position as list.
179
+ """
180
+ return [self.x, self.y, self.z]
181
+
182
+ def to_tuple(self) -> tuple[float, float, float]:
183
+ """Returns the position as tuple.
184
+
185
+ Returns:
186
+ The position as tuple.
187
+ """
188
+ return (self.x, self.y, self.z)
189
+
190
+ def __str__(self) -> str:
191
+ return self._core_position.__str__()
192
+
193
+ def __repr__(self) -> str:
194
+ return self._core_position.__repr__()
cartesian_tree/lib.py ADDED
@@ -0,0 +1,258 @@
1
+ """Defines a the main lib classes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ from .helper import RPY, Position, Quaternion
8
+ from cartesian_tree import _cartesian_tree as _core # type: ignore[attr-defined]
9
+
10
+
11
+ class Frame:
12
+ """Defines a coordinate frame in a Cartesian tree structure.
13
+
14
+ Each frame can have one parent and multiple children. The frame stores its
15
+ transformation (position and orientation) relative to its parent.
16
+ """
17
+
18
+ def __init__(self, name: str) -> None:
19
+ """Initializes a new root frame (origin) with the given name.
20
+
21
+ Args:
22
+ name: The name of the root frame.
23
+ """
24
+ self._core_frame = _core.Frame(name)
25
+
26
+ @property
27
+ def name(self) -> str:
28
+ """The name of the frame."""
29
+ return self._core_frame.name
30
+
31
+ @property
32
+ def depth(self) -> int:
33
+ """The depth from the frame to its root."""
34
+ return self._core_frame.depth
35
+
36
+ def add_child(self, name: str, position: Position, orientation: RPY | Quaternion) -> Frame:
37
+ """Adds a new child frame to the current frame.
38
+
39
+ Args:
40
+ name: The name of the new child frame.
41
+ position: The translational offset from the parent.
42
+ orientation: The orientational offset from the parent.
43
+
44
+ Returns:
45
+ The newly created child frame.
46
+
47
+ Raises:
48
+ ValueError: If a child with the same name already exists.
49
+ """
50
+ if isinstance(orientation, RPY):
51
+ orientation = orientation.to_quaternion()
52
+
53
+ binding_frame = self._core_frame.add_child(name, position._binding_structure, orientation._binding_structure)
54
+ return Frame._from_rust(binding_frame)
55
+
56
+ def calibrate_child(
57
+ self, name: str, desired_position: Position, desired_orientation: RPY | Quaternion, reference_pose: Pose
58
+ ) -> Frame:
59
+ """Adds a child frame such that a reference pose, expressed in the new frame, matches the desired isometry.
60
+
61
+ Args:
62
+ name: The name of the new child frame.
63
+ desired_position: The desired position of the reference pose in the new frame.
64
+ desired_orientation: The desired orientation of the reference pose in the new frame.
65
+ reference_pose: The reference pose for calibration.
66
+
67
+ Returns:
68
+ The newly created child frame.
69
+
70
+ Raises:
71
+ ValueError: If a child with the same name already exists.
72
+ """
73
+ if isinstance(desired_orientation, RPY):
74
+ desired_orientation = desired_orientation.to_quaternion()
75
+
76
+ binding_frame = self._core_frame.calibrate_child(
77
+ name,
78
+ desired_position._binding_structure,
79
+ desired_orientation._binding_structure,
80
+ reference_pose._binding_structure,
81
+ )
82
+ return Frame._from_rust(binding_frame)
83
+
84
+ def add_pose(self, position: Position, orientation: RPY | Quaternion) -> Pose:
85
+ """Adds a pose to the current frame.
86
+
87
+ Args:
88
+ position: The translational part of the pose.
89
+ orientation: The orientational part of the pose.
90
+
91
+ Returns:
92
+ The newly created pose.
93
+ """
94
+ if isinstance(orientation, RPY):
95
+ orientation = orientation.to_quaternion()
96
+
97
+ binding_pose = self._core_frame.add_pose(position._binding_structure, orientation._binding_structure)
98
+ return Pose._from_rust(binding_pose)
99
+
100
+ def transformation_to_parent(self) -> tuple[Position, Quaternion]:
101
+ """Returns the transformation from this frame to its parent frame.
102
+
103
+ Returns:
104
+ The transformation from this frame to its parent frame (Position, Quaternion(x, y, z, w)).
105
+
106
+ Raises:
107
+ ValueError: If the frame has no parent.
108
+ """
109
+ binding_position, binding_quat = self._core_frame.transformation_to_parent()
110
+ return (
111
+ Position(*binding_position.to_tuple()),
112
+ Quaternion(*binding_quat.to_tuple()),
113
+ )
114
+
115
+ def update_transformation(self, position: Position, orientation: RPY | Quaternion) -> None:
116
+ """Updates the frames transformation relative to its parent.
117
+
118
+ Args:
119
+ position: The translational offset from the parent.
120
+ orientation: The orientational offset from the parent.
121
+
122
+ Raises:
123
+ ValueError: If the frame has no parent or invalid dimensions.
124
+ """
125
+ if isinstance(orientation, RPY):
126
+ orientation = orientation.to_quaternion()
127
+ self._core_frame.update_transformation(position._binding_structure, orientation._binding_structure)
128
+
129
+ def to_json(self) -> str:
130
+ """Serializes the frame tree to a JSON string.
131
+
132
+ Returns:
133
+ The JSON representation of the tree.
134
+
135
+ Raises:
136
+ ValueError: On serialization failure.
137
+ """
138
+ return self._core_frame.to_json()
139
+
140
+ def apply_config(self, config_json: str) -> None:
141
+ """Applies a JSON config to update matching transforms in the tree.
142
+
143
+ Deserializes the JSON to a temporary structure, then recursively updates transforms to the
144
+ parent frames where names match (partial apply; ignores unmatched frames in config).
145
+ It is assumed this frame is the root.
146
+
147
+ Args:
148
+ config_json: The JSON string to apply.
149
+
150
+ Raises:
151
+ ValueError: On deserialization or mismatch errors (e.g. if this frame is not the root).
152
+ """
153
+ self._core_frame.apply_config(config_json)
154
+
155
+ def parent(self) -> Frame | None:
156
+ """Returns the parent of the frame.
157
+
158
+ Returns:
159
+ The parent of the frame.
160
+ """
161
+ binding_parent = self._core_frame.parent()
162
+ if binding_parent is None:
163
+ return None
164
+ return Frame._from_rust(binding_parent)
165
+
166
+ def root(self) -> Frame:
167
+ """Returns the root frame of the tree.
168
+
169
+ Returns:
170
+ The root frame of the tree.
171
+ """
172
+ binding_root = self._core_frame.root()
173
+ return Frame._from_rust(binding_root)
174
+
175
+ def children(self) -> list[Frame]:
176
+ """Returns the children of the frame.
177
+
178
+ Returns:
179
+ The children of the frame.
180
+ """
181
+ return [Frame._from_rust(binding_child) for binding_child in self._core_frame.children()]
182
+
183
+ def __str__(self) -> str:
184
+ return self._core_frame.__str__()
185
+
186
+ def __repr__(self) -> str:
187
+ return self._core_frame.__repr__()
188
+
189
+ @property
190
+ def _binding_structure(self) -> Any:
191
+ return self._core_frame
192
+
193
+ @classmethod
194
+ def _from_rust(cls, rust_frame: _core.Frame) -> Frame:
195
+ instance = cls.__new__(cls)
196
+ instance._core_frame = rust_frame
197
+ return instance
198
+
199
+
200
+ class Pose:
201
+ """Defines a Cartesian pose."""
202
+
203
+ _core_pose: _core.Pose
204
+
205
+ def frame(self) -> Frame:
206
+ """Returns the frame of the pose."""
207
+ return Frame._from_rust(self._core_pose.frame())
208
+
209
+ def transformation(self) -> tuple[Position, Quaternion]:
210
+ """Returns the transformation of the pose to its parent frame.
211
+
212
+ Returns:
213
+ The transformation from this frame to its parent frame (position, quaternion(x, y, z, w)).
214
+ """
215
+ binding_position, binding_quat = self._core_pose.transformation()
216
+ return (
217
+ Position(*binding_position.to_tuple()),
218
+ Quaternion(*binding_quat.to_tuple()),
219
+ )
220
+
221
+ def update(self, position: Position, orientation: RPY | Quaternion) -> None:
222
+ """Updates the pose's transformation.
223
+
224
+ Args:
225
+ position: The translational part of the pose.
226
+ orientation: The orientational part of the pose.
227
+ """
228
+ if isinstance(orientation, RPY):
229
+ orientation = orientation.to_quaternion()
230
+ self._core_pose.update(position._binding_structure, orientation._binding_structure)
231
+
232
+ def in_frame(self, target_frame: Frame) -> Pose:
233
+ """Transforms this pose into the coordinate system of the given target frame.
234
+
235
+ Args:
236
+ target_frame: The frame to express this pose in.
237
+
238
+ Returns:
239
+ This pose in the new frame.
240
+ """
241
+ binding_pose = self._core_pose.in_frame(target_frame._binding_structure)
242
+ return Pose._from_rust(binding_pose)
243
+
244
+ @property
245
+ def _binding_structure(self) -> Any:
246
+ return self._core_pose
247
+
248
+ @classmethod
249
+ def _from_rust(cls, rust_pose: _core.Pose) -> Pose:
250
+ instance = cls.__new__(cls)
251
+ instance._core_pose = rust_pose
252
+ return instance
253
+
254
+ def __str__(self) -> str:
255
+ return self._core_pose.__str__()
256
+
257
+ def __repr__(self) -> str:
258
+ return self._core_pose.__repr__()
File without changes
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: cartesian-tree
3
+ Version: 0.2.1
4
+ Classifier: Programming Language :: Python
5
+ Requires-Dist: cartesian-tree[lint] ; extra == 'dev'
6
+ Requires-Dist: cartesian-tree[test] ; extra == 'dev'
7
+ Requires-Dist: cartesian-tree[build] ; extra == 'dev'
8
+ Requires-Dist: ruff==0.12.4 ; extra == 'lint'
9
+ Requires-Dist: mypy==1.17.0 ; extra == 'lint'
10
+ Requires-Dist: pytest==8.4.1 ; extra == 'test'
11
+ Requires-Dist: maturin==1.8.6 ; extra == 'build'
12
+ Provides-Extra: dev
13
+ Provides-Extra: lint
14
+ Provides-Extra: test
15
+ Provides-Extra: build
16
+ License-File: license-mit
17
+ Summary: Build hierarchical Cartesian coordinate systems to easily transform poses.
18
+ Keywords: Cartesian,coordinate systems,transform,poses
19
+ Home-Page: https://github.com/Kn0g/cartesian-tree
20
+ Author: Kn0g
21
+ Maintainer: Kn0g
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
24
+
25
+ # cartesian-tree
26
+
27
+ A simple project for me to learn Rust and the binding process.
28
+
29
+ Useful for converting Cartesian poses from one coordinate system into another.
@@ -0,0 +1,9 @@
1
+ cartesian_tree-0.2.1.dist-info/METADATA,sha256=horSd_5vR-PEYtHaiKFlmvrH371lDpXwXlEk7enEQjI,1035
2
+ cartesian_tree-0.2.1.dist-info/WHEEL,sha256=TJQY77QRLvXq32tEs9ATmwKO6NAOtuKOAw50eyiSmWU,96
3
+ cartesian_tree-0.2.1.dist-info/licenses/license-mit,sha256=13YSIF32H4sbmK-1C4usuKbV7rOjXHJmkwMW7hGM3qA,1084
4
+ cartesian_tree/__init__.py,sha256=Nnetfz3mSo_OkN6AJfOfy-vmw7-cSfjLb2x7GUhcceA,254
5
+ cartesian_tree/_cartesian_tree.cp313-win_amd64.pyd,sha256=voD8JBUmepfJy_D7NFikzJQhTJBoGt3JhiTF8HcKRNw,476160
6
+ cartesian_tree/helper.py,sha256=IFNIJAM2wLKtQk_7fwGh8Ha6XwS5UnAoLNYWaOacY_E,5045
7
+ cartesian_tree/lib.py,sha256=4VN7R-Hi_a6vdsRYnD7XEaCy0pIb-ilYk8DB5WlxUmI,8952
8
+ cartesian_tree/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ cartesian_tree-0.2.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: maturin (1.9.6)
3
+ Root-Is-Purelib: false
4
+ Tag: cp313-cp313-win_amd64
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2025] [Kn0g]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.