pfc-geometry 0.1.2__tar.gz → 0.1.4__tar.gz

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.
Files changed (27) hide show
  1. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/PKG-INFO +4 -1
  2. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/geometry/point.py +8 -20
  3. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/geometry/quaternion.py +44 -21
  4. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/pfc_geometry.egg-info/PKG-INFO +4 -1
  5. pfc_geometry-0.1.4/pfc_geometry.egg-info/top_level.txt +1 -0
  6. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/setup.cfg +1 -1
  7. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/tests/test_point.py +13 -9
  8. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/tests/test_quaternion.py +2 -1
  9. pfc_geometry-0.1.2/pfc_geometry.egg-info/top_level.txt +0 -1
  10. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/LICENSE +0 -0
  11. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/README.md +0 -0
  12. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/geometry/__init__.py +0 -0
  13. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/geometry/base.py +0 -0
  14. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/geometry/coordinate_frame.py +0 -0
  15. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/geometry/gps.py +0 -0
  16. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/geometry/mass.py +0 -0
  17. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/geometry/testing.py +0 -0
  18. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/geometry/transformation.py +0 -0
  19. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/pfc_geometry.egg-info/SOURCES.txt +0 -0
  20. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/pfc_geometry.egg-info/dependency_links.txt +0 -0
  21. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/pfc_geometry.egg-info/requires.txt +1 -1
  22. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/setup.py +0 -0
  23. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/tests/test_base.py +0 -0
  24. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/tests/test_coord.py +0 -0
  25. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/tests/test_gps.py +0 -0
  26. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/tests/test_mass.py +0 -0
  27. {pfc_geometry-0.1.2 → pfc_geometry-0.1.4}/tests/test_transform.py +0 -0
@@ -1,10 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pfc_geometry
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: A package for handling 3D geometry with a nice interface
5
5
  Home-page: https://github.com/PyFlightCoach/geometry
6
6
  Author: Thomas David
7
7
  Author-email: thomasdavid0@gmail.com
8
+ License: UNKNOWN
9
+ Platform: UNKNOWN
8
10
  Description-Content-Type: text/markdown
9
11
  License-File: LICENSE
10
12
 
@@ -22,3 +24,4 @@ Many convenience methods and constructors are available. Documentation is limite
22
24
  now available on pypi:
23
25
 
24
26
  pip install pfc-geometry
27
+
@@ -9,6 +9,7 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9
9
  You should have received a copy of the GNU General Public License along with
10
10
  this program. If not, see <http://www.gnu.org/licenses/>.
11
11
  """
12
+ from __future__ import annotations
12
13
  from .base import Base
13
14
  import numpy as np
14
15
  import pandas as pd
@@ -23,15 +24,13 @@ class Point(Base):
23
24
  "arcsin","arccos","arctan",
24
25
  ]
25
26
 
26
- def scale(self, value):
27
- a, b=value, abs(self)
28
- res = a/b
29
- res[b==0] = 0
30
- res = self * res
27
+ def scale(self, value) -> Point:
28
+ with np.errstate(divide="ignore"):
29
+ res = value/abs(self)
30
+ res[res==np.inf] = 0
31
+ return self * res
31
32
 
32
- return res
33
-
34
- def unit(self):
33
+ def unit(self) -> Point:
35
34
  return self.scale(1)
36
35
 
37
36
  def remove_outliers(self, nstds = 2):
@@ -141,31 +140,22 @@ def cross(a, b) -> Point:
141
140
 
142
141
  @ppmeth
143
142
  def cos_angle_between(a: Point, b: Point) -> np.ndarray:
144
- if a == 0 or b == 0:
145
- raise ValueError("cannot measure the angle to a zero length vector")
146
143
  return a.unit().dot(b.unit())
147
144
 
148
-
149
145
  @ppmeth
150
146
  def angle_between(a: Point, b: Point) -> np.ndarray:
151
147
  return np.arccos(a.cos_angle_between(b))
152
148
 
153
149
  @ppmeth
154
150
  def scalar_projection(a: Point, b: Point) -> Point:
155
- if a==0 or b==0:
156
- return 0
157
151
  return a.cos_angle_between(b) * abs(a)
158
152
 
159
153
  @ppmeth
160
154
  def vector_projection(a: Point, b: Point) -> Point:
161
- if abs(a) == 0:
162
- return Point.zeros()
163
155
  return b.scale(a.scalar_projection(b))
164
156
 
165
157
  @ppmeth
166
158
  def is_parallel(a: Point, b: Point, tolerance=1e-6):
167
- if a.unit() == b.unit():
168
- return True
169
159
  return abs(a.cos_angle_between(b) - 1) < tolerance
170
160
 
171
161
  @ppmeth
@@ -182,9 +172,7 @@ def angle_between(a: Point, b: Point) -> float:
182
172
  return np.arccos(cos_angle_between(a, b))
183
173
 
184
174
  def arbitrary_perpendicular(v: Point) -> Point:
185
- if v.x == 0 and v.y == 0:
186
- return Point(0, 1, 0)
187
- return Point(-v.y, v.x, 0).unit
175
+ return Point(-v.y, v.x, 0).unit()
188
176
 
189
177
  def vector_norm(point: Point):
190
178
  return abs(point)
@@ -9,7 +9,8 @@ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9
9
  You should have received a copy of the GNU General Public License along with
10
10
  this program. If not, see <http://www.gnu.org/licenses/>.
11
11
  """
