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.
- cartesian_tree/__init__.py +12 -0
- cartesian_tree/_cartesian_tree.cp313-win_amd64.pyd +0 -0
- cartesian_tree/helper.py +194 -0
- cartesian_tree/lib.py +258 -0
- cartesian_tree/py.typed +0 -0
- cartesian_tree-0.2.1.dist-info/METADATA +29 -0
- cartesian_tree-0.2.1.dist-info/RECORD +9 -0
- cartesian_tree-0.2.1.dist-info/WHEEL +4 -0
- cartesian_tree-0.2.1.dist-info/licenses/license-mit +21 -0
|
Binary file
|
cartesian_tree/helper.py
ADDED
|
@@ -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__()
|
cartesian_tree/py.typed
ADDED
|
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,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.
|