pfc-geometry 0.2.17__py3-none-any.whl → 0.2.20__py3-none-any.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.
- geometry/__init__.py +1 -10
- geometry/angles.py +19 -0
- geometry/base.py +3 -0
- geometry/coordinate_frame.py +33 -0
- geometry/point.py +46 -11
- geometry/quaternion.py +1 -1
- geometry/transformation.py +12 -7
- {pfc_geometry-0.2.17.dist-info → pfc_geometry-0.2.20.dist-info}/METADATA +1 -1
- pfc_geometry-0.2.20.dist-info/RECORD +17 -0
- pfc_geometry-0.2.17.dist-info/RECORD +0 -16
- {pfc_geometry-0.2.17.dist-info → pfc_geometry-0.2.20.dist-info}/WHEEL +0 -0
- {pfc_geometry-0.2.17.dist-info → pfc_geometry-0.2.20.dist-info}/licenses/LICENSE +0 -0
geometry/__init__.py
CHANGED
|
@@ -11,6 +11,7 @@ this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
11
11
|
"""
|
|
12
12
|
from .base import Base
|
|
13
13
|
from .time import Time
|
|
14
|
+
from . import angles as angles
|
|
14
15
|
from .point import *
|
|
15
16
|
from .quaternion import *
|
|
16
17
|
from .gps import GPS
|
|
@@ -27,13 +28,3 @@ def Euldeg(*args, **kwargs) -> Quaternion:
|
|
|
27
28
|
return Quaternion.from_euler(Point(*args, **kwargs).radians())
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
def angle_diff(a, b):
|
|
31
|
-
d1 = a - b
|
|
32
|
-
d2 = d1 - 2 * np.pi
|
|
33
|
-
bd=np.abs(d2) < np.abs(d1)
|
|
34
|
-
d1[bd] = d2[bd]
|
|
35
|
-
d3 = d1 + 2 * np.pi
|
|
36
|
-
bd=np.abs(d3) < np.abs(d1)
|
|
37
|
-
d1[bd] = d3[bd]
|
|
38
|
-
|
|
39
|
-
return d1
|
geometry/angles.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import numpy.typing as npt
|
|
3
|
+
|
|
4
|
+
def unwind(angles: npt.NDArray, center: float = 0.0):
|
|
5
|
+
"""given an angle, unwind it to the range [-pi, pi] around a center point."""
|
|
6
|
+
turns = np.round((angles - center) / (2 * np.pi))
|
|
7
|
+
return angles - turns * 2 * np.pi
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def difference(a, b):
|
|
11
|
+
d1 = a - b
|
|
12
|
+
d2 = d1 - 2 * np.pi
|
|
13
|
+
bd=np.abs(d2) < np.abs(d1)
|
|
14
|
+
d1[bd] = d2[bd]
|
|
15
|
+
d3 = d1 + 2 * np.pi
|
|
16
|
+
bd=np.abs(d3) < np.abs(d1)
|
|
17
|
+
d1[bd] = d3[bd]
|
|
18
|
+
|
|
19
|
+
return d1
|
geometry/base.py
CHANGED
|
@@ -252,6 +252,9 @@ class Base:
|
|
|
252
252
|
def __neg__(self) -> Self:
|
|
253
253
|
return self.__class__(-self.data)
|
|
254
254
|
|
|
255
|
+
def __pow__(self, power: Number) -> Self:
|
|
256
|
+
return self.__class__(self.data ** power)
|
|
257
|
+
|
|
255
258
|
@dprep
|
|
256
259
|
def dot(self, other: Self) -> Self:
|
|
257
260
|
return np.einsum("ij,ij->i", self.data, other)
|
geometry/coordinate_frame.py
CHANGED
|
@@ -87,3 +87,36 @@ class Coord(Base):
|
|
|
87
87
|
|
|
88
88
|
def axes(self):
|
|
89
89
|
return Point.concatenate([self.x_axis, self.y_axis, self.z_axis])
|
|
90
|
+
|
|
91
|
+
def plot(self, fig=None, scale=1, label: str = None):
|
|
92
|
+
import plotly.graph_objects as go
|
|
93
|
+
if fig is None:
|
|
94
|
+
fig = go.Figure(layout=dict(scene=dict(aspectmode="data")))
|
|
95
|
+
|
|
96
|
+
if len(self) > 1:
|
|
97
|
+
for c in self:
|
|
98
|
+
fig = c.plot(fig)
|
|
99
|
+
return fig
|
|
100
|
+
fig.add_trace(
|
|
101
|
+
go.Scatter3d(
|
|
102
|
+
x=self.origin.x,
|
|
103
|
+
y=self.origin.y,
|
|
104
|
+
z=self.origin.z,
|
|
105
|
+
mode="markers",
|
|
106
|
+
name="Origin",
|
|
107
|
+
marker=dict(size=5, color="black"),
|
|
108
|
+
)
|
|
109
|
+
)
|
|
110
|
+
colors = ["red", "green", "blue"]
|
|
111
|
+
for i, axis in enumerate([self.x_axis, self.y_axis, self.z_axis]):
|
|
112
|
+
fig.add_trace(
|
|
113
|
+
go.Scatter3d(
|
|
114
|
+
x=[self.origin.x[0], (self.origin.x + axis.x * scale)[0]],
|
|
115
|
+
y=[self.origin.y[0], (self.origin.y + axis.y * scale)[0]],
|
|
116
|
+
z=[self.origin.z[0], (self.origin.z + axis.z * scale)[0]],
|
|
117
|
+
mode="lines",
|
|
118
|
+
name=f"{label or 'Axis'} {Point.cols[i]}",
|
|
119
|
+
line=dict(width=2, color=colors.pop(0))
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
return fig
|
geometry/point.py
CHANGED
|
@@ -150,18 +150,45 @@ class Point(Base):
|
|
|
150
150
|
def zeros(count=1):
|
|
151
151
|
return Point(np.zeros((count, 3)))
|
|
152
152
|
|
|
153
|
+
@staticmethod
|
|
154
|
+
def circle_xy(radius: float, n: int) -> Point:
|
|
155
|
+
"""
|
|
156
|
+
Generate points on a circle in the specified plane.
|
|
157
|
+
|
|
158
|
+
:param radius: Radius of the circle.
|
|
159
|
+
:param n: Number of points to generate.
|
|
160
|
+
:return: Points on the circle as a Point object.
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
angles = np.linspace(0, 2 * np.pi, n, endpoint=False)
|
|
164
|
+
return Point(radius * np.cos(angles), radius * np.sin(angles), np.zeros(n))
|
|
165
|
+
|
|
166
|
+
@staticmethod
|
|
167
|
+
def ellipse_xy(a: float, b: float, n: int) -> Point:
|
|
168
|
+
"""
|
|
169
|
+
Generate points on an ellipse in the specified plane.
|
|
170
|
+
|
|
171
|
+
:param a: Semi-major axis length.
|
|
172
|
+
:param b: Semi-minor axis length.
|
|
173
|
+
:param n: Number of points to generate.
|
|
174
|
+
:return: Points on the ellipse as a Point object.
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
angles = np.linspace(0, 2 * np.pi, n, endpoint=False)
|
|
178
|
+
return Point(a * np.cos(angles), b * np.sin(angles), np.zeros(n))
|
|
179
|
+
|
|
153
180
|
def bearing(self):
|
|
154
181
|
return np.arctan2(self.y, self.x)
|
|
155
182
|
|
|
156
|
-
def plot3d(self, **kwargs):
|
|
183
|
+
def plot3d(self, fig=None, **kwargs):
|
|
157
184
|
import plotly.graph_objects as go
|
|
158
|
-
fig = go.Figure()
|
|
159
185
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
186
|
+
_fig = go.Figure() if fig is None else fig
|
|
187
|
+
|
|
188
|
+
_fig.add_trace(go.Scatter3d(x=self.x, y=self.y, z=self.z, **kwargs))
|
|
189
|
+
if fig is None:
|
|
190
|
+
_fig.update_layout(scene=dict(aspectmode="data"))
|
|
191
|
+
return _fig
|
|
165
192
|
|
|
166
193
|
def plotxy(self):
|
|
167
194
|
import plotly.express as px
|
|
@@ -183,6 +210,18 @@ class Point(Base):
|
|
|
183
210
|
return px.line(self.df, x="z", y="x").update_layout(
|
|
184
211
|
yaxis=dict(scaleanchor="x", scaleratio=1, title="x"), xaxis=dict(title="z")
|
|
185
212
|
)
|
|
213
|
+
def plotxz(self):
|
|
214
|
+
import plotly.express as px
|
|
215
|
+
|
|
216
|
+
return px.line(self.df, x="x", y="z").update_layout(
|
|
217
|
+
yaxis=dict(scaleanchor="x", scaleratio=1, title="x"), xaxis=dict(title="z")
|
|
218
|
+
)
|
|
219
|
+
def arbitrary_perpendicular(self) -> Point:
|
|
220
|
+
min_axes = np.argmin(np.abs(self.data), axis=1)
|
|
221
|
+
cvecs = Point.concatenate(
|
|
222
|
+
[Point(*[1 if axis == i else 0 for i in np.arange(3)]) for axis in min_axes]
|
|
223
|
+
)
|
|
224
|
+
return cross(self, cvecs)
|
|
186
225
|
|
|
187
226
|
|
|
188
227
|
def Points(*args, **kwargs):
|
|
@@ -262,10 +301,6 @@ def min_angle_between(p1: Point, p2: Point):
|
|
|
262
301
|
return np.minimum(angle, np.pi - angle)
|
|
263
302
|
|
|
264
303
|
|
|
265
|
-
def arbitrary_perpendicular(v: Point) -> Point:
|
|
266
|
-
return Point(-v.y, v.x, 0).unit()
|
|
267
|
-
|
|
268
|
-
|
|
269
304
|
def vector_norm(point: Point):
|
|
270
305
|
return abs(point)
|
|
271
306
|
|
geometry/quaternion.py
CHANGED
|
@@ -12,7 +12,7 @@ this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
from .point import Point
|
|
14
14
|
from .base import Base
|
|
15
|
-
from geometry import PZ
|
|
15
|
+
from geometry.point import PZ
|
|
16
16
|
import numpy as np
|
|
17
17
|
import numpy.typing as npt
|
|
18
18
|
import pandas as pd
|
geometry/transformation.py
CHANGED
|
@@ -40,9 +40,6 @@ class Transformation(Base):
|
|
|
40
40
|
@property
|
|
41
41
|
def q(self):
|
|
42
42
|
return Quaternion(self.data[:,3:])
|
|
43
|
-
|
|
44
|
-
def offset(self, p: Point):
|
|
45
|
-
return Transformation(self.p + p, self.q)
|
|
46
43
|
|
|
47
44
|
def __getattr__(self, name):
|
|
48
45
|
if name in list("xyz"):
|
|
@@ -94,7 +91,6 @@ class Transformation(Base):
|
|
|
94
91
|
coord_b.origin - coord_a.origin,
|
|
95
92
|
-q1 * q2
|
|
96
93
|
)
|
|
97
|
-
|
|
98
94
|
|
|
99
95
|
def apply(self, oin: Point | Quaternion | Self | Coord):
|
|
100
96
|
if isinstance(oin, Point):
|
|
@@ -115,6 +111,14 @@ class Transformation(Base):
|
|
|
115
111
|
else:
|
|
116
112
|
raise TypeError(f"expected a Point or a Quaternion, got a {oin.__class__.__name__}")
|
|
117
113
|
|
|
114
|
+
def offset(self, p: Point | Self):
|
|
115
|
+
if isinstance(p, Point):
|
|
116
|
+
return Transformation(self.p + p, self.q)
|
|
117
|
+
elif isinstance(p, self.__class__):
|
|
118
|
+
return Transformation(self.p + p.p, self.q * p.q)
|
|
119
|
+
else:
|
|
120
|
+
raise TypeError(f"expected a Point or a Transformation, got a {p.__class__.__name__}")
|
|
121
|
+
|
|
118
122
|
def translate(self, point: Point):
|
|
119
123
|
return point + self.p
|
|
120
124
|
|
|
@@ -134,11 +138,12 @@ class Transformation(Base):
|
|
|
134
138
|
return outarr
|
|
135
139
|
|
|
136
140
|
|
|
137
|
-
def
|
|
141
|
+
def plot(self, fig=None, size: float=3, vis:Literal["coord", "plane"]="coord"):
|
|
138
142
|
import plotly.graph_objects as go
|
|
139
143
|
from plotting.traces import axestrace, meshes
|
|
140
|
-
|
|
141
|
-
|
|
144
|
+
if fig is None:
|
|
145
|
+
import plotting.templates
|
|
146
|
+
fig = go.Figure(layout=dict(template="generic3d+clean_paper"))
|
|
142
147
|
if vis=="coord":
|
|
143
148
|
fig.add_traces(axestrace(self, length=size))
|
|
144
149
|
elif vis=="plane":
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
geometry/__init__.py,sha256=Qipz0oxyBg-C_-sFLVI8LKW9Yq8_keR-CbyuseAvTGg,1082
|
|
2
|
+
geometry/angles.py,sha256=Gw4PXr2kQMvvKECsugmi5kNiqOnK8T3PZSo3IKUpxPY,480
|
|
3
|
+
geometry/base.py,sha256=6bM4J7VGhstel6qu-C4xU39vU4dZ71S9FZMY9Y7xDZY,14966
|
|
4
|
+
geometry/checks.py,sha256=o8yMBAdU5Vy0EspBYaof4fPGgRSFZhRDhzBjRPsLd0M,375
|
|
5
|
+
geometry/coordinate_frame.py,sha256=6KzRu0sSPzmHj5vLIuX5Efi0OWdEF6gQtKPf63wsaRk,4358
|
|
6
|
+
geometry/gps.py,sha256=EsokABt40ZoltpAQfKrRc4kA-Lc2ScP_ltJNF7pvAWc,3654
|
|
7
|
+
geometry/mass.py,sha256=BUWBSITwpdRfpJR5-oJTd16BI7FLZt8rhxdzr0cx1HY,1675
|
|
8
|
+
geometry/point.py,sha256=wjAau7PiTpoIHMMvDDexXq8PisE5RdAQqT7Ze3NTm6A,8477
|
|
9
|
+
geometry/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
geometry/quaternion.py,sha256=dZwpViiVPwHUhRk7DhOwNab3pLhIWXyHk3DVh9mukSk,12143
|
|
11
|
+
geometry/time.py,sha256=VTfMHLxhcws8YESYvxxP8W_vSePvl4lwKRXHxFWhJeA,2695
|
|
12
|
+
geometry/transformation.py,sha256=wnxoDN9-IeapVlrS_Da4hXyxtPCyoa_UcCsB8QVgxuo,5539
|
|
13
|
+
geometry/utils.py,sha256=q7-aaxDzRDwl78-3XzdpcJuh5iL7I8lN5agk2WNWjSY,3443
|
|
14
|
+
pfc_geometry-0.2.20.dist-info/METADATA,sha256=XXAr-l0CNGbvC2GTXtUj0os-mDRYN_3OK7LwAft9LoU,1629
|
|
15
|
+
pfc_geometry-0.2.20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
+
pfc_geometry-0.2.20.dist-info/licenses/LICENSE,sha256=z72U6pv-bQgJ_Svr4uCXnMjemsp38aSerhHEdEAOMJ4,7632
|
|
17
|
+
pfc_geometry-0.2.20.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
geometry/__init__.py,sha256=HNhMyemIJzDq1nDjrr09eX5PS7q9ULscSbYsXss3JRM,1253
|
|
2
|
-
geometry/base.py,sha256=aYHsY8WdXFN17EYcUTjjn7Rh9Z8LAZpvn-jsMm_niRU,14869
|
|
3
|
-
geometry/checks.py,sha256=o8yMBAdU5Vy0EspBYaof4fPGgRSFZhRDhzBjRPsLd0M,375
|
|
4
|
-
geometry/coordinate_frame.py,sha256=YTCAtCUuIq5LAsO-P9FwFs55f4qpWP9pUP6mf-Nhk54,3145
|
|
5
|
-
geometry/gps.py,sha256=EsokABt40ZoltpAQfKrRc4kA-Lc2ScP_ltJNF7pvAWc,3654
|
|
6
|
-
geometry/mass.py,sha256=BUWBSITwpdRfpJR5-oJTd16BI7FLZt8rhxdzr0cx1HY,1675
|
|
7
|
-
geometry/point.py,sha256=FR2TBAV-YyKei0VA4ECDGA7ah5KRUhSKa2u_pVfAx-s,7057
|
|
8
|
-
geometry/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
geometry/quaternion.py,sha256=J7Yk7qAUsH8SfjhcqQAF7Lw5hk_LGfpotVaVRyJnmeQ,12137
|
|
10
|
-
geometry/time.py,sha256=VTfMHLxhcws8YESYvxxP8W_vSePvl4lwKRXHxFWhJeA,2695
|
|
11
|
-
geometry/transformation.py,sha256=pEJXS7JYxu-f05ELTUUU4w7swcQPAgAxQNI6vPp5VCg,5242
|
|
12
|
-
geometry/utils.py,sha256=q7-aaxDzRDwl78-3XzdpcJuh5iL7I8lN5agk2WNWjSY,3443
|
|
13
|
-
pfc_geometry-0.2.17.dist-info/METADATA,sha256=9vxRWWyYmXUu7wmjaqTtMp015ePjd4BPAOJnktIfgI4,1629
|
|
14
|
-
pfc_geometry-0.2.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
-
pfc_geometry-0.2.17.dist-info/licenses/LICENSE,sha256=z72U6pv-bQgJ_Svr4uCXnMjemsp38aSerhHEdEAOMJ4,7632
|
|
16
|
-
pfc_geometry-0.2.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|