12
- from .point import Point
12
+ from __future__ import annotations
13
+ from .point import Point, P0
13
14
  from .base import Base
14
15
  from geometry import PZ
15
16
  from typing import Union, Tuple
@@ -23,7 +24,7 @@ class Quaternion(Base):
23
24
  cols=["w", "x", "y", "z"]
24
25
 
25
26
  @staticmethod
26
- def zero(count=1):
27
+ def zero(count=1) -> Quaternion:
27
28
  return Quaternion(np.tile([1,0,0,0], (count,1)))
28
29
 
29
30
  @property
@@ -31,13 +32,13 @@ class Quaternion(Base):
31
32
  return np.array([self.x, self.y, self.z, self.w]).T
32
33
 
33
34
  @property
34
- def axis(self):
35
+ def axis(self) -> Point:
35
36
  return Point(self.data[:,1:])
36
37
 
37
- def norm(self):
38
+ def norm(self) -> Quaternion:
38
39
  return self / abs(self)
39
40
 
40
- def conjugate(self):
41
+ def conjugate(self) -> Quaternion:
41
42
  return Quaternion(self.w, -self.x, -self.y, -self.z)
42
43
 
43
44
  def inverse(self):
@@ -53,7 +54,7 @@ class Quaternion(Base):
53
54
  elif isinstance(other, Number):
54
55
  return Quaternion(self.data * other)
55
56
  elif isinstance(other, np.ndarray):
56
- return Quaternions(self.data * self._dprep(other))
57
+ return Quaternion(self.data * self._dprep(other))
57
58
 
58
59
  raise TypeError(f"cant multiply a quaternion by a {other.__class__.__name__}")
59
60
 
@@ -61,7 +62,7 @@ class Quaternion(Base):
61
62
  #either it should have been picked up by the left hand object or it should commute
62
63
  return self * other
63
64
 
64
- def transform_point(self, point: Point):
65
+ def transform_point(self, point: Point) -> Point:
65
66
  '''Transform a point by the rotation described by self'''
66
67
  a, b = Base.length_check(self, point)
67
68
 
@@ -70,7 +71,7 @@ class Quaternion(Base):
70
71
  return (a * Quaternion(qdata) * a.inverse()).axis
71
72
 
72
73
  @staticmethod
73
- def from_euler(eul: Point):
74
+ def from_euler(eul: Point) -> Quaternion:
74
75
  eul = Point.type_check(eul)
75
76
  # xyz-fixed Euler angle convention: matches ArduPilot AP_Math/Quaternion::from_euler
76
77
  half = eul * 0.5
@@ -86,7 +87,7 @@ class Quaternion(Base):
86
87
  ]).T
87
88
  )
88
89
 
89
- def to_euler(self):
90
+ def to_euler(self) -> Point:
90
91
 
91
92
  # roll (x-axis rotation)
92
93
  sinr_cosp = 2 * (self.w * self.x + self.y * self.z)
@@ -111,7 +112,7 @@ class Quaternion(Base):
111
112
  return Point(roll, pitch, yaw)
112
113
 
113
114
  @staticmethod
114
- def from_axis_angle(axangles: Point):
115
+ def from_axis_angle(axangles: Point) -> Quaternion:
115
116
  small = 1e-6
116
117
  angles = abs(axangles)
