pfc-geometry 0.1.1__tar.gz → 0.1.2__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.
- pfc_geometry-0.1.2/PKG-INFO +24 -0
- pfc_geometry-0.1.2/README.md +14 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/geometry/__init__.py +2 -2
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/geometry/base.py +10 -6
- pfc_geometry-0.1.2/pfc_geometry.egg-info/PKG-INFO +24 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/pfc_geometry.egg-info/SOURCES.txt +8 -3
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/setup.cfg +1 -3
- pfc_geometry-0.1.2/tests/test_base.py +216 -0
- pfc_geometry-0.1.2/tests/test_coord.py +32 -0
- pfc_geometry-0.1.2/tests/test_gps.py +53 -0
- pfc_geometry-0.1.2/tests/test_mass.py +57 -0
- pfc_geometry-0.1.2/tests/test_point.py +92 -0
- pfc_geometry-0.1.2/tests/test_quaternion.py +221 -0
- pfc_geometry-0.1.2/tests/test_transform.py +64 -0
- pfc_geometry-0.1.1/COPYING +0 -674
- pfc_geometry-0.1.1/PKG-INFO +0 -15
- pfc_geometry-0.1.1/README.md +0 -4
- pfc_geometry-0.1.1/geometry/circle.py +0 -28
- pfc_geometry-0.1.1/pfc_geometry.egg-info/PKG-INFO +0 -15
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/LICENSE +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/geometry/coordinate_frame.py +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/geometry/gps.py +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/geometry/mass.py +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/geometry/point.py +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/geometry/quaternion.py +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/geometry/testing.py +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/geometry/transformation.py +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/pfc_geometry.egg-info/dependency_links.txt +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/pfc_geometry.egg-info/requires.txt +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/pfc_geometry.egg-info/top_level.txt +0 -0
- {pfc_geometry-0.1.1 → pfc_geometry-0.1.2}/setup.py +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pfc_geometry
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A package for handling 3D geometry with a nice interface
|
|
5
|
+
Home-page: https://github.com/PyFlightCoach/geometry
|
|
6
|
+
Author: Thomas David
|
|
7
|
+
Author-email: thomasdavid0@gmail.com
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
|
|
11
|
+
# geometry #
|
|
12
|
+
|
|
13
|
+
Tools for handling 3D geometry, mostly just adds a nice interface to various geometric enterties. Each geometric entity can also be a vector of geometric entities. Each entity wraps a numpy array with the relevant number of columns labelled according to the cols class property and rows equal to the number of elements in the vector. Attribute access to each column is available and returns a numpy array.
|
|
14
|
+
|
|
15
|
+
Where operations are supported between geometric types the size of the output is inferred based on the length of the inputs. Where the two vectors of entities are of the same length, elementwise operations are performed. Where one vector is length one and the other is greater than one then the operation will be performed on every element of the longer vector.
|
|
16
|
+
|
|
17
|
+
Magic methods are used extensively and the function of operators are logical for each type. If unsure what the logical option is then check the code where it should be pretty clear.
|
|
18
|
+
|
|
19
|
+
Many convenience methods and constructors are available. Documentation is limited but if you need something it has probably already been written so check the code first.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
now available on pypi:
|
|
23
|
+
|
|
24
|
+
pip install pfc-geometry
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# geometry #
|
|
2
|
+
|
|
3
|
+
Tools for handling 3D geometry, mostly just adds a nice interface to various geometric enterties. Each geometric entity can also be a vector of geometric entities. Each entity wraps a numpy array with the relevant number of columns labelled according to the cols class property and rows equal to the number of elements in the vector. Attribute access to each column is available and returns a numpy array.
|
|
4
|
+
|
|
5
|
+
Where operations are supported between geometric types the size of the output is inferred based on the length of the inputs. Where the two vectors of entities are of the same length, elementwise operations are performed. Where one vector is length one and the other is greater than one then the operation will be performed on every element of the longer vector.
|
|
6
|
+
|
|
7
|
+
Magic methods are used extensively and the function of operators are logical for each type. If unsure what the logical option is then check the code where it should be pretty clear.
|
|
8
|
+
|
|
9
|
+
Many convenience methods and constructors are available. Documentation is limited but if you need something it has probably already been written so check the code first.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
now available on pypi:
|
|
13
|
+
|
|
14
|
+
pip install pfc-geometry
|
|
@@ -17,11 +17,11 @@ from .transformation import Transformation
|
|
|
17
17
|
from .mass import Mass
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def Euler(*args, **kwargs):
|
|
20
|
+
def Euler(*args, **kwargs) -> Quaternion:
|
|
21
21
|
return Quaternion.from_euler(Point(*args, **kwargs))
|
|
22
22
|
|
|
23
23
|
|
|
24
|
-
def Euldeg(*args, **kwargs):
|
|
24
|
+
def Euldeg(*args, **kwargs) -> Quaternion:
|
|
25
25
|
return Quaternion.from_euler(Point(*args, **kwargs).radians())
|
|
26
26
|
|
|
27
27
|
|
|
@@ -47,11 +47,11 @@ class Base:
|
|
|
47
47
|
elif "data" in kwargs:
|
|
48
48
|
args = [kwargs["data"]]
|
|
49
49
|
else:
|
|
50
|
-
raise TypeError("unknown kwargs passed")
|
|
50
|
+
raise TypeError(f"unknown kwargs passed to {self.__class__.__name__}: {args}")
|
|
51
51
|
|
|
52
52
|
if len(args)==1:
|
|
53
|
-
if isinstance(args[0], np.ndarray): #data was passed directly
|
|
54
|
-
self.data = self.__class__._clean_data(args[0])
|
|
53
|
+
if isinstance(args[0], np.ndarray) or isinstance(args[0], list): #data was passed directly
|
|
54
|
+
self.data = self.__class__._clean_data(np.array(args[0]))
|
|
55
55
|
|
|
56
56
|
elif all([isinstance(a, self.__class__) for a in args[0]]):
|
|
57
57
|
#a list of self.__class__ is passed, concatenate into one
|
|
@@ -60,7 +60,7 @@ class Base:
|
|
|
60
60
|
elif isinstance(args[0], pd.DataFrame):
|
|
61
61
|
self.data = self.__class__._clean_data(np.array(args[0]))
|
|
62
62
|
else:
|
|
63
|
-
raise TypeError("unknown
|
|
63
|
+
raise TypeError(f"unknown args passed to {self.__class__.__name__}: {args[0]}")
|
|
64
64
|
|
|
65
65
|
elif len(args) == len(self.__class__.cols):
|
|
66
66
|
#three args passed, each represents a col
|
|
@@ -80,7 +80,7 @@ class Base:
|
|
|
80
80
|
def _clean_data(cls, data) -> np.ndarray:
|
|
81
81
|
assert isinstance(data, np.ndarray)
|
|
82
82
|
if data.dtype == 'O':
|
|
83
|
-
raise TypeError('data must have homogeneous shape')
|
|
83
|
+
raise TypeError(f'data must have homogeneous shape for {cls.__name__}, given {data.shape}')
|
|
84
84
|
if len(data.shape) == 1:
|
|
85
85
|
data = data.reshape(1, len(data))
|
|
86
86
|
|
|
@@ -257,4 +257,8 @@ class Base:
|
|
|
257
257
|
return self.__class__(np.cumsum(self.data,axis=0))
|
|
258
258
|
|
|
259
259
|
def round(self, decimals=0):
|
|
260
|
-
return self.__class__(self.data.round(decimals))
|
|
260
|
+
return self.__class__(self.data.round(decimals))
|
|
261
|
+
|
|
262
|
+
def __repr__(self):
|
|
263
|
+
return f"{self.__class__.__name__}\n{self.to_pandas()}"
|
|
264
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: pfc-geometry
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A package for handling 3D geometry with a nice interface
|
|
5
|
+
Home-page: https://github.com/PyFlightCoach/geometry
|
|
6
|
+
Author: Thomas David
|
|
7
|
+
Author-email: thomasdavid0@gmail.com
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
|
|
11
|
+
# geometry #
|
|
12
|
+
|
|
13
|
+
Tools for handling 3D geometry, mostly just adds a nice interface to various geometric enterties. Each geometric entity can also be a vector of geometric entities. Each entity wraps a numpy array with the relevant number of columns labelled according to the cols class property and rows equal to the number of elements in the vector. Attribute access to each column is available and returns a numpy array.
|
|
14
|
+
|
|
15
|
+
Where operations are supported between geometric types the size of the output is inferred based on the length of the inputs. Where the two vectors of entities are of the same length, elementwise operations are performed. Where one vector is length one and the other is greater than one then the operation will be performed on every element of the longer vector.
|
|
16
|
+
|
|
17
|
+
Magic methods are used extensively and the function of operators are logical for each type. If unsure what the logical option is then check the code where it should be pretty clear.
|
|
18
|
+
|
|
19
|
+
Many convenience methods and constructors are available. Documentation is limited but if you need something it has probably already been written so check the code first.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
now available on pypi:
|
|
23
|
+
|
|
24
|
+
pip install pfc-geometry
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
COPYING
|
|
2
1
|
LICENSE
|
|
3
2
|
README.md
|
|
4
3
|
setup.cfg
|
|
5
4
|
setup.py
|
|
6
5
|
geometry/__init__.py
|
|
7
6
|
geometry/base.py
|
|
8
|
-
geometry/circle.py
|
|
9
7
|
geometry/coordinate_frame.py
|
|
10
8
|
geometry/gps.py
|
|
11
9
|
geometry/mass.py
|
|
@@ -17,4 +15,11 @@ pfc_geometry.egg-info/PKG-INFO
|
|
|
17
15
|
pfc_geometry.egg-info/SOURCES.txt
|
|
18
16
|
pfc_geometry.egg-info/dependency_links.txt
|
|
19
17
|
pfc_geometry.egg-info/requires.txt
|
|
20
|
-
pfc_geometry.egg-info/top_level.txt
|
|
18
|
+
pfc_geometry.egg-info/top_level.txt
|
|
19
|
+
tests/test_base.py
|
|
20
|
+
tests/test_coord.py
|
|
21
|
+
tests/test_gps.py
|
|
22
|
+
tests/test_mass.py
|
|
23
|
+
tests/test_point.py
|
|
24
|
+
tests/test_quaternion.py
|
|
25
|
+
tests/test_transform.py
|
|
@@ -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.
|
|
8
|
+
version = 0.1.2
|
|
9
9
|
url = https://github.com/PyFlightCoach/geometry
|
|
10
10
|
|
|
11
11
|
[options]
|
|
@@ -13,8 +13,6 @@ install_requires =
|
|
|
13
13
|
setuptools
|
|
14
14
|
numpy
|
|
15
15
|
pandas
|
|
16
|
-
packages =
|
|
17
|
-
geometry
|
|
18
16
|
|
|
19
17
|
[egg_info]
|
|
20
18
|
tag_build =
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
from geometry.base import Base
|
|
2
|
+
from pytest import mark, approx, raises
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
a_b_c = ["a", "b", "c"]
|
|
7
|
+
class ABC(Base):
|
|
8
|
+
cols=list("abc")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_init_nparray():
|
|
12
|
+
abc = ABC(np.ones((5,3)))
|
|
13
|
+
np.testing.assert_array_equal(abc.data, np.ones((5,3)))
|
|
14
|
+
|
|
15
|
+
abc = ABC(np.ones(3))
|
|
16
|
+
np.testing.assert_array_equal(abc.data, np.ones(3).reshape((1,3)))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test__dprep():
|
|
21
|
+
np.testing.assert_array_equal(
|
|
22
|
+
ABC(np.ones((10,3)))._dprep(np.ones(10)),
|
|
23
|
+
np.ones((10,3))
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
np.testing.assert_array_equal(
|
|
27
|
+
ABC(np.ones((10,3)))._dprep(np.ones((10,3))),
|
|
28
|
+
np.ones((10,3))
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
with raises(ValueError):
|
|
32
|
+
ABC(np.ones((10,3)))._dprep(np.ones((5,3)))
|
|
33
|
+
|
|
34
|
+
with raises(ValueError):
|
|
35
|
+
np.testing.assert_array_equal(
|
|
36
|
+
ABC(np.ones((10,3)))._dprep(np.ones(3)),
|
|
37
|
+
np.ones((10,3))
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
np.testing.assert_array_equal(
|
|
41
|
+
ABC(np.ones((10,3)))._dprep(np.array([1])),
|
|
42
|
+
np.ones((10,3))
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
np.testing.assert_array_equal(
|
|
46
|
+
ABC(np.ones((10,3)))._dprep(1),
|
|
47
|
+
np.ones((10,3))
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_dot():
|
|
52
|
+
a = ABC(np.random.random((10, 3)))
|
|
53
|
+
b = ABC(np.random.random((10, 3)))
|
|
54
|
+
|
|
55
|
+
c = a.dot(b)
|
|
56
|
+
|
|
57
|
+
c_check = np.array([np.dot(_a.data[0], _b.data[0]) for _a, _b in zip(a,b)])
|
|
58
|
+
|
|
59
|
+
np.testing.assert_array_almost_equal(c, c_check)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_init_values():
|
|
63
|
+
abc = ABC(1,2,3)
|
|
64
|
+
np.testing.assert_array_equal(abc.data, np.array([[1,2,3]]))
|
|
65
|
+
|
|
66
|
+
abc = ABC(*[np.ones(10) for _ in range(3)])
|
|
67
|
+
np.testing.assert_array_equal(abc.data, np.ones((10,3)))
|
|
68
|
+
|
|
69
|
+
with raises(TypeError):
|
|
70
|
+
abc = ABC([1,2,3], [1,2], [1,2,3,4])
|
|
71
|
+
|
|
72
|
+
abc = ABC([1,2], [1,2], [1,2])
|
|
73
|
+
|
|
74
|
+
np.testing.assert_array_equal(abc.data, np.array([[1,2], [1,2], [1,2]]).T)
|
|
75
|
+
|
|
76
|
+
def test_init_kwargs():
|
|
77
|
+
with raises(TypeError):
|
|
78
|
+
abc = ABC(1,b=2,c=3)
|
|
79
|
+
|
|
80
|
+
abc = ABC(a=1,b=2,c=3)
|
|
81
|
+
np.testing.assert_array_equal(abc.data, np.array([[1,2,3]]))
|
|
82
|
+
|
|
83
|
+
abc = ABC(a=[1,1],b=[1,1],c=[1,1])
|
|
84
|
+
np.testing.assert_array_equal(abc.data, np.ones((2,3)))
|
|
85
|
+
|
|
86
|
+
abc = ABC(data=np.ones((10,3)))
|
|
87
|
+
np.testing.assert_array_equal(abc.data, np.ones((10,3)))
|
|
88
|
+
|
|
89
|
+
with raises(TypeError):
|
|
90
|
+
ABC(ggg=234342)
|
|
91
|
+
|
|
92
|
+
def test_init_list_of_self():
|
|
93
|
+
abc = ABC([ABC(1,2,3), ABC(np.ones((4,3)))])
|
|
94
|
+
np.testing.assert_array_equal(abc.b, np.array([2,1,1,1,1]))
|
|
95
|
+
|
|
96
|
+
def test_init_empty():
|
|
97
|
+
with raises(TypeError):
|
|
98
|
+
ABC()
|
|
99
|
+
|
|
100
|
+
def test_init_df():
|
|
101
|
+
abc = ABC(pd.DataFrame(np.tile([1,2,3], (20,1)), columns=list("abc")))
|
|
102
|
+
assert all(abc.c == 3)
|
|
103
|
+
|
|
104
|
+
def test_attr():
|
|
105
|
+
abc = ABC(np.ones((5,3)))
|
|
106
|
+
|
|
107
|
+
np.testing.assert_array_equal(abc.a, np.ones(5))
|
|
108
|
+
np.testing.assert_array_equal(abc.b, np.ones(5))
|
|
109
|
+
np.testing.assert_array_equal(abc.c, np.ones(5))
|
|
110
|
+
|
|
111
|
+
assert dir(abc) == a_b_c
|
|
112
|
+
|
|
113
|
+
with raises(AttributeError):
|
|
114
|
+
d = abc.d
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def test_getitem():
|
|
118
|
+
abc = ABC(np.tile(np.linspace(0,4,5), (3,1)).T)
|
|
119
|
+
|
|
120
|
+
assert abc[0] == ABC(np.array([[0,0,0]]))
|
|
121
|
+
assert abc[0][0] == ABC(np.array([[0,0,0]]))
|
|
122
|
+
|
|
123
|
+
assert abc[2:][0] == ABC(np.array([[2,2,2]]))
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_eq():
|
|
128
|
+
assert ABC(np.ones((5,3))) == ABC(np.ones((5,3)))
|
|
129
|
+
|
|
130
|
+
assert not ABC(np.zeros((5,3))) == ABC(np.ones((5,3)))
|
|
131
|
+
with raises(TypeError):
|
|
132
|
+
ABC(np.zeros((5,3))) == ABC(np.zeros((6,3)))
|
|
133
|
+
|
|
134
|
+
assert ABC(1,2,4) == ABC(1.0, 2.0, 4.0)
|
|
135
|
+
|
|
136
|
+
assert ABC(np.ones((5,3))) == 1
|
|
137
|
+
|
|
138
|
+
def test_add():
|
|
139
|
+
assert ABC(1,2,3) + 1 == ABC(2,3,4)
|
|
140
|
+
assert 1 + ABC(1,2,3) == ABC(2,3,4)
|
|
141
|
+
assert ABC(1,2,3) + ABC(1,2,3) == ABC(2,4,6)
|
|
142
|
+
assert ABC(1,1,1) + np.ones(10) == ABC(np.full((10,3), 2))
|
|
143
|
+
|
|
144
|
+
def test_mul():
|
|
145
|
+
assert ABC(1,2,3) * 2 == ABC(2,4,6)
|
|
146
|
+
assert 2 * ABC(1,2,3) == ABC(2,4,6)
|
|
147
|
+
assert ABC(1,2,3) * ABC(1,2,3) == ABC(1,4,9)
|
|
148
|
+
assert ABC(2,2,2) * np.ones(10) == ABC(np.full((10,3), 2))
|
|
149
|
+
|
|
150
|
+
a = ABC(1,1,1) * np.array([2])
|
|
151
|
+
b = np.array([2]) * ABC(1,1,1)
|
|
152
|
+
assert a.data.shape == b.data.shape
|
|
153
|
+
|
|
154
|
+
assert ABC(1,1,1).tile(10) * np.ones(10) == ABC(np.ones((10,3)))
|
|
155
|
+
|
|
156
|
+
assert ABC(1,2,3) * np.full(5, 2) == ABC(2,4,6).tile(5)
|
|
157
|
+
|
|
158
|
+
assert ABC(1,2,3) * ABC(2,2,2).tile(10) == ABC(2,4, 6).tile(10)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def test_div():
|
|
162
|
+
assert ABC(1,2,3) / 2 == ABC(0.5,1,1.5)
|
|
163
|
+
assert 6 / ABC(1,2,3) == ABC(6,3,2)
|
|
164
|
+
|
|
165
|
+
assert (6 / ABC(1,2,3)).data.shape == (1,3)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_abs():
|
|
169
|
+
assert abs(ABC(1,1,1)) == np.sqrt(3)
|
|
170
|
+
assert abs(ABC(2,2,2)) == np.sqrt(12)
|
|
171
|
+
np.testing.assert_array_equal(abs(ABC(np.ones((5,3)))), np.full(5, np.sqrt(3)))
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def test_diff():
|
|
175
|
+
testarr = ABC(np.tile(np.linspace(0,100,20), (3,1)).T)
|
|
176
|
+
np.testing.assert_array_almost_equal(
|
|
177
|
+
testarr.diff(np.full(20,1)).data,
|
|
178
|
+
np.tile(np.full(20, 100/19), (3,1)).T
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def test_full():
|
|
183
|
+
full =ABC(1,2,3).tile(100)
|
|
184
|
+
|
|
185
|
+
assert len(full) == 100
|
|
186
|
+
assert full[50] == ABC(1,2,3)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
@mark.skip("not expected to work yet")
|
|
191
|
+
def test_single_col():
|
|
192
|
+
class A(Base):
|
|
193
|
+
cols = ["a"]
|
|
194
|
+
|
|
195
|
+
assert A(1).data == np.array([[1]])
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
np.testing.assert_array_equal(A([1,2,3]).data, np.array([[1],[2],[3]]))
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def test_concatenate():
|
|
202
|
+
a = ABC.full(ABC(1,2,3), 10)
|
|
203
|
+
b = ABC.full(ABC(4,5,6), 10)
|
|
204
|
+
c=ABC.concatenate([a,b])
|
|
205
|
+
|
|
206
|
+
assert len(c) == len(a) + len(b)
|
|
207
|
+
np.testing.assert_array_equal(c.a, np.concatenate([a.a, b.a], axis=0))
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_repr__():
|
|
212
|
+
p = ABC(1,2,3)
|
|
213
|
+
rpr = p.__repr__().split("\n")
|
|
214
|
+
assert rpr[0] == "ABC"
|
|
215
|
+
assert np.all(rpr[1:] == str(p.to_pandas()).split("\n"))
|
|
216
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from pytest import approx, mark
|
|
2
|
+
from geometry import Coord, Point, Quaternion, P0, PX, PY, PZ
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_axes():
|
|
8
|
+
coord = Coord(np.ones((20, 12)))
|
|
9
|
+
assert coord.origin == Point(np.ones((20,3)))
|
|
10
|
+
|
|
11
|
+
def test_from_axes():
|
|
12
|
+
coord = Coord.from_axes(P0(2), PX(1,2), PY(1,2), PZ(1,2))
|
|
13
|
+
assert coord.data[:,:3] == P0(2)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_rotation_matrix():
|
|
17
|
+
np.testing.assert_array_equal(
|
|
18
|
+
Coord.from_nothing(10).rotation_matrix(),
|
|
19
|
+
np.tile(np.identity(3).reshape(1,3,3), (10,1,1))
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_rotate():
|
|
24
|
+
q = Quaternion.from_euler(Point(0, 0, np.pi/2))
|
|
25
|
+
|
|
26
|
+
c = Coord.from_nothing(10)
|
|
27
|
+
|
|
28
|
+
rc = c.rotate(q)
|
|
29
|
+
assert rc.origin == P0()
|
|
30
|
+
np.testing.assert_almost_equal(rc.x_axis.data, PY(1,10).data)
|
|
31
|
+
np.testing.assert_almost_equal(rc.y_axis.data, PX(-1,10).data)
|
|
32
|
+
np.testing.assert_almost_equal(rc.z_axis.data, PZ(1,10).data)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from pytest import approx
|
|
2
|
+
from geometry.gps import GPS
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
def test_offset():
|
|
7
|
+
c = GPS( 52.542375, -1.631038)
|
|
8
|
+
p = GPS( 52.542264, -1.631817)
|
|
9
|
+
diff = c - p
|
|
10
|
+
c2 = p.offset(diff)
|
|
11
|
+
|
|
12
|
+
diff2 = c2 - p
|
|
13
|
+
|
|
14
|
+
np.testing.assert_array_almost_equal(diff.data, diff2.data, 1e-4)
|
|
15
|
+
|
|
16
|
+
def test_diff():
|
|
17
|
+
p0 = GPSPosition( 50.206, 4.1941755999999994)
|
|
18
|
+
p0n = GPSPosition( 50.201, 4.1941755999999994)
|
|
19
|
+
|
|
20
|
+
diff= p0 - p0n # should be south vector
|
|
21
|
+
pytest.approx(diff.y, 0)
|
|
22
|
+
assert diff.x > 0
|
|
23
|
+
|
|
24
|
+
p0e= GPSPosition( 50.206, 4.195)
|
|
25
|
+
diff= p0 - p0e # should be west vector
|
|
26
|
+
pytest.approx(diff.x, 0)
|
|
27
|
+
assert diff.y < 0
|
|
28
|
+
|
|
29
|
+
def test_diff():
|
|
30
|
+
p0 = GPS( 50.201, 4.195)
|
|
31
|
+
p0n = GPS( 50.206, 4.195) #directly north of p0
|
|
32
|
+
|
|
33
|
+
diff= p0 - p0n # should be south vector
|
|
34
|
+
assert diff.y == approx(0)
|
|
35
|
+
assert diff.x < 0
|
|
36
|
+
|
|
37
|
+
p0e= GPS( 50.201, 4.196)
|
|
38
|
+
diff= p0 - p0e # should be west vector
|
|
39
|
+
assert diff.x == approx(0)
|
|
40
|
+
assert diff.y < 0
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_sub():
|
|
46
|
+
centre = GPS( 52.542375, -1.631038)
|
|
47
|
+
pilot = GPS( 52.542264, -1.631817)
|
|
48
|
+
|
|
49
|
+
vec = centre - pilot
|
|
50
|
+
|
|
51
|
+
assert abs(vec)[0] == approx(54.167, 1e-3)
|
|
52
|
+
assert vec.x[0] == approx(12.3563, 1e-3)
|
|
53
|
+
assert vec.y[0] == approx(52.7393, 1e-3)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from geometry.mass import Mass
|
|
2
|
+
from geometry import PX
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_point():
|
|
9
|
+
p = Mass.point(1)
|
|
10
|
+
np.testing.assert_array_equal(
|
|
11
|
+
p.data,
|
|
12
|
+
np.concatenate([[1], np.zeros(9)]).reshape(1,10)
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
def test_sphere():
|
|
16
|
+
s = Mass.sphere(1, 1)
|
|
17
|
+
|
|
18
|
+
np.testing.assert_array_equal(
|
|
19
|
+
s.data,
|
|
20
|
+
np.concatenate([[1], 2/5 * np.identity(3).reshape(9)]).reshape(1,10)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def test_cuboid():
|
|
25
|
+
c = Mass.cuboid(1, 1, 2, 3)
|
|
26
|
+
|
|
27
|
+
np.testing.assert_array_almost_equal(
|
|
28
|
+
c.data,
|
|
29
|
+
np.concatenate([[1], ([
|
|
30
|
+
(2**2 + 3**2) / 12,
|
|
31
|
+
(1**2 + 3**2) / 12,
|
|
32
|
+
(1**2 + 2**2) / 12
|
|
33
|
+
] * np.identity(3)).reshape(9)]).reshape(1,10)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def test_matrix():
|
|
37
|
+
m = Mass.sphere(1, 1)
|
|
38
|
+
np.testing.assert_array_almost_equal(
|
|
39
|
+
m.matrix()[0],
|
|
40
|
+
2/5 * np.identity(3)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
mf = Mass.full(m, 10)
|
|
44
|
+
np.testing.assert_array_almost_equal(
|
|
45
|
+
mf.matrix(),
|
|
46
|
+
np.tile(2/5 * np.identity(3), (10,1,1))
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def test_offset():
|
|
50
|
+
m = Mass.point(1).offset(PX(1))
|
|
51
|
+
|
|
52
|
+
assert m.m[0] == 1
|
|
53
|
+
assert m.xx[0] == 0
|
|
54
|
+
assert m.yy[0] == 1
|
|
55
|
+
assert m.zz[0] == 1
|
|
56
|
+
|
|
57
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from geometry.point import Point, cross, PX, PY, PZ, P0, is_perpendicular
|
|
2
|
+
|
|
3
|
+
import unittest
|
|
4
|
+
from math import pi
|
|
5
|
+
from pytest import mark, approx, fixture, raises
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_init():
|
|
10
|
+
p = Point(1,2,3)
|
|
11
|
+
assert p.x == 1
|
|
12
|
+
assert p.y == 2
|
|
13
|
+
|
|
14
|
+
def test_cross():
|
|
15
|
+
a = Point(1,2,3)
|
|
16
|
+
b = Point(1,2,3)
|
|
17
|
+
|
|
18
|
+
c = a.cross(b)
|
|
19
|
+
|
|
20
|
+
np.testing.assert_almost_equal(c.data[0,:], np.cross(a.data[0,:], b.data[0,:]))
|
|
21
|
+
assert a.cross(b) == cross(a, b)
|
|
22
|
+
|
|
23
|
+
def test_cross2():
|
|
24
|
+
a = Point(np.random.random((10, 3)))
|
|
25
|
+
b = Point(np.random.random((10, 3)))
|
|
26
|
+
|
|
27
|
+
c = a.cross(b)
|
|
28
|
+
|
|
29
|
+
c_check = np.array([np.cross(_a.data[0], _b.data[0]) for _a, _b in zip(a,b)])
|
|
30
|
+
|
|
31
|
+
np.testing.assert_array_almost_equal(c.data, c_check)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_scale():
|
|
35
|
+
assert Point(1,0,0).scale(5) == Point(5,0,0)
|
|
36
|
+
assert Point(1,0,0).scale(5).data.shape == Point(5,0,0).data.shape
|
|
37
|
+
|
|
38
|
+
assert P0().scale(5) == P0()
|
|
39
|
+
|
|
40
|
+
def test_unit():
|
|
41
|
+
assert Point(5,0,0).unit() == Point(1,0,0)
|
|
42
|
+
assert Point(5,0,0).unit().data.shape == (1,3)
|
|
43
|
+
|
|
44
|
+
def test_angle_between():
|
|
45
|
+
with raises(ValueError):
|
|
46
|
+
Point(1,2,3).cos_angle_between(Point.zeros())
|
|
47
|
+
|
|
48
|
+
assert PX().cos_angle_between(PY()) == 0
|
|
49
|
+
|
|
50
|
+
assert PX().angle_between(PY()) == np.pi / 2
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
assert PX(-1).angle_between(Point(1,1,0)) == 3 * np.pi / 4
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_is_parallel():
|
|
57
|
+
assert not Point(1,0, 0).is_parallel(Point(-1,0, 0))
|
|
58
|
+
assert Point(1,0, 0).is_parallel(Point(1,0, 0))
|
|
59
|
+
assert not Point(1,0, 0).is_parallel(Point(0,1, 0))
|
|
60
|
+
|
|
61
|
+
|
|
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
|
+
def test_scalar_projection():
|
|
69
|
+
assert Point(1, 1, 0).scalar_projection(PX()) == 1
|
|
70
|
+
assert P0().scalar_projection(Point(1, 1, 0)) == 0
|
|
71
|
+
assert Point(1, 1, 0).scalar_projection(P0()) == 0
|
|
72
|
+
|
|
73
|
+
def test_is_perpendicular():
|
|
74
|
+
assert is_perpendicular(Point(1, 0, 0), Point(0, 1, 0))
|
|
75
|
+
assert is_perpendicular(Point(1, 0, 0), Point(0, 0, 1))
|
|
76
|
+
assert is_perpendicular(Point(0, 1, 0), Point(1, 0, 0))
|
|
77
|
+
assert not is_perpendicular(Point(1, 0, 0), Point(1, 1, 0))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_full():
|
|
81
|
+
points = Point.full(Point(1,2,3), 100)
|
|
82
|
+
assert len(points) == 100
|
|
83
|
+
assert isinstance(points, Point)
|
|
84
|
+
assert points[20] == Point(1, 2, 3)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_X():
|
|
88
|
+
assert Point.X(1,100) + Point.Y(1,100) + Point.Z(1,100) == Point(np.ones((100,3)))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_to_rotation_matrix():
|
|
92
|
+
np.testing.assert_array_equal(P0().to_rotation_matrix()[0],np.identity(3))
|