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.

Files changed (46) hide show
  1. patme/__init__.py +52 -0
  2. patme/buildtools/__init__.py +7 -0
  3. patme/buildtools/rce_releasecreator.py +336 -0
  4. patme/buildtools/release.py +26 -0
  5. patme/femtools/__init__.py +5 -0
  6. patme/femtools/abqmsgfilechecker.py +137 -0
  7. patme/femtools/fecall.py +1092 -0
  8. patme/geometry/__init__.py +0 -0
  9. patme/geometry/area.py +124 -0
  10. patme/geometry/coordinatesystem.py +635 -0
  11. patme/geometry/intersect.py +284 -0
  12. patme/geometry/line.py +183 -0
  13. patme/geometry/misc.py +420 -0
  14. patme/geometry/plane.py +464 -0
  15. patme/geometry/rotate.py +244 -0
  16. patme/geometry/scale.py +152 -0
  17. patme/geometry/shape2d.py +50 -0
  18. patme/geometry/transformations.py +1831 -0
  19. patme/geometry/translate.py +139 -0
  20. patme/mechanics/__init__.py +4 -0
  21. patme/mechanics/loads.py +435 -0
  22. patme/mechanics/material.py +1260 -0
  23. patme/service/__init__.py +7 -0
  24. patme/service/decorators.py +85 -0
  25. patme/service/duration.py +96 -0
  26. patme/service/exceptionhook.py +104 -0
  27. patme/service/exceptions.py +36 -0
  28. patme/service/io/__init__.py +3 -0
  29. patme/service/io/basewriter.py +122 -0
  30. patme/service/logger.py +375 -0
  31. patme/service/mathutils.py +108 -0
  32. patme/service/misc.py +71 -0
  33. patme/service/moveimports.py +217 -0
  34. patme/service/stringutils.py +419 -0
  35. patme/service/systemutils.py +290 -0
  36. patme/sshtools/__init__.py +3 -0
  37. patme/sshtools/cara.py +435 -0
  38. patme/sshtools/clustercaller.py +420 -0
  39. patme/sshtools/facluster.py +350 -0
  40. patme/sshtools/sshcall.py +168 -0
  41. patme-0.4.4.dist-info/LICENSE +21 -0
  42. patme-0.4.4.dist-info/LICENSES/MIT.txt +9 -0
  43. patme-0.4.4.dist-info/METADATA +168 -0
  44. patme-0.4.4.dist-info/RECORD +46 -0
  45. patme-0.4.4.dist-info/WHEEL +4 -0
  46. patme-0.4.4.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,635 @@
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
+ """
7
+ The following conditions and Copyright is related to two functionalities of Transformation:
8
+ Transformation.setRotationByAxisAndAngle()
9
+ Transformation.estimateTransformationFromPointSets()
10
+
11
+ For detailed information please refer to '<http://www.lfd.uci.edu/~gohlke/code/transformations.py.html>'_ (23.01.2014).
12
+
13
+ References
14
+ ----------
15
+ (1) Matrices and transformations. Ronald Goldman.
16
+ In "Graphics Gems I", pp 472-475. Morgan Kaufmann, 1990.
17
+ (2) More matrices and transformations: shear and pseudo-perspective.
18
+ Ronald Goldman. In "Graphics Gems II", pp 320-323. Morgan Kaufmann, 1991.
19
+ (3) Decomposing a matrix into simple transformations. Spencer Thomas.
20
+ In "Graphics Gems II", pp 320-323. Morgan Kaufmann, 1991.
21
+ (4) Recovering the data from the transformation matrix. Ronald Goldman.
22
+ In "Graphics Gems II", pp 324-331. Morgan Kaufmann, 1991.
23
+ (5) Euler angle conversion. Ken Shoemake.
24
+ In "Graphics Gems IV", pp 222-229. Morgan Kaufmann, 1994.
25
+ (6) Arcball rotation control. Ken Shoemake.
26
+ In "Graphics Gems IV", pp 175-192. Morgan Kaufmann, 1994.
27
+ (7) Representing attitude: Euler angles, unit quaternions, and rotation
28
+ vectors. James Diebel. 2006.
29
+ (8) A discussion of the solution for the best rotation to relate two sets
30
+ of vectors. W Kabsch. Acta Cryst. 1978. A34, 827-828.
31
+ (9) Closed-form solution of absolute orientation using unit quaternions.
32
+ BKP Horn. J Opt Soc Am A. 1987. 4(4):629-642.
33
+ (10) Quaternions. Ken Shoemake.
34
+ http://www.sfu.ca/~jwa3/cmpt461/files/quatut.pdf
35
+ (11) From quaternion to matrix and back. JMP van Waveren. 2005.
36
+ http://www.intel.com/cd/ids/developer/asmo-na/eng/293748.htm
37
+ (12) Uniform random rotations. Ken Shoemake.
38
+ In "Graphics Gems III", pp 124-132. Morgan Kaufmann, 1992.
39
+ (13) Quaternion in molecular modeling. CFF Karney.
40
+ J Mol Graph Mod, 25(5):595-604
41
+ (14) New method for extracting the quaternion from a rotation matrix.
42
+ Itzhack Y Bar-Itzhack, J Guid Contr Dynam. 2000. 23(6): 1085-1087.
43
+ (15) Multiple View Geometry in Computer Vision. Hartley and Zissermann.
44
+ Cambridge University Press; 2nd Ed. 2004. Chapter 4, Algorithm 4.7, p 130.
45
+ (16) Column Vectors vs. Row Vectors.
46
+ http://steve.hollasch.net/cgindex/math/matrix/column-vec.html
47
+ """
48
+ # Copyright (c) 2006-2014, Christoph Gohlke
49
+ # Copyright (c) 2006-2014, The Regents of the University of California
50
+ # Produced at the Laboratory for Fluorescence Dynamics
51
+ # All rights reserved.
52
+ #
53
+ # Redistribution and use in source and binary forms, with or without
54
+ # modification, are permitted provided that the following conditions are met:
55
+ #
56
+ # * Redistributions of source code must retain the above copyright
57
+ # notice, this list of conditions and the following disclaimer.
58
+ # * Redistributions in binary form must reproduce the above copyright
59
+ # notice, this list of conditions and the following disclaimer in the
60
+ # documentation and/or other materials provided with the distribution.
61
+ # * Neither the name of the copyright holders nor the names of any
62
+ # contributors may be used to endorse or promote products derived
63
+ # from this software without specific prior written permission.
64
+ #
65
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
66
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
67
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
68
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
69
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
70
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
71
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
72
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
73
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
74
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
75
+ # POSSIBILITY OF SUCH DAMAGE.
76
+
77
+ import warnings
78
+
79
+ import numpy as np
80
+
81
+ from patme import epsilon
82
+ from patme.geometry.plane import Plane
83
+ from patme.geometry.rotate import Rotation
84
+ from patme.geometry.scale import Scaling
85
+ from patme.geometry.translate import Translation
86
+ from patme.service.exceptions import ImproperParameterError, InternalError
87
+ from patme.service.logger import log
88
+
89
+
90
+ class Transformation(np.ndarray):
91
+ """
92
+ This class describes a Transformation in 3D space which is represented by a 3x3 matrix.
93
+
94
+ With this class coordinate transformations in 3D space can be created and operations may be performed.
95
+ For that purpose the class inherits from np.ndarray which is a class for storing
96
+ matrix values and performing operations on them. For further details on np.ndarray
97
+ please refer to the numpy/scipy documentation.
98
+
99
+ Transformations are represented by a 4x4 matrix. A transformation is composed of
100
+ a rotation matrix r_ii, a translation t_i, a scaling s_i and a projection p_i the following way::
101
+
102
+ [[ s_1*r_11, r_21, r_31, t_1],
103
+ [ r_12, s_2*r_22, r_32, t_2],
104
+ [ r_13, r_23, s_3*r_33, t_3],
105
+ [ p_1 , p_2 , p_3 , 1 ]]
106
+
107
+ Actually Scalings are not actively supported in this class. Thus is it still
108
+ mathematically possible.
109
+
110
+ Transformations can be created and used in the following ways::
111
+
112
+ >>> import numpy as np
113
+ >>> from patme.geometry.coordinatesystem import Transformation
114
+ >>> from patme.geometry.translate import Translation
115
+ >>> from patme.geometry.rotate import Rotation
116
+ >>> t = Translation([5,2,0])
117
+ >>> # creating identity transformation
118
+ >>> tr = Transformation()
119
+ >>> tr
120
+ Transformation([[1., 0., 0., 0.],
121
+ [0., 1., 0., 0.],
122
+ [0., 0., 1., 0.],
123
+ [0., 0., 0., 1.]])
124
+ >>> # setting a translation
125
+ >>> tr.translation = t
126
+ >>> tr
127
+ Transformation([[1., 0., 0., 5.],
128
+ [0., 1., 0., 2.],
129
+ [0., 0., 1., 0.],
130
+ [0., 0., 0., 1.]])
131
+
132
+ >>> ####################### adding a translation
133
+ >>> tr.addTranslation(t)
134
+ >>> tr
135
+ Transformation([[ 1., 0., 0., 10.],
136
+ [ 0., 1., 0., 4.],
137
+ [ 0., 0., 1., 0.],
138
+ [ 0., 0., 0., 1.]])
139
+
140
+ >>> ####################### setting a rotation
141
+ >>> rx = Rotation()
142
+ >>> rx.angles = (np.pi/2,0,0)
143
+ >>> ry = Rotation()
144
+ >>> ry.angles = (0,np.pi/2,0)
145
+ >>> tr.rotation = rx
146
+ >>> tr.round() + 0 # ".round()" avoids long floats and "+ 0" turns all -0 in 0
147
+ Transformation([[ 1., 0., 0., 10.],
148
+ [ 0., 0., -1., 4.],
149
+ [ 0., 1., 0., 0.],
150
+ [ 0., 0., 0., 1.]])
151
+ >>> # adding a rotation
152
+ >>> tr.addRotation(ry)
153
+ >>> tr.round() + 0
154
+ Transformation([[ 0., 0., 1., 10.],
155
+ [ 1., 0., 0., 4.],
156
+ [ 0., 1., 0., 0.],
157
+ [ 0., 0., 0., 1.]])
158
+ >>> # concatenation of transformations
159
+ >>> tr2 = Transformation()
160
+ >>> tr2.translation = [0,0,1]
161
+ >>> tr3 = tr*tr2
162
+ >>> tr3.round() + 0
163
+ Transformation([[ 0., 0., 1., 11.],
164
+ [ 1., 0., 0., 4.],
165
+ [ 0., 1., 0., 0.],
166
+ [ 0., 0., 0., 1.]])
167
+ >>> # inverting transformation
168
+ >>> tr3inv = tr3.getInverse()
169
+ >>> tr3inv.round() + 0
170
+ Transformation([[ 0., 1., 0., -4.],
171
+ [ 0., 0., 1., 0.],
172
+ [ 1., 0., 0., -11.],
173
+ [ 0., 0., 0., 1.]])
174
+ >>> (tr3*tr3inv).round() + 0 # retruns identity matrix
175
+ Transformation([[1., 0., 0., 0.],
176
+ [0., 1., 0., 0.],
177
+ [0., 0., 1., 0.],
178
+ [0., 0., 0., 1.]])
179
+
180
+ >>> ####################### transformation of translations
181
+ >>> # MOST IMPORTANT !!!
182
+ >>> tr3*t
183
+ Translation([11., 9., 2.])
184
+ >>> t1 = tr3inv * (tr3 * t)
185
+ >>> t1.round()
186
+ Translation([5., 2., 0.])
187
+
188
+ >>> ####################### setting a scaling
189
+ >>> sx = Scaling()
190
+ >>> sx.factors = (2.0,1.0,1.0)
191
+ >>> sxyz = Scaling()
192
+ >>> sxyz.factors = (2.0,2.0,2.0)
193
+ >>> tr.scaling = sx # also resets the rotation
194
+ >>> tr
195
+ Transformation([[ 2., 0., 0., 10.],
196
+ [ 0., 1., 0., 4.],
197
+ [ 0., 0., 1., 0.],
198
+ [ 0., 0., 0., 1.]])
199
+ >>> # adding a scaling
200
+ >>> tr.addScaling(sxyz)
201
+ >>> tr
202
+ Transformation([[ 4., 0., 0., 10.],
203
+ [ 0., 2., 0., 4.],
204
+ [ 0., 0., 2., 0.],
205
+ [ 0., 0., 0., 1.]])
206
+ """
207
+
208
+ __hash__ = object.__hash__
209
+ """hash reimplementation due to definition of __eq__. See __hash__ doc for more details.
210
+ https://docs.python.org/3.4/reference/datamodel.html#object.__hash__"""
211
+
212
+ def __new__(cls, input_array=None, tId=None, description=None):
213
+ """constructing np.ndarray instance. For more information see
214
+ http://docs.scipy.org/doc/numpy/user/basics.subclassing.html#basics-subclassing"""
215
+ # Input array is an already formed ndarray instance
216
+ # We first cast to be our class type
217
+ if input_array is None:
218
+ input_array = np.identity(4, dtype=np.float64)
219
+ if not hasattr(input_array, "shape"):
220
+ input_array = np.asarray(input_array)
221
+ if not input_array.shape == (4, 4):
222
+ raise ImproperParameterError(
223
+ "The given transformation must be a 4x4 matrix like object. Got this instead: " + str(input_array)
224
+ )
225
+ obj = input_array.view(cls)
226
+ return obj
227
+
228
+ def setTransformation(self, t=None, r=None, s=None, t_id=None):
229
+ """doc"""
230
+ if np.any(t):
231
+ self[:3, 3] = t * 1
232
+
233
+ # # scale the transformation matrix
234
+ # # get scaling in each dimension and perform a matrix multiplication
235
+ # sc1 = trafo.scale_matrix(s.vals.x, None, [1, 0, 0])
236
+ # sc2 = trafo.scale_matrix(s.vals.y, None, [0, 1, 0])
237
+ # sc3 = trafo.scale_matrix(s.vals.z, None, [0, 0, 1])
238
+ # self = np.dot(np.dot(sc1, sc2), np.dot(sc3, self))
239
+
240
+ if np.any(r):
241
+ self[:3, :3] = r
242
+ if np.any(s):
243
+ self.addScaling(s)
244
+ if t_id:
245
+ self.id = t_id
246
+
247
+ def isIdentity(self):
248
+ """doc"""
249
+ return np.allclose(np.identity(4, dtype=np.float64), self, atol=epsilon)
250
+
251
+ def getInverse(self):
252
+ """invert Transformation
253
+ Rotation = R^transpose and Position = -R^transposed*p"""
254
+ ret = Transformation()
255
+ rT = self.rotation.T
256
+ sT = self.scaling.getInverse()
257
+ ret[:3, :3] = rT * sT
258
+ ret[:3, 3] = (rT * self.translation) * -1
259
+ return ret
260
+
261
+ def __invert__(self):
262
+ """invert Coordinatesystem
263
+ Rotation = R^Transponiert und Position = -R^Transponiert*p"""
264
+ self[:, :] = self.getInverse()
265
+
266
+ def __mul__(self, other):
267
+ """doc"""
268
+ if other is None:
269
+ raise ImproperParameterError("Got None for multiplication with transformation")
270
+
271
+ otherOrg = other
272
+ other = np.asarray(other)
273
+ if other.shape == (3,):
274
+ other = np.array([other[0], other[1], other[2], 1.0])
275
+ cls = otherOrg.__class__
276
+ if cls is np.ndarray:
277
+ cls = np.array
278
+ return cls((self @ other)[:3])
279
+
280
+ elif other.shape == (4,):
281
+ return np.array(self @ other)
282
+
283
+ elif other.shape == (4, 4):
284
+ return self @ other
285
+
286
+ elif other.shape[0] == 3:
287
+ # input is interpreted as array comprising of Translations
288
+ other = np.insert(other, 3, 1, axis=0)
289
+ return np.array(self @ other)[:3, :]
290
+
291
+ elif other.shape[0] == 4:
292
+ return np.array(np.dot(self, other))
293
+
294
+ # elif other.shape[1] == 3:
295
+ # other = np.insert(other, 3, 1, axis=1)
296
+ # return np.array(self @ other.T).T[:,:3]
297
+
298
+ # elif other.shape[1] == 4:
299
+ # return np.array(np.dot(self, other.T)).T
300
+ else:
301
+ raise ImproperParameterError(
302
+ "only array sizes of 3, 4, 4x4, 3xn and 4xn are supported. Got instead: " + str(other.shape)
303
+ )
304
+
305
+ def __imul__(self, other):
306
+ """doc"""
307
+ return self * other
308
+
309
+ def __eq__(self, other):
310
+ """doc"""
311
+ try:
312
+ return np.allclose(self, other)
313
+ except TypeError:
314
+ return False
315
+
316
+ def addTranslation(self, t):
317
+ """doc"""
318
+ if np.array(t).shape != (3,):
319
+ log.error(
320
+ 'Could not set translation "%s". An iterable with 3 scalars or an object of type Translation is needed!'
321
+ % t
322
+ )
323
+ self[:3, 3] += t[:]
324
+
325
+ def addRotation(self, rot):
326
+ """doc"""
327
+ if np.array(rot).shape != (3, 3):
328
+ raise ImproperParameterError(
329
+ 'Could not set rotation "%s". An iterable with 3x3 scalars or an object of type Rotation is needed!'
330
+ % rot
331
+ )
332
+ self[:3, :3] = np.dot(self[:3, :3], rot)
333
+
334
+ def addScaling(self, scal):
335
+ """doc"""
336
+ if np.array(scal).shape != (3, 3):
337
+ raise ImproperParameterError(
338
+ 'Could not set scaling "%s". An iterable with 3x3 scalars or an object of type Scaling is needed!'
339
+ % scal
340
+ )
341
+ self[:3, :3] = np.dot(self[:3, :3], scal)
342
+
343
+ def setRotationByAxisAndAngle(self, axis, angle):
344
+ """This method converts a rotation around a specified axis by a specified angle into rotation matrix.
345
+ The mathematical algorithm can be found in [Wiki2013]_ and also in [Tayl2013]_.
346
+
347
+ :param axis: instance of L{Translation}, specifying the rotational axis to be rotated about
348
+ :param angle: float, variable specifying the rotating angle in degrees about the axis
349
+ .. note:: The angle is being transformed into radians.
350
+
351
+ .. _[Wiki2013]: http://en.wikipedia.org/wiki/Rotation_matrix#cite_ref-2 (08.10.2013)
352
+ .. _[Tayl1994] Taylor, Camillo; Kriegman (1994). "Minimization on the Lie Group SO(3) and Related Manifolds". Technical Report. No. 9405 (Yale University).
353
+ """
354
+
355
+ if np.array(axis).shape != (3,):
356
+ log.error("Could not set rotation. An iterable with 3 scalars or an object of type Translation is needed!")
357
+
358
+ if not (isinstance(angle, float) or isinstance(angle, int)):
359
+ log.error("Could not set rotation. An angle in radiant of type float is needed!")
360
+
361
+ angle *= np.pi / 180.0
362
+ unitVector = axis / np.linalg.norm(axis)
363
+ sinAngle, cosAngle = np.sin(angle), np.cos(angle)
364
+
365
+ r = np.diag(cosAngle + unitVector[:3] ** 2 * (1.0 - cosAngle))
366
+
367
+ r[0, 1] = unitVector[0] * unitVector[1] * (1.0 - cosAngle) - unitVector[2] * sinAngle
368
+ r[1, 0] = unitVector[0] * unitVector[1] * (1.0 - cosAngle) + unitVector[2] * sinAngle
369
+
370
+ r[0, 2] = unitVector[0] * unitVector[2] * (1.0 - cosAngle) + unitVector[1] * sinAngle
371
+ r[2, 0] = unitVector[0] * unitVector[2] * (1.0 - cosAngle) - unitVector[1] * sinAngle
372
+
373
+ r[1, 2] = unitVector[1] * unitVector[2] * (1.0 - cosAngle) - unitVector[0] * sinAngle
374
+ r[2, 1] = unitVector[1] * unitVector[2] * (1.0 - cosAngle) + unitVector[0] * sinAngle
375
+
376
+ self.rotation = r
377
+
378
+ def getAxisAndAngleByRotation(self):
379
+ """This method is intended to decompose the transformation instance into one rotation axis and a corresponding rotation angle.
380
+ The mathematical algorithm can be found in [Wiki2013]_.
381
+
382
+ :returns: tuple, containing the rotation axis as Translation and the rotation angle in radians
383
+
384
+ .. _[Wiki2013]: http://en.wikipedia.org/wiki/Rotation_matrix#cite_ref-2 (08.10.2013)
385
+ """
386
+
387
+ def getAxis(rotationMatrix):
388
+
389
+ rotMatEigenResult = np.linalg.eig(rotationMatrix)
390
+ # ---FIND MATRIX EIGENVALUE EQUAL TO 1
391
+ if not complex(1.0) in rotMatEigenResult[0]:
392
+ raise InternalError("Result of eigenanalysis of rotation matrix contains no eigenvalue equal to 1.")
393
+ else:
394
+ for i in [0, 1, 2]:
395
+ if rotMatEigenResult[0][i] == complex(1.0):
396
+ eigenValueIndex = i
397
+
398
+ # ---X-COORDINATE
399
+ xReal = rotMatEigenResult[1][0][eigenValueIndex].real
400
+ xImag = rotMatEigenResult[1][0][eigenValueIndex].imag
401
+ if xImag > epsilon:
402
+ raise InternalError("Rotation axis x-coordinate is complex.")
403
+
404
+ yReal = rotMatEigenResult[1][1][eigenValueIndex].real
405
+ yImag = rotMatEigenResult[1][1][eigenValueIndex].imag
406
+ if yImag > epsilon:
407
+ raise InternalError("Rotation axis y-coordinate is complex.")
408
+
409
+ zReal = rotMatEigenResult[1][2][eigenValueIndex].real
410
+ zImag = rotMatEigenResult[1][2][eigenValueIndex].imag
411
+ if zImag > epsilon:
412
+ raise InternalError("Rotation axis z-coordinate is complex.")
413
+
414
+ return Translation([xReal, yReal, zReal])
415
+
416
+ rotationMatrix = self.getRotation()
417
+ # ---DETERMINE AXIS
418
+ # the roation axis has to be an eigenvector of the rotation matrix
419
+ # the axis is equivalent to the eigenvector belonging to the first eigenvalue of the matrix
420
+ with warnings.catch_warnings():
421
+
422
+ warnings.simplefilter("ignore")
423
+ axis = getAxis(rotationMatrix)
424
+
425
+ # ---DETERMINE ANGLE
426
+ # ---RETRIEVE VECTOR PERPENDICULAR TO AXIS
427
+ tmpPlane = Plane().generatePlane(positioningPoint=Translation([0.0, 0.0, 0.0]), planeNormalVector=axis)
428
+ axisPerpendiculaVector = tmpPlane.planeOrientationVector1
429
+
430
+ rotatedAxisPerpendiculaVector = rotationMatrix * axisPerpendiculaVector
431
+ arcos = np.dot(axisPerpendiculaVector, rotatedAxisPerpendiculaVector)
432
+ arcos = arcos / (np.linalg.norm(axisPerpendiculaVector) * np.linalg.norm(rotatedAxisPerpendiculaVector))
433
+
434
+ return (axis, np.arccos(arcos))
435
+
436
+ def estimateTransformationFromPointSets(self, fromPoints, toPoints):
437
+ r"""
438
+ This method is intended for superimposing arrays of 3D homogeneous coordinates.
439
+ It calculates the Transformation representing the transformation of the fromPoints into
440
+ the corresponding toPoints.
441
+
442
+ :param fromPoints: list, containing at least three instances of Translation
443
+ :param toPoints: list, containing at least three instances of Translation corresponding to the points within the list toPoints
444
+
445
+ :returns: self, with translation, rotation and scaling set adequately
446
+
447
+
448
+ :Author:
449
+ `Christoph Gohlke <http://www.lfd.uci.edu/~gohlke/>`_
450
+
451
+ :Editor:
452
+ `Falk Heinecke 23.01.2014`
453
+
454
+ :Organization:
455
+ Laboratory for Fluorescence Dynamics, University of California, Irvine
456
+
457
+ :Version: 2013.06.29
458
+
459
+ Requirements
460
+ ------------
461
+ * `CPython 2.7 or 3.3 <http://www.python.org>`_
462
+ * `Numpy 1.7 <http://www.numpy.org>`_
463
+ * `Transformations.c 2013.01.18 <http://www.lfd.uci.edu/~gohlke/>`_
464
+ (recommended for speedup of some functions)
465
+
466
+ Return affine transform matrix to register two point sets.
467
+
468
+ v0 and v1 are shape (ndims, \*) arrays of at least ndims non-homogeneous
469
+ coordinates, where ndims is the dimensionality of the coordinate space.
470
+
471
+ A rigid/Euclidean transformation matrix is returned.
472
+
473
+ Similarity and Euclidean transformation matrices
474
+ are calculated by minimizing the weighted sum of squared deviations
475
+ (RMSD) according to the algorithm by Kabsch [8].
476
+
477
+ Example:
478
+
479
+ >>> v0 = [Translation([1., 0., 0.]), Translation([0., 1., 0.]), Translation([0., 0., 1.])]
480
+ >>> v1 = [Translation([2., 0., 0.]), Translation([1., 1., 0.]), Translation([1., 0., 1.])]
481
+ >>> tr = Transformation().estimateTransformationFromPointSets(v0, v1)
482
+ >>> tr.round().astype(int)
483
+ Transformation([[1, 0, 0, 1],
484
+ [0, 1, 0, 0],
485
+ [0, 0, 1, 0],
486
+ [0, 0, 0, 1]])
487
+ """
488
+
489
+ fromPoints = np.array(fromPoints, dtype=np.float64, copy=True)
490
+ toPoints = np.array(toPoints, dtype=np.float64, copy=True)
491
+ # points need to be stored as columns within the array
492
+ fromPoints = np.transpose(fromPoints)
493
+ toPoints = np.transpose(toPoints)
494
+
495
+ ndims = fromPoints.shape[0]
496
+ if ndims < 2 or fromPoints.shape[1] < ndims or fromPoints.shape != toPoints.shape:
497
+ raise ValueError("input arrays are of wrong shape or type")
498
+
499
+ # move centroids to origin
500
+ t0 = -np.mean(fromPoints, axis=1)
501
+ M0 = np.identity(ndims + 1)
502
+ M0[:ndims, ndims] = t0
503
+ fromPoints += t0.reshape(ndims, 1)
504
+ t1 = -np.mean(toPoints, axis=1)
505
+ M1 = np.identity(ndims + 1)
506
+ M1[:ndims, ndims] = t1
507
+ toPoints += t1.reshape(ndims, 1)
508
+
509
+ # Rigid transformation via SVD of covariance matrix
510
+ u, s, vh = np.linalg.svd(np.dot(toPoints, fromPoints.T))
511
+ # rotation matrix from SVD orthonormal bases
512
+ R = np.dot(u, vh)
513
+ if np.linalg.det(R) < 0.0:
514
+ # R does not constitute right handed system
515
+ R -= np.outer(u[:, ndims - 1], vh[ndims - 1, :] * 2.0)
516
+ s[-1] *= -1.0
517
+ # homogeneous transformation matrix
518
+ M = np.identity(ndims + 1)
519
+ M[:ndims, :ndims] = R
520
+
521
+ if 1: # scale:
522
+ # Affine transformation; scale is ratio of RMS deviations from centroid
523
+ fromPoints *= fromPoints
524
+ toPoints *= toPoints
525
+ M[:ndims, :ndims] *= np.sqrt(np.sum(toPoints) / np.sum(fromPoints))
526
+
527
+ # move centroids back
528
+ M = np.dot(np.linalg.inv(M1), np.dot(M, M0))
529
+ M /= M[ndims, ndims]
530
+
531
+ self.rotation = M[:3, :3]
532
+ self.translation = M[:3, 3]
533
+
534
+ return self
535
+
536
+ def mirror(self):
537
+ """Method to create a new coordinate system object with mirroring the axis
538
+ on the x-z plane."""
539
+ transformation = Transformation(
540
+ [
541
+ [1, -1, 1, 1],
542
+ [-1, 1, -1, -1],
543
+ [1, -1, 1, 1],
544
+ [0, 0, 0, 1],
545
+ ]
546
+ )
547
+
548
+ return np.multiply(self.copy(), transformation)
549
+
550
+ def _setTranslation(self, translation):
551
+ """doc"""
552
+ if not np.array(translation).shape == (3,):
553
+ raise ImproperParameterError(
554
+ 'Could not set translation "%s".'
555
+ + " An iterable with 3 scalars or an object of type Translation is needed!" % translation
556
+ )
557
+ self[:3, 3] = translation[:]
558
+
559
+ def _getTranslation(self):
560
+ """doc"""
561
+ return np.array(self[:3, 3])
562
+
563
+ def _setRotation(self, rot):
564
+ """doc"""
565
+ if not np.array(rot).shape == (3, 3):
566
+ raise ImproperParameterError(
567
+ 'Could not set rotation "%s".'
568
+ + " An iterable with 3x3 scalars or an object of type Rotation is needed!" % rot
569
+ )
570
+ self[:3, :3] = rot
571
+
572
+ def _getRotation(self):
573
+ """doc"""
574
+ scaleFactors = 1.0 / np.outer(self.scaling.factors[:3], np.ones(3))
575
+ return Rotation(np.multiply(self[:3, :3], scaleFactors))
576
+
577
+ def _setScaling(self, scal):
578
+ """doc"""
579
+ if np.array(scal).shape != (3, 3):
580
+ raise ImproperParameterError(
581
+ 'Could not set scaling "%s".'
582
+ + " An iterable with 3x3 scalars or an object of type Scaling is needed!" % scal
583
+ )
584
+ self[:3, :3] = scal
585
+
586
+ def _getScaling(self):
587
+ """doc"""
588
+ scaling = Scaling()
589
+ scaling.factors = (np.linalg.norm(self[0, :3]), np.linalg.norm(self[1, :3]), np.linalg.norm(self[2, :3]))
590
+
591
+ return scaling
592
+
593
+ def getReflectedCoordinateSystem(self, reflectionType):
594
+ """this routine reflects the respective coordinatesystem by the given reflection Type
595
+ reflectionType "xAxis" generates a reflection about the x-axis
596
+ reflect = [[1,0.,0.],
597
+ [0.,-1, 0.],
598
+ [0.,0.,1]]
599
+ reflectionType "yAxis" generates a reflection about the y-axis
600
+ reflect = [[-1,0.,0.],
601
+ [0.,1, 0.],
602
+ [0.,0.,1]]
603
+ reflectionType "zAxis" generates a reflection about the z-axis
604
+ reflect = [[1,0.,0.],
605
+ [0.,1, 0.],
606
+ [0.,0.,-1]]
607
+ """
608
+
609
+ idxToReflectedDict = {"xAxis": 1, "yAxis": 0, "zAxis": 2}
610
+ if reflectionType in idxToReflectedDict.keys():
611
+ reflect = np.identity(3, np.float64)
612
+ idx = idxToReflectedDict[reflectionType]
613
+ reflect[idx, idx] *= -1
614
+ else:
615
+ raise ImproperParameterError("the reflection Type is not correct: " + reflectionType)
616
+
617
+ if not np.array(reflect).shape == (3, 3):
618
+ log.error(
619
+ 'Could not set reflection "%s". An iterable with 3x3 scalars or an object of type Reflection is needed!'
620
+ % reflect
621
+ )
622
+ self[:3, :3] = np.dot(self[:3, :3], reflect)
623
+ self[:3, 3] = np.dot(self[:3, 3], reflect)
624
+
625
+ return self
626
+
627
+ translation = property(fset=_setTranslation, fget=_getTranslation)
628
+ rotation = property(fset=_setRotation, fget=_getRotation)
629
+ scaling = property(fset=_setScaling, fget=_getScaling)
630
+
631
+
632
+ if __name__ == "__main__":
633
+ import doctest
634
+
635
+ doctest.testmod()