117
118
 
@@ -133,27 +134,49 @@ class Quaternion(Base):
133
134
  return Quaternion(qdat)
134
135
 
135
136
  def to_axis_angle(self):
137
+ a = (-self)._to_axis_angle()
138
+ b = self._to_axis_angle()
139
+
140
+ res = a.data
141
+ res[abs(a)>abs(b), :] = -b.data[abs(a)>abs(b), :]
142
+
143
+ return Point(res)
144
+
145
+ def _to_axis_angle(self) -> Point:
136
146
  """to a point of axis angles. must be normalized first."""
137
147
  angle = 2 * np.arccos(self.w)
138
148
  s = np.sqrt(1 - self.w**2)
139
149
  np.array(s)[np.array(s) < 1e-6] = 1.0
140
- return self.axis * angle / s
141
-
150
+ with np.errstate(divide="ignore"):
151
+ sangle = angle / s
152
+ sangle[sangle==np.inf] = 0
153
+ res = self.axis * sangle
154
+ return res
142
155
 
143
156
  @staticmethod
144
- def axis_rates(q, qdot) -> Point:
157
+ def axis_rates(q: Quaternion, qdot: Quaternion) -> Point:
145
158
  wdash = qdot * q.conjugate()
146
159
  return wdash.norm().to_axis_angle()
147
160
 
148
161
  @staticmethod
149
- def body_axis_rates(q, qdot) -> Point:
162
+ def _axis_rates(q: Quaternion, qdot: Quaternion) -> Point:
163
+ wdash = qdot * q.conjugate()
164
+ return wdash.norm()._to_axis_angle()
165
+
166
+ @staticmethod
167
+ def body_axis_rates(q: Quaternion, qdot: Quaternion) -> Point:
150
168
  wdash = q.conjugate() * qdot
151
169
  return wdash.norm().to_axis_angle()
152
170
 
153
- def rotate(self, rate: Point):
171
+ @staticmethod
172
+ def _body_axis_rates(q: Quaternion, qdot: Quaternion) -> Point:
173
+ wdash = q.conjugate() * qdot
174
+ return wdash.norm()._to_axis_angle()
175
+
176
+ def rotate(self, rate: Point) -> Quaternion:
154
177
  return (Quaternion.from_axis_angle(rate) * self).norm()
155
178
 
156
- def body_rotate(self, rate: Point):
179
+ def body_rotate(self, rate: Point) -> Quaternion:
157
180
  return (self * Quaternion.from_axis_angle(rate)).norm()
158
181
 
159
182
  def diff(self, dt: np.array) -> Point:
@@ -161,7 +184,7 @@ class Quaternion(Base):
161
184
  assert len(dt) == len(self)
162
185
  dt = dt * len(dt) / (len(dt) - 1)
163
186
 
164
- ps = Quaternion.axis_rates(
187
+ ps = Quaternion._axis_rates(
165
188
  Quaternion(self.data[:-1, :]),
166
189
  Quaternion(self.data[1:, :])
167
190
  ) / dt[:-1]
@@ -172,7 +195,7 @@ class Quaternion(Base):
172
195
  assert len(dt) == len(self)
173
196
  dt = dt * len(dt) / (len(dt) - 1)
174
197
 
175
- ps = Quaternion.body_axis_rates(
198
+ ps = Quaternion._body_axis_rates(
176
199
  Quaternion(self.data[:-1, :]),
177
200
  Quaternion(self.data[1:, :])
178
201
  ) / dt[:-1]
@@ -193,7 +216,7 @@ class Quaternion(Base):
193
216
  ]).T
194
217
 
195
218
  @staticmethod
196
- def from_rotation_matrix(matrix: np.ndarray):
219
+ def from_rotation_matrix(matrix: np.ndarray) -> Quaternion:
197
220
  # This method assumes row-vector and postmultiplication of that vector
198
221
  m = matrix.conj().transpose()
199
222
  if m[2, 2] < 0:
@@ -220,12 +243,12 @@ class Quaternion(Base):
220
243
  def __str__(self):
221
244
  return "W:{w:.2f}\nX:{x:.2f}\nY:{y:.2f}\nZ:{z:.2f}".format(w=self.w, x=self.x, y=self.y, z=self.z)
222
245
 
223
- def closest_principal(self):
246
+ def closest_principal(self) -> Quaternion:
224
247
  eul = self.to_euler()
