patme 0.4.4__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.
Potentially problematic release.
This version of patme might be problematic. Click here for more details.
- patme/__init__.py +52 -0
- patme/buildtools/__init__.py +7 -0
- patme/buildtools/rce_releasecreator.py +336 -0
- patme/buildtools/release.py +26 -0
- patme/femtools/__init__.py +5 -0
- patme/femtools/abqmsgfilechecker.py +137 -0
- patme/femtools/fecall.py +1092 -0
- patme/geometry/__init__.py +0 -0
- patme/geometry/area.py +124 -0
- patme/geometry/coordinatesystem.py +635 -0
- patme/geometry/intersect.py +284 -0
- patme/geometry/line.py +183 -0
- patme/geometry/misc.py +420 -0
- patme/geometry/plane.py +464 -0
- patme/geometry/rotate.py +244 -0
- patme/geometry/scale.py +152 -0
- patme/geometry/shape2d.py +50 -0
- patme/geometry/transformations.py +1831 -0
- patme/geometry/translate.py +139 -0
- patme/mechanics/__init__.py +4 -0
- patme/mechanics/loads.py +435 -0
- patme/mechanics/material.py +1260 -0
- patme/service/__init__.py +7 -0
- patme/service/decorators.py +85 -0
- patme/service/duration.py +96 -0
- patme/service/exceptionhook.py +104 -0
- patme/service/exceptions.py +36 -0
- patme/service/io/__init__.py +3 -0
- patme/service/io/basewriter.py +122 -0
- patme/service/logger.py +375 -0
- patme/service/mathutils.py +108 -0
- patme/service/misc.py +71 -0
- patme/service/moveimports.py +217 -0
- patme/service/stringutils.py +419 -0
- patme/service/systemutils.py +290 -0
- patme/sshtools/__init__.py +3 -0
- patme/sshtools/cara.py +435 -0
- patme/sshtools/clustercaller.py +420 -0
- patme/sshtools/facluster.py +350 -0
- patme/sshtools/sshcall.py +168 -0
- patme-0.4.4.dist-info/LICENSE +21 -0
- patme-0.4.4.dist-info/LICENSES/MIT.txt +9 -0
- patme-0.4.4.dist-info/METADATA +168 -0
- patme-0.4.4.dist-info/RECORD +46 -0
- patme-0.4.4.dist-info/WHEEL +4 -0
- patme-0.4.4.dist-info/entry_points.txt +3 -0
patme/geometry/rotate.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Copyright (C) 2020 Deutsches Zentrum fuer Luft- und Raumfahrt(DLR, German Aerospace Center) <www.dlr.de>
|
|
2
|
+
# SPDX-FileCopyrightText: 2022 German Aerospace Center (DLR)
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
"""
|
|
6
|
+
documentation
|
|
7
|
+
"""
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from patme import epsilon
|
|
11
|
+
from patme.geometry.transformations import euler_from_matrix, euler_matrix
|
|
12
|
+
from patme.geometry.translate import Translation
|
|
13
|
+
from patme.service.exceptions import GeometryError, ImproperParameterError
|
|
14
|
+
from patme.service.logger import log
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Rotation(np.ndarray):
|
|
18
|
+
"""
|
|
19
|
+
This class describes a rotation in 3D space which is represented by a 3x3 matrix.
|
|
20
|
+
|
|
21
|
+
With this class rotations in 3D space can be created and operations may be performed.
|
|
22
|
+
For that purpose the class inherits from np.ndarray which is a class for storing
|
|
23
|
+
matrix values and performing operations on them. For further details on np.ndarray
|
|
24
|
+
please refer to the numpy/scipy documentation.
|
|
25
|
+
|
|
26
|
+
Rotations are represented by a 3x3 symmetirc rotation matrix.
|
|
27
|
+
|
|
28
|
+
Usage::
|
|
29
|
+
|
|
30
|
+
>>> import numpy as np
|
|
31
|
+
>>> from patme.geometry.rotate import Rotation
|
|
32
|
+
>>> # creating identity rotation
|
|
33
|
+
>>> r=Rotation()
|
|
34
|
+
>>> r
|
|
35
|
+
Rotation([[1., 0., 0.],
|
|
36
|
+
[0., 1., 0.],
|
|
37
|
+
[0., 0., 1.]])
|
|
38
|
+
>>> # creating empty rotation objects for demonstration below
|
|
39
|
+
>>> rx=r.copy()
|
|
40
|
+
>>> ry=r.copy()
|
|
41
|
+
>>> rxy=r.copy()
|
|
42
|
+
>>> # creating rotation by defining angles in radians!
|
|
43
|
+
>>> # by default the rotation is performed in a static reference frame in the
|
|
44
|
+
>>> # order x,y,z. Those 24 rotationstypes may be used: 'sxyz', 'sxyx', 'sxzy',
|
|
45
|
+
>>> # 'sxzx', 'syzx', 'syzy', 'syxz', 'syxy', 'szxy', 'szxz', 'szyx', 'szyz',
|
|
46
|
+
>>> # 'rzyx', 'rxyx', 'ryzx', 'rxzx', 'rxzy', 'ryzy', 'rzxy', 'ryxy', 'ryxz',
|
|
47
|
+
>>> # 'rzxz', 'rxyz'(default), 'rzyz'.
|
|
48
|
+
>>> # The first item represents a static or rotating reference frame respectively.
|
|
49
|
+
>>> # Static reference frame means that also the rotation around the second and thrid
|
|
50
|
+
>>> # angle is performed in reference to the initial rotation. Rotating reference frame
|
|
51
|
+
>>> # means that rotation around the second angle is done in respect to the reference
|
|
52
|
+
>>> # frame that is created by the rotation around the first angle; respectively
|
|
53
|
+
>>> # for the third angle.
|
|
54
|
+
>>> # Additionally a rotation 'sxyz' is equal to 'rzyx' and so on.
|
|
55
|
+
>>> #
|
|
56
|
+
>>> # rotating by 90 degrees by x-coordinate
|
|
57
|
+
>>> rx.angles = [np.pi/2,0,0] # or
|
|
58
|
+
>>> rx.angles = [np.pi/2,0,0,'rxyz']
|
|
59
|
+
>>> rx.round()
|
|
60
|
+
Rotation([[ 1., 0., -0.],
|
|
61
|
+
[-0., 0., -1.],
|
|
62
|
+
[ 0., 1., 0.]])
|
|
63
|
+
>>> # rotating by 90 degrees around x and subsequently by 90 degrees around y
|
|
64
|
+
>>> # with a rotating reference frame
|
|
65
|
+
>>> rxy.angles = [np.pi/2,np.pi/2,0,'rxyz']
|
|
66
|
+
>>> rxy
|
|
67
|
+
Rotation([[ 6.12323400e-17, 0.00000000e+00, 1.00000000e+00],
|
|
68
|
+
[ 1.00000000e+00, 6.12323400e-17, -6.12323400e-17],
|
|
69
|
+
[-6.12323400e-17, 1.00000000e+00, 3.74939946e-33]])
|
|
70
|
+
|
|
71
|
+
>>> # multiplying rotations
|
|
72
|
+
>>> ry.angles = [0,np.pi/2,0,'rxyz']
|
|
73
|
+
>>> ry
|
|
74
|
+
Rotation([[ 6.123234e-17, 0.000000e+00, 1.000000e+00],
|
|
75
|
+
[-0.000000e+00, 1.000000e+00, 0.000000e+00],
|
|
76
|
+
[-1.000000e+00, -0.000000e+00, 6.123234e-17]])
|
|
77
|
+
>>> rx * ry
|
|
78
|
+
Rotation([[ 6.12323400e-17, 0.00000000e+00, 1.00000000e+00],
|
|
79
|
+
[ 1.00000000e+00, 6.12323400e-17, -6.12323400e-17],
|
|
80
|
+
[-6.12323400e-17, 1.00000000e+00, 3.74939946e-33]])
|
|
81
|
+
|
|
82
|
+
>>> # rotating points
|
|
83
|
+
>>> from patme.geometry.translate import Translation
|
|
84
|
+
>>> t = Translation([1,2,0])
|
|
85
|
+
>>> # rotating around x
|
|
86
|
+
>>> rx * t
|
|
87
|
+
Translation([1., 0., 2.])
|
|
88
|
+
>>> # rotating around x and then y with rotating reference frame
|
|
89
|
+
>>> rxy * t
|
|
90
|
+
Translation([0., 1., 2.])
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
__hash__ = object.__hash__
|
|
94
|
+
"""hash reimplementation due to definition of __eq__. See __hash__ doc for more details.
|
|
95
|
+
https://docs.python.org/3.4/reference/datamodel.html#object.__hash__"""
|
|
96
|
+
|
|
97
|
+
def __new__(cls, input_array=None):
|
|
98
|
+
"""constructing np.ndarray instance. For more information see
|
|
99
|
+
http://docs.scipy.org/doc/numpy/user/basics.subclassing.html#basics-subclassing"""
|
|
100
|
+
# Input array is an already formed ndarray instance
|
|
101
|
+
# We first cast to be our class type
|
|
102
|
+
if input_array is None:
|
|
103
|
+
input_array = np.identity(3, dtype=np.float64)
|
|
104
|
+
if not hasattr(input_array, "shape"):
|
|
105
|
+
input_array = np.asarray(input_array)
|
|
106
|
+
if not input_array.shape == (3, 3):
|
|
107
|
+
raise ImproperParameterError(
|
|
108
|
+
"The given rotation must be a 3x3 matrix like object. Got this instead: " + str(input_array)
|
|
109
|
+
)
|
|
110
|
+
obj = input_array.view(cls)
|
|
111
|
+
# add the new attribute to the created instance
|
|
112
|
+
# Finally, we must return the newly created object:
|
|
113
|
+
return obj
|
|
114
|
+
|
|
115
|
+
def __array_finalize__(self, obj):
|
|
116
|
+
"""constructing np.ndarray instance. For more information see
|
|
117
|
+
http://docs.scipy.org/doc/numpy/user/basics.subclassing.html#basics-subclassing"""
|
|
118
|
+
# see InfoArray.__array_finalize__ for comments
|
|
119
|
+
if obj is None:
|
|
120
|
+
return
|
|
121
|
+
|
|
122
|
+
def _isIdentity(self):
|
|
123
|
+
"""doc"""
|
|
124
|
+
return np.allclose(self, np.identity(3))
|
|
125
|
+
|
|
126
|
+
def getInverse(self):
|
|
127
|
+
""":return: returns a copy of the inverted array"""
|
|
128
|
+
return np.linalg.inv(self)
|
|
129
|
+
|
|
130
|
+
def setRotationByPoints(self, origin, xCoordinate, xyPlaneCoordinate):
|
|
131
|
+
"""doc"""
|
|
132
|
+
if not isinstance(origin, Translation):
|
|
133
|
+
origin = Translation(origin)
|
|
134
|
+
if not isinstance(xCoordinate, Translation):
|
|
135
|
+
xCoordinate = Translation(xCoordinate)
|
|
136
|
+
if not isinstance(xyPlaneCoordinate, Translation):
|
|
137
|
+
xyPlaneCoordinate = Translation(xyPlaneCoordinate)
|
|
138
|
+
|
|
139
|
+
xAxisLocal = xCoordinate - origin
|
|
140
|
+
yxAxisLocal = xyPlaneCoordinate - origin
|
|
141
|
+
if xCoordinate.distance(origin) < epsilon or xyPlaneCoordinate.distance(origin) < epsilon:
|
|
142
|
+
raise GeometryError("Given points are not distinct from each other.")
|
|
143
|
+
|
|
144
|
+
xAxisLocal = xAxisLocal / np.linalg.norm(xAxisLocal, 2)
|
|
145
|
+
zAxisLocal = np.cross(xAxisLocal, yxAxisLocal)
|
|
146
|
+
# check for linear dependency
|
|
147
|
+
if np.linalg.norm(zAxisLocal, 2) < epsilon:
|
|
148
|
+
raise ImproperParameterError("given vectors are linear dependent")
|
|
149
|
+
zAxisLocal = zAxisLocal / np.linalg.norm(zAxisLocal, 2)
|
|
150
|
+
|
|
151
|
+
yAxisLocal = np.cross(xAxisLocal, zAxisLocal) * -1
|
|
152
|
+
yAxisLocal = yAxisLocal / np.linalg.norm(yAxisLocal, 2)
|
|
153
|
+
|
|
154
|
+
self[:, 0] = xAxisLocal
|
|
155
|
+
self[:, 1] = yAxisLocal
|
|
156
|
+
self[:, 2] = zAxisLocal
|
|
157
|
+
|
|
158
|
+
def getPointsByRotation(self, rotation, origin):
|
|
159
|
+
"""doc"""
|
|
160
|
+
if not isinstance(origin, Translation):
|
|
161
|
+
origin = Translation(origin)
|
|
162
|
+
|
|
163
|
+
xAxisLocal = Translation(rotation[:, 0])
|
|
164
|
+
yAxisLocal = Translation(rotation[:, 1])
|
|
165
|
+
yCoordinate = yAxisLocal + origin
|
|
166
|
+
xCoordinate = xAxisLocal + origin
|
|
167
|
+
|
|
168
|
+
return [xCoordinate, yCoordinate]
|
|
169
|
+
|
|
170
|
+
def __invert__(self):
|
|
171
|
+
"""doc"""
|
|
172
|
+
self[:3, :3] = self.getInverse()
|
|
173
|
+
|
|
174
|
+
def __mul__(self, other):
|
|
175
|
+
"""doc"""
|
|
176
|
+
try:
|
|
177
|
+
ret = np.dot(self, other)
|
|
178
|
+
if ret.shape == (3,):
|
|
179
|
+
cls = other.__class__
|
|
180
|
+
if cls is np.ndarray:
|
|
181
|
+
cls = np.array
|
|
182
|
+
return cls(ret)
|
|
183
|
+
else:
|
|
184
|
+
return ret
|
|
185
|
+
except ValueError:
|
|
186
|
+
# possibly multiplication with coordinatesystem
|
|
187
|
+
return np.dot(self, other[:3, :3])
|
|
188
|
+
|
|
189
|
+
def __imul__(self, other):
|
|
190
|
+
"""doc"""
|
|
191
|
+
if other.shape == (3,):
|
|
192
|
+
raise GeometryError("Operator *= cannot be used with (rotation, translation) operands!")
|
|
193
|
+
self = self * other
|
|
194
|
+
|
|
195
|
+
def __eq__(self, other):
|
|
196
|
+
"""doc"""
|
|
197
|
+
try:
|
|
198
|
+
return np.allclose(self, other)
|
|
199
|
+
except TypeError:
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
def _setAngles(self, angles):
|
|
203
|
+
"""
|
|
204
|
+
:param angles: iterable with at least 3 items: (x,y,z,[rotationtype])
|
|
205
|
+
- the input angles have to be in radians"""
|
|
206
|
+
if len(angles) == 4:
|
|
207
|
+
alpha, beta, gamma, rotationType = angles[:4]
|
|
208
|
+
elif len(angles) == 3:
|
|
209
|
+
alpha, beta, gamma = angles[:3]
|
|
210
|
+
rotationType = "rxyz"
|
|
211
|
+
else:
|
|
212
|
+
log.error("There must be 3 or 4 scalar parameters! Using identity rotation instead")
|
|
213
|
+
alpha, beta, gamma, rotationType = 0, 0, 0, "rxyz"
|
|
214
|
+
|
|
215
|
+
if np.any(np.array([alpha, beta, gamma]) > np.pi * 2):
|
|
216
|
+
log.warning(
|
|
217
|
+
"Rotation object will set angle that is bigger than 2*pi. Maybe the angles are not given in radians!"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
self.setfield(euler_matrix(alpha, beta, gamma, rotationType)[:3, :3], np.float64)
|
|
221
|
+
|
|
222
|
+
return self
|
|
223
|
+
|
|
224
|
+
def _getAngles(self):
|
|
225
|
+
"""returns the 'rxyz' Euler angles"""
|
|
226
|
+
return self.getAngles("rxyz")
|
|
227
|
+
|
|
228
|
+
def getAngles(self, axis):
|
|
229
|
+
"""returns the Euler angles depending on the desired axis. See class description
|
|
230
|
+
for details about the axis parameter."""
|
|
231
|
+
return euler_from_matrix(self, axis)
|
|
232
|
+
|
|
233
|
+
angles = property(fset=_setAngles, fget=_getAngles)
|
|
234
|
+
|
|
235
|
+
isIdentity = property(fget=_isIdentity)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
if __name__ == "__main__":
|
|
239
|
+
import doctest
|
|
240
|
+
|
|
241
|
+
doctest.testmod()
|
|
242
|
+
# alpha, beta, gamma, rotationType = np.pi*0.25, 0, np.pi*0.5, "rxyz"
|
|
243
|
+
# myArr = trafo.euler_matrix(alpha, beta, gamma, rotationType)[:3, :3]
|
|
244
|
+
# print()
|
patme/geometry/scale.py
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Copyright (C) 2020 Deutsches Zentrum fuer Luft- und Raumfahrt(DLR, German Aerospace Center) <www.dlr.de>
|
|
2
|
+
# SPDX-FileCopyrightText: 2022 German Aerospace Center (DLR)
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
"""
|
|
6
|
+
documentation
|
|
7
|
+
"""
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from patme.service.exceptions import GeometryError, ImproperParameterError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Scaling(np.ndarray):
|
|
14
|
+
"""
|
|
15
|
+
This class describes a scaling in 3D space which is represented by a 3x3 matrix.
|
|
16
|
+
|
|
17
|
+
With this class scalings in 3D space can be created and operations may be performed.
|
|
18
|
+
For that purpose the class inherits from np.ndarray which is a class for storing
|
|
19
|
+
matrix values and performing operations on them. For further details on np.ndarray
|
|
20
|
+
please refer to the numpy/scipy documentation.
|
|
21
|
+
|
|
22
|
+
Scalings are represented by a 3x3 diagonal scaling matrix.
|
|
23
|
+
|
|
24
|
+
Usage::
|
|
25
|
+
|
|
26
|
+
>>> import numpy as np
|
|
27
|
+
>>> from patme.geometry.scale import Scaling
|
|
28
|
+
>>> # creating identity Scaling
|
|
29
|
+
>>> s=Scaling()
|
|
30
|
+
>>> s
|
|
31
|
+
Scaling([[1., 0., 0.],
|
|
32
|
+
[0., 1., 0.],
|
|
33
|
+
[0., 0., 1.]])
|
|
34
|
+
>>> # creating empty scaling objects for demonstration below
|
|
35
|
+
>>> sx=s.copy()
|
|
36
|
+
>>> sxy=s.copy()
|
|
37
|
+
>>> # creating scaling by defining scaling factors!
|
|
38
|
+
>>> # scaling by 2.0 by x-coordinate
|
|
39
|
+
>>> sx.factors = (2.0,1.0,1.0)
|
|
40
|
+
>>> sx
|
|
41
|
+
Scaling([[2., 0., 0.],
|
|
42
|
+
[0., 1., 0.],
|
|
43
|
+
[0., 0., 1.]])
|
|
44
|
+
>>> # scaling by 2.0 by x-, y-coordinate and z-coordinate
|
|
45
|
+
>>> sxyz=s.copy()
|
|
46
|
+
>>> sxyz.factors = (2.0,2.0,2.0)
|
|
47
|
+
>>> sxyz
|
|
48
|
+
Scaling([[2., 0., 0.],
|
|
49
|
+
[0., 2., 0.],
|
|
50
|
+
[0., 0., 2.]])
|
|
51
|
+
|
|
52
|
+
>>> # multiplying scalings
|
|
53
|
+
>>> sx * sxyz
|
|
54
|
+
Scaling([[4., 0., 0.],
|
|
55
|
+
[0., 2., 0.],
|
|
56
|
+
[0., 0., 2.]])
|
|
57
|
+
|
|
58
|
+
>>> # scaling points
|
|
59
|
+
>>> from patme.geometry.translate import Translation
|
|
60
|
+
>>> t = Translation([1,2,0])
|
|
61
|
+
>>> # scaling in x
|
|
62
|
+
>>> sx * t
|
|
63
|
+
Translation([2., 2., 0.])
|
|
64
|
+
>>> # scaling in all directions
|
|
65
|
+
>>> sxyz * t
|
|
66
|
+
Translation([2., 4., 0.])
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
__hash__ = object.__hash__
|
|
70
|
+
"""hash reimplementation due to definition of __eq__. See __hash__ doc for more details.
|
|
71
|
+
https://docs.python.org/3.4/reference/datamodel.html#object.__hash__"""
|
|
72
|
+
|
|
73
|
+
def __new__(cls, input_array=None):
|
|
74
|
+
"""constructing np.ndarray instance. For more information see
|
|
75
|
+
http://docs.scipy.org/doc/numpy/user/basics.subclassing.html#basics-subclassing"""
|
|
76
|
+
# Input array is an already formed ndarray instance
|
|
77
|
+
# We first cast to be our class type
|
|
78
|
+
if input_array is None:
|
|
79
|
+
input_array = np.identity(3, dtype=np.float64)
|
|
80
|
+
if not hasattr(input_array, "shape"):
|
|
81
|
+
input_array = np.asarray(input_array)
|
|
82
|
+
if not input_array.shape == (3, 3):
|
|
83
|
+
raise ImproperParameterError(
|
|
84
|
+
"The given scaling must be a 3x3 matrix like object. Got this instead: " + str(input_array)
|
|
85
|
+
)
|
|
86
|
+
obj = input_array.view(cls)
|
|
87
|
+
# add the new attribute to the created instance
|
|
88
|
+
# Finally, we must return the newly created object:
|
|
89
|
+
return obj
|
|
90
|
+
|
|
91
|
+
def __array_finalize__(self, obj):
|
|
92
|
+
"""constructing np.ndarray instance. For more information see
|
|
93
|
+
http://docs.scipy.org/doc/numpy/user/basics.subclassing.html#basics-subclassing"""
|
|
94
|
+
# see InfoArray.__array_finalize__ for comments
|
|
95
|
+
if obj is None:
|
|
96
|
+
return
|
|
97
|
+
"""ID of the Point within the FEM"""
|
|
98
|
+
|
|
99
|
+
def isIdentity(self):
|
|
100
|
+
"""doc"""
|
|
101
|
+
return np.allclose(self, np.identity(3, dtype=np.float64))
|
|
102
|
+
|
|
103
|
+
def getInverse(self):
|
|
104
|
+
""":return: returns a copy of the inverted array"""
|
|
105
|
+
return np.linalg.inv(self)
|
|
106
|
+
|
|
107
|
+
def __invert__(self):
|
|
108
|
+
"""doc"""
|
|
109
|
+
self[:3, :3] = self.getInverse()
|
|
110
|
+
|
|
111
|
+
def __mul__(self, other):
|
|
112
|
+
"""doc"""
|
|
113
|
+
try:
|
|
114
|
+
ret = np.dot(self, other)
|
|
115
|
+
if ret.shape == (3,):
|
|
116
|
+
cls = other.__class__
|
|
117
|
+
if cls is np.ndarray:
|
|
118
|
+
cls = np.array
|
|
119
|
+
return cls(ret)
|
|
120
|
+
return ret
|
|
121
|
+
except ValueError: # possibly multiplication with coordinatesystem
|
|
122
|
+
return np.dot(self, other[:3, :3])
|
|
123
|
+
|
|
124
|
+
def __imul__(self, other):
|
|
125
|
+
"""doc"""
|
|
126
|
+
if other.shape == (3,):
|
|
127
|
+
raise GeometryError("Operator *= cannot be used with (rotation, translation) operands!")
|
|
128
|
+
self = self * other
|
|
129
|
+
|
|
130
|
+
def __eq__(self, other):
|
|
131
|
+
"""doc"""
|
|
132
|
+
try:
|
|
133
|
+
return np.allclose(self, other)
|
|
134
|
+
except TypeError:
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
def _getScaling(self):
|
|
138
|
+
"""doc"""
|
|
139
|
+
return tuple(np.diag(self[:3, :3]))
|
|
140
|
+
|
|
141
|
+
def _setScaling(self, factors):
|
|
142
|
+
"""doc"""
|
|
143
|
+
self.setfield(np.diag(factors[:3]), np.float64)
|
|
144
|
+
return self
|
|
145
|
+
|
|
146
|
+
factors = property(fget=_getScaling, fset=_setScaling)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
if __name__ == "__main__":
|
|
150
|
+
import doctest
|
|
151
|
+
|
|
152
|
+
doctest.testmod()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Copyright (C) 2020 Deutsches Zentrum fuer Luft- und Raumfahrt(DLR, German Aerospace Center) <www.dlr.de>
|
|
2
|
+
# SPDX-FileCopyrightText: 2022 German Aerospace Center (DLR)
|
|
3
|
+
#
|
|
4
|
+
# SPDX-License-Identifier: MIT
|
|
5
|
+
"""methods for 2d shapes"""
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from patme import epsilon
|
|
10
|
+
from patme.geometry.rotate import Rotation
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def getSuperEllipseCoords(
|
|
14
|
+
halfAxisHoriz, halfAxisVert, ellipseParamHoriz, ellipseParamVert, ellipseRotationAngle, samplePointsEllipse
|
|
15
|
+
):
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
:param halfAxisHoriz: horizontal half axis
|
|
19
|
+
:param halfAxisVert: vertical half axis
|
|
20
|
+
:param ellipseParamHoriz: horizontal ellipse power parameter
|
|
21
|
+
:param ellipseParamVert: vertical ellipse power parameter
|
|
22
|
+
:param ellipseRotationAngle: rotation in deg of the ellipse
|
|
23
|
+
:param samplePointsEllipse: number of sample points of the ellipse
|
|
24
|
+
:return: np array of shape (samplePointsEllipse, 2) with ellipse coords
|
|
25
|
+
"""
|
|
26
|
+
ellipse = np.ones((samplePointsEllipse, 3))
|
|
27
|
+
|
|
28
|
+
angles = np.linspace(0, 2 * np.pi, samplePointsEllipse, endpoint=False)
|
|
29
|
+
|
|
30
|
+
ellipse[:, 0] = _calcPowerForEllipse(np.cos, halfAxisHoriz, angles, ellipseParamHoriz)
|
|
31
|
+
ellipse[:, 1] = _calcPowerForEllipse(np.sin, halfAxisVert, angles, ellipseParamVert)
|
|
32
|
+
|
|
33
|
+
# rotate ellipse
|
|
34
|
+
if abs(ellipseRotationAngle) > epsilon:
|
|
35
|
+
rot = Rotation()
|
|
36
|
+
rot.angles = (0, 0, np.deg2rad(ellipseRotationAngle))
|
|
37
|
+
ellipse = np.dot(rot, ellipse.T).T
|
|
38
|
+
ellipse = ellipse[:, :2]
|
|
39
|
+
|
|
40
|
+
return ellipse
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _calcPowerForEllipse(sinOrCosFunc, halfAxis, angles, ellipseParameter):
|
|
44
|
+
"""The power calculation for negative values does not work for float exponents. Use a workaround for it."""
|
|
45
|
+
sinOrCos = sinOrCosFunc(angles)
|
|
46
|
+
sinOrCosIsNeg = sinOrCos < 0.0
|
|
47
|
+
sinOrCos[sinOrCosIsNeg] *= -1
|
|
48
|
+
ellipse = halfAxis * np.power(sinOrCos, 2.0 / ellipseParameter)
|
|
49
|
+
ellipse[sinOrCosIsNeg] *= -1
|
|
50
|
+
return ellipse
|