225
248
  rads = eul * (2 / np.pi)
226
249
  return Quaternion.from_euler(rads.round(0) * np.pi/2)
227
250
 
228
- def is_inverted(self):
251
+ def is_inverted(self) -> bool:
229
252
  # does the rotation reverse the Z axis?
230
253
  return np.sign(self.transform_point(PZ()).z) > 0
231
254
 
@@ -1,10 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pfc-geometry
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: A package for handling 3D geometry with a nice interface
5
5
  Home-page: https://github.com/PyFlightCoach/geometry
6
6
  Author: Thomas David
7
7
  Author-email: thomasdavid0@gmail.com
8
+ License: UNKNOWN
9
+ Platform: UNKNOWN
8
10
  Description-Content-Type: text/markdown
9
11
  License-File: LICENSE
10
12
 
@@ -22,3 +24,4 @@ Many convenience methods and constructors are available. Documentation is limite
22
24
  now available on pypi:
23
25
 
24
26
  pip install pfc-geometry
27
+
@@ -5,7 +5,7 @@ author_email = thomasdavid0@gmail.com
5
5
  description = A package for handling 3D geometry with a nice interface
6
6
  long_description = file: README.md
7
7
  long_description_content_type = text/markdown
8
- version = 0.1.2
8
+ version = 0.1.4
9
9
  url = https://github.com/PyFlightCoach/geometry
10
10
 
11
11
  [options]
@@ -35,15 +35,17 @@ def test_scale():
35
35
  assert Point(1,0,0).scale(5) == Point(5,0,0)
36
36
  assert Point(1,0,0).scale(5).data.shape == Point(5,0,0).data.shape
37
37
 
38
- assert P0().scale(5) == P0()
38
+ with np.errstate(divide="raise"):
39
+ assert Point.concatenate([PX(1,10), P0(10)]).scale(5) == Point.concatenate([PX(5,10), P0(10)])
40
+
39
41
 
40
42
  def test_unit():
41
43
  assert Point(5,0,0).unit() == Point(1,0,0)
42
44
  assert Point(5,0,0).unit().data.shape == (1,3)
43
45
 
44
46
  def test_angle_between():
45
- with raises(ValueError):
46
- Point(1,2,3).cos_angle_between(Point.zeros())
47
+ #with raises(ValueError):
48
+ Point(1,2,3).cos_angle_between(Point.zeros()) == P0()
47
49
 
48
50
  assert PX().cos_angle_between(PY()) == 0
49
51
 
@@ -59,12 +61,6 @@ def test_is_parallel():
59
61
  assert not Point(1,0, 0).is_parallel(Point(0,1, 0))
60
62
 
61
63
 
62
- @mark.skip("this is going to be picked up later with coord and Quaternion stuff")
63
- def test_rotate():
64
- assert PX().rotate(np.identity(3)) == PX()
65
-
66
- assert PX().rotate(np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]])) == Point(0, 1, 0)
67
-
68
64
  def test_scalar_projection():
69
65
  assert Point(1, 1, 0).scalar_projection(PX()) == 1
70
66
  assert P0().scalar_projection(Point(1, 1, 0)) == 0
@@ -90,3 +86,11 @@ def test_X():
90
86
 
91
87
  def test_to_rotation_matrix():
92
88
  np.testing.assert_array_equal(P0().to_rotation_matrix()[0],np.identity(3))
89
+
90
+
91
+ def test_vector_projection():
92
+ res = Point.vector_projection(PX(1,20), PY(1))
93
+ assert res == P0()
94
+
95
+ res = Point.vector_projection(PZ(20), PZ(20,20))
96
+ assert res == PZ(20)
@@ -138,7 +138,8 @@ def test_body_rotate_zero():
138
138
  qinit = Quaternion.from_euler(Point(0, 0, 0))
139
139
  qdot = qinit.body_rotate(Point(0, 0, 0))
140
140
 
141
- np.testing.assert_array_equal(list(qinit), list(qdot))
141
+ assert qinit == qdot
142
+ # np.testing.assert_array_equal(list(qinit), list(qdot))
142
143
 
143
144
 
144
145
  def test_to_from_axis_angle():
@@ -1 +0,0 @@
1
- geometry
File without changes
File without changes
@@ -1,3 +1,3 @@
1
- setuptools
2
1
  numpy
3
2
  pandas
3
+ setuptools
File without changes