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/misc.py
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
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
|
+
from operator import eq, ne
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
from patme import epsilon
|
|
11
|
+
from patme.geometry.translate import Translation
|
|
12
|
+
from patme.service.exceptions import ImproperParameterError, InternalError
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def PCA(data, correlation=False, sort=True):
|
|
16
|
+
"""Applies Principal Component Analysis to the data
|
|
17
|
+
|
|
18
|
+
Method from
|
|
19
|
+
https://github.com/daavoo/pyntcloud/blob/master/pyntcloud/utils/array.py
|
|
20
|
+
MIT license
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
data: array
|
|
25
|
+
The array containing the data. The array must have NxM dimensions, where each
|
|
26
|
+
of the N rows represents a different individual record and each of the M columns
|
|
27
|
+
represents a different variable recorded for that individual record.
|
|
28
|
+
array([
|
|
29
|
+
[V11, ... , V1m],
|
|
30
|
+
...,
|
|
31
|
+
[Vn1, ... , Vnm]])
|
|
32
|
+
|
|
33
|
+
correlation(Optional) : bool
|
|
34
|
+
Set the type of matrix to be computed (see Notes):
|
|
35
|
+
If True compute the correlation matrix.
|
|
36
|
+
If False(Default) compute the covariance matrix.
|
|
37
|
+
|
|
38
|
+
sort(Optional) : bool
|
|
39
|
+
Set the order that the eigenvalues/vectors will have
|
|
40
|
+
If True(Default) they will be sorted (from higher value to less).
|
|
41
|
+
If False they won't.
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
eigenvalues: (1,M) array
|
|
45
|
+
The eigenvalues of the corresponding matrix.
|
|
46
|
+
|
|
47
|
+
eigenvector: (M,M) array
|
|
48
|
+
The eigenvectors of the corresponding matrix.
|
|
49
|
+
|
|
50
|
+
Notes
|
|
51
|
+
-----
|
|
52
|
+
The correlation matrix is a better choice when there are different magnitudes
|
|
53
|
+
representing the M variables. Use covariance matrix in other cases.
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
mean = np.mean(data, axis=0)
|
|
58
|
+
|
|
59
|
+
data_adjust = data - mean
|
|
60
|
+
|
|
61
|
+
#: the data is transposed due to np.cov/corrcoef syntax
|
|
62
|
+
if correlation:
|
|
63
|
+
|
|
64
|
+
matrix = np.corrcoef(data_adjust.T)
|
|
65
|
+
|
|
66
|
+
else:
|
|
67
|
+
matrix = np.cov(data_adjust.T)
|
|
68
|
+
|
|
69
|
+
eigenvalues, eigenvectors = np.linalg.eig(matrix)
|
|
70
|
+
|
|
71
|
+
if sort:
|
|
72
|
+
#: sort eigenvalues and eigenvectors
|
|
73
|
+
sort = eigenvalues.argsort()[::-1]
|
|
74
|
+
eigenvalues = eigenvalues[sort]
|
|
75
|
+
eigenvectors = eigenvectors[:, sort]
|
|
76
|
+
|
|
77
|
+
return eigenvalues, eigenvectors
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def areVectorsParallel(vector1, vector2):
|
|
81
|
+
"""checks if two vectors are parallel to each other
|
|
82
|
+
|
|
83
|
+
>>> areVectorsParallel([0,0,2],[0,0,1])
|
|
84
|
+
True
|
|
85
|
+
>>> areVectorsParallel([0,0,2],[0,1,1])
|
|
86
|
+
False
|
|
87
|
+
>>> areVectorsParallel([0,0,0],[0,0,1])
|
|
88
|
+
False
|
|
89
|
+
"""
|
|
90
|
+
vector1, vector2 = np.array(vector1), np.array(vector2)
|
|
91
|
+
if np.linalg.norm(vector1) < epsilon or np.linalg.norm(vector2) < epsilon:
|
|
92
|
+
return False
|
|
93
|
+
|
|
94
|
+
# normalize vectors
|
|
95
|
+
vector1, vector2 = vector1 / vector1[np.argmax(np.abs(vector1))], vector2 / vector2[np.argmax(np.abs(vector2))]
|
|
96
|
+
return np.linalg.norm(vector1 - vector2) < epsilon
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def getGeometricCenterSimple(keypointList):
|
|
100
|
+
"""Returns the geometric center of self using the average of the min and
|
|
101
|
+
max coordinates.
|
|
102
|
+
|
|
103
|
+
>>> from patme.geometry.translate import Translation
|
|
104
|
+
>>> keypointList = [Translation([0,a,(-1)**a*5]) for a in range(10)]
|
|
105
|
+
>>> getGeometricCenterSimple(keypointList)
|
|
106
|
+
Translation([0. , 4.5, 0. ])
|
|
107
|
+
|
|
108
|
+
:param keypointList: list of Translation objects
|
|
109
|
+
:return: instance of type Translation at the geometric center of the rib
|
|
110
|
+
"""
|
|
111
|
+
pointArray = np.array([kp[:3] for kp in keypointList])
|
|
112
|
+
center = (pointArray.max(axis=0) + pointArray.min(axis=0)) / 2
|
|
113
|
+
return Translation(center)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def getAngleOnYZPlane(keypoint, referenceY=0.0, referenceZ=0.0):
|
|
117
|
+
"""calculating angle of keypoint on the y-z plane in reference to a
|
|
118
|
+
optionally shifted coordinate system.
|
|
119
|
+
|
|
120
|
+
:return: angle in degees[0,360]
|
|
121
|
+
|
|
122
|
+
.. note:: Please refer to the CPACS definition of the fuselage coordinate system.
|
|
123
|
+
These are the results for the CPACS definition::
|
|
124
|
+
|
|
125
|
+
# results of getAngleOnYZPlane:
|
|
126
|
+
# y ^
|
|
127
|
+
# 225 | 315
|
|
128
|
+
# |
|
|
129
|
+
# <---x---> z
|
|
130
|
+
# |
|
|
131
|
+
# 135 | 45
|
|
132
|
+
|
|
133
|
+
tan alpha = y / z
|
|
134
|
+
coordinate system is rotated since angle=0 is on positive z-axis
|
|
135
|
+
additionally the negative angles need to be shifted by 360deg
|
|
136
|
+
|
|
137
|
+
>>> t1 = Translation([0,-1.,1.])
|
|
138
|
+
>>> abs(getAngleOnYZPlane(t1))
|
|
139
|
+
45.0
|
|
140
|
+
>>> t1 = Translation([0,0.,1.])
|
|
141
|
+
>>> abs(getAngleOnYZPlane(t1))
|
|
142
|
+
0.0
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
alpha = np.arctan2(keypoint.y - referenceY, keypoint.z - referenceZ) * 180 / np.pi
|
|
146
|
+
if alpha < epsilon:
|
|
147
|
+
alpha += 360
|
|
148
|
+
|
|
149
|
+
# subtract alpha by 360 since arctan2 calculates alpha in mathematically positive direction but
|
|
150
|
+
# tigl calculates it in the negative direction
|
|
151
|
+
return 360.0 - alpha
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def getNearestKeypoint(points, point):
|
|
155
|
+
"""This method returns the point from a specified point list next to the
|
|
156
|
+
position of a specified point.
|
|
157
|
+
All points have to be of a class that inherits the numpy array class."""
|
|
158
|
+
|
|
159
|
+
tmpPoints = np.array(points, copy=True)
|
|
160
|
+
allDists = np.linalg.norm(tmpPoints - point, axis=1)
|
|
161
|
+
return points[np.argmin(allDists)]
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def getRelativePositionOfProjectedPointOnLine(point, lineStartPoint, lineEndPoint):
|
|
165
|
+
"""Retrieve the relative distance of point Pp to the lineStartPoint. Pp is the point
|
|
166
|
+
projection of point P on the straight line through the points lineStartPoint and
|
|
167
|
+
lineEndPoint. The relative distance can also be < 0 or > 1, indicating that the
|
|
168
|
+
projected point is not in between the given points.
|
|
169
|
+
|
|
170
|
+
x P P = point
|
|
171
|
+
Pp = projected point
|
|
172
|
+
o-------x-------o Start = lineStartPoint
|
|
173
|
+
Start Pp End End = lineEndPoint
|
|
174
|
+
0-----alpha---->1
|
|
175
|
+
|
|
176
|
+
>>> round(getRelativePositionOfProjectedPointOnLine([0,0,0], [-1,-1,-1], [1,1,1]), 1)
|
|
177
|
+
0.5
|
|
178
|
+
>>> round(getRelativePositionOfProjectedPointOnLine([-1,-1,-1], [-1,-1,-1], [1,1,1]), 1)
|
|
179
|
+
0.0
|
|
180
|
+
>>> round(getRelativePositionOfProjectedPointOnLine([1,1,1], [-1,-1,-1], [1,1,1]), 1)
|
|
181
|
+
1.0
|
|
182
|
+
>>> round(getRelativePositionOfProjectedPointOnLine([10,10,10], [-1,-1,-1], [1,1,1]), 1)
|
|
183
|
+
5.5
|
|
184
|
+
>>> round(getRelativePositionOfProjectedPointOnLine([-3,-3,-3], [-1,-1,-1], [1,1,1]), 1)
|
|
185
|
+
-1.0
|
|
186
|
+
>>> round(getRelativePositionOfProjectedPointOnLine([2,2,2], [0,0,0], [1,0,0]), 1)
|
|
187
|
+
2.0
|
|
188
|
+
|
|
189
|
+
:param point: array with 3d coordinate of point to be projected
|
|
190
|
+
:param lineStartPoint: array with 3d coordinate of the line start point
|
|
191
|
+
:param lineEndPoint: array with 3d coordinate of the line end point
|
|
192
|
+
:return: relative distance between lineStartPoint and projection of point
|
|
193
|
+
"""
|
|
194
|
+
point, lineStartPoint, lineEndPoint = np.array(point), np.array(lineStartPoint), np.array(lineEndPoint)
|
|
195
|
+
alpha = np.dot(
|
|
196
|
+
(point - lineStartPoint), (lineEndPoint - lineStartPoint) / np.linalg.norm(lineEndPoint - lineStartPoint) ** 2
|
|
197
|
+
)
|
|
198
|
+
return alpha
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def getPointProjectedOnLine(lineStart, lineEnd, point, clipAlpha=True):
|
|
202
|
+
"""calculate the point that is projected from point to the line defined by lineStart and lineEnd
|
|
203
|
+
|
|
204
|
+
:return: Pp - projected point
|
|
205
|
+
|
|
206
|
+
x P P = point
|
|
207
|
+
Pp = projected point
|
|
208
|
+
o-------x-------o Start = lineStartPoint
|
|
209
|
+
Start Pp End End = lineEndPoint
|
|
210
|
+
0-----alpha---->1
|
|
211
|
+
|
|
212
|
+
>>> getPointProjectedOnLine([0,0,0], [0,0,1], [1,0,0])
|
|
213
|
+
array([0., 0., 0.])
|
|
214
|
+
>>> getPointProjectedOnLine([0,0,0], [0,0,2], [1,0,1])
|
|
215
|
+
array([0., 0., 1.])
|
|
216
|
+
|
|
217
|
+
"""
|
|
218
|
+
point, lineStart, lineEnd = np.array(point), np.array(lineStart), np.array(lineEnd)
|
|
219
|
+
alpha = getRelativePositionOfProjectedPointOnLine(point, lineStart, lineEnd)
|
|
220
|
+
# limitation of alpha between 0 and 1
|
|
221
|
+
# next position for distance calculation if clipAlpha equals true
|
|
222
|
+
# else projection point is used for distance computation
|
|
223
|
+
if clipAlpha:
|
|
224
|
+
alpha = np.clip(alpha, 0, 1)
|
|
225
|
+
projPoint = lineStart + alpha * (lineEnd - lineStart)
|
|
226
|
+
return projPoint
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def getPointDistanceToLine(lineStart, lineEnd, point, clipAlpha=True):
|
|
230
|
+
"""Retrieve the absolute distance of point from the straight line through lineStart and
|
|
231
|
+
lineEnd. The returned distance is the distance between P and Pp
|
|
232
|
+
|
|
233
|
+
x P P = point
|
|
234
|
+
Pp = projected point
|
|
235
|
+
o-------x-------o Start = lineStartPoint
|
|
236
|
+
Start Pp End End = lineEndPoint
|
|
237
|
+
0-----alpha---->1
|
|
238
|
+
|
|
239
|
+
>>> round(getPointDistanceToLine([-1,-1,-1], [1,1,1], [0,0,0]), 1)
|
|
240
|
+
0.0
|
|
241
|
+
>>> round(getPointDistanceToLine([0,0,0], [0,0,1], [2,0,-2], clipAlpha=False), 1)
|
|
242
|
+
2.0
|
|
243
|
+
>>> round(getPointDistanceToLine([-1,-1,-1], [1,1,1], [10,10,10], clipAlpha=False), 1)
|
|
244
|
+
0.0
|
|
245
|
+
|
|
246
|
+
:param lineStart: array with 3d coordinate of the line start point
|
|
247
|
+
:param lineEnd: array with 3d coordinate of the line end point
|
|
248
|
+
:param point: array with 3d coordinate of point to be projected
|
|
249
|
+
:return: distance between point and the straight line through lineStart and lineEnd
|
|
250
|
+
"""
|
|
251
|
+
point, lineStart, lineEnd = np.array(point), np.array(lineStart), np.array(lineEnd)
|
|
252
|
+
projPoint = getPointProjectedOnLine(lineStart, lineEnd, point, clipAlpha)
|
|
253
|
+
distance = np.linalg.norm(point - projPoint)
|
|
254
|
+
if distance == np.nan:
|
|
255
|
+
length = np.linalg.norm(lineStart, lineEnd)
|
|
256
|
+
raise InternalError(
|
|
257
|
+
"Got an error while calculating the distance of a point to a line. " + "The line-points coincide."
|
|
258
|
+
if length < epsilon
|
|
259
|
+
else ""
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
return distance
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def isPointProjectionOnLineBetweenPoints(point, lineStartPoint, lineEndPoint):
|
|
266
|
+
"""Checks if the projection of point onto the straight line through lineStartPoint
|
|
267
|
+
and lineEndPoint is between lineStartPoint and lineEndPoint. Both end points are
|
|
268
|
+
included into the interval. For further information read the documentation of the
|
|
269
|
+
function getRelativePositionOfProjectedPointOnLine()
|
|
270
|
+
|
|
271
|
+
>>> isPointProjectionOnLineBetweenPoints([0,0,0], [-1,-1,-1], [1,1,1])
|
|
272
|
+
True
|
|
273
|
+
>>> isPointProjectionOnLineBetweenPoints([-1,-1,-1], [-1,-1,-1], [1,1,1])
|
|
274
|
+
True
|
|
275
|
+
>>> isPointProjectionOnLineBetweenPoints([1,1,1], [-1,-1,-1], [1,1,1])
|
|
276
|
+
True
|
|
277
|
+
>>> isPointProjectionOnLineBetweenPoints([10,10,10], [-1,-1,-1], [1,1,1])
|
|
278
|
+
False
|
|
279
|
+
>>> isPointProjectionOnLineBetweenPoints([-10,-10,-10], [-1,-1,-1], [1,1,1])
|
|
280
|
+
False
|
|
281
|
+
>>> isPointProjectionOnLineBetweenPoints([-0.5,-0.5,0.], [-1,-1,-1], [1,1,1])
|
|
282
|
+
True
|
|
283
|
+
|
|
284
|
+
:param point: array with 3d coordinate of point to be projected
|
|
285
|
+
:param lineStartPoint: array with 3d coordinate of the line start point
|
|
286
|
+
:param lineEndPoint: array with 3d coordinate of the line end point
|
|
287
|
+
:return: True if projection of point is between lineStartPoint and lineEndPoint, else False
|
|
288
|
+
"""
|
|
289
|
+
point, lineStartPoint, lineEndPoint = np.array(point), np.array(lineStartPoint), np.array(lineEndPoint)
|
|
290
|
+
alpha = getRelativePositionOfProjectedPointOnLine(point, lineStartPoint, lineEndPoint)
|
|
291
|
+
if alpha > -epsilon and alpha < 1 + epsilon:
|
|
292
|
+
return True
|
|
293
|
+
else:
|
|
294
|
+
return False
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def isPointOnLineBetweenPoints(point, lineStartPoint, lineEndPoint):
|
|
298
|
+
"""Checks if point is located on a straight line between the given points lineStartPoint and
|
|
299
|
+
lineEndPoint including these points.
|
|
300
|
+
|
|
301
|
+
>>> isPointOnLineBetweenPoints([0,0,0], [-1,-1,-1], [1,1,1])
|
|
302
|
+
True
|
|
303
|
+
>>> isPointOnLineBetweenPoints([-1,-1,-1], [-1,-1,-1], [1,1,1])
|
|
304
|
+
True
|
|
305
|
+
>>> isPointOnLineBetweenPoints([1,1,1], [-1,-1,-1], [1,1,1])
|
|
306
|
+
True
|
|
307
|
+
>>> isPointOnLineBetweenPoints([10,10,10], [-1,-1,-1], [1,1,1])
|
|
308
|
+
False
|
|
309
|
+
>>> isPointOnLineBetweenPoints([-10,-10,-10], [-1,-1,-1], [1,1,1])
|
|
310
|
+
False
|
|
311
|
+
>>> isPointOnLineBetweenPoints([-10,-10,0], [-1,-1,-1], [1,1,1])
|
|
312
|
+
False
|
|
313
|
+
|
|
314
|
+
:param point: 1x3 array containing a 3D coordinate of the point to be checked
|
|
315
|
+
:param lineStartPoint: 1x3 array containing a 3D coordinate of the line start point
|
|
316
|
+
:param lineStartPoint: 1x3 array containing a 3D coordinate of the line end point
|
|
317
|
+
:return: True if point is on straight line between lineStartPoint and lineEndPoint, else False
|
|
318
|
+
"""
|
|
319
|
+
point, lineStartPoint, lineEndPoint = np.array(point), np.array(lineStartPoint), np.array(lineEndPoint)
|
|
320
|
+
# check if lineStartPoint and lineEndPoint are not equal
|
|
321
|
+
if all(np.abs(lineStartPoint - lineEndPoint) < epsilon):
|
|
322
|
+
raise ImproperParameterError("lineStartPoint and lineEndPoint must not be coincidental")
|
|
323
|
+
# check if point is coincident with lineStartPoint or lineEndPoint
|
|
324
|
+
if all(np.abs(lineStartPoint - point) < epsilon) or all(np.abs(lineEndPoint - point) < epsilon):
|
|
325
|
+
return True
|
|
326
|
+
# check if point is not on straight line through lineStartPoint and lineEndPoint
|
|
327
|
+
if getPointDistanceToLine(lineStartPoint, lineEndPoint, point, clipAlpha=False) > epsilon:
|
|
328
|
+
return False
|
|
329
|
+
# check if point is in between lineStartPoint and lineEndPoint
|
|
330
|
+
if isPointProjectionOnLineBetweenPoints(point, lineStartPoint, lineEndPoint):
|
|
331
|
+
return True
|
|
332
|
+
return False
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def getLineLoop(lines, startLine=None):
|
|
336
|
+
"""retuns a list of lines in the correct order"""
|
|
337
|
+
|
|
338
|
+
def isAdjacentLine(line, otherLine):
|
|
339
|
+
return bool({line.p1, line.p2} & {otherLine.p1, otherLine.p2})
|
|
340
|
+
|
|
341
|
+
if len(lines) == 1:
|
|
342
|
+
return lines, [1]
|
|
343
|
+
|
|
344
|
+
linesCopy = lines[:]
|
|
345
|
+
if startLine is not None:
|
|
346
|
+
ix = linesCopy.index(startLine)
|
|
347
|
+
if ix == -1:
|
|
348
|
+
firstLine = linesCopy[0]
|
|
349
|
+
else:
|
|
350
|
+
firstLine = linesCopy[ix]
|
|
351
|
+
else:
|
|
352
|
+
firstLine = linesCopy[0]
|
|
353
|
+
|
|
354
|
+
loop = [firstLine]
|
|
355
|
+
linesCopy.remove(firstLine)
|
|
356
|
+
|
|
357
|
+
curLine = None
|
|
358
|
+
adjLines = [line for line in linesCopy if isAdjacentLine(line, loop[0])]
|
|
359
|
+
if adjLines:
|
|
360
|
+
curLine = next((line for line in adjLines if line.p1 == loop[0].p2), None)
|
|
361
|
+
if not curLine:
|
|
362
|
+
curLine = next((line for line in adjLines if line.p2 == loop[0].p2), None)
|
|
363
|
+
if not curLine:
|
|
364
|
+
raise Exception("No matching adjacent line found for line %s" % loop[0].id)
|
|
365
|
+
else:
|
|
366
|
+
raise Exception("No matching adjacent line found for line %s" % loop[0].id)
|
|
367
|
+
|
|
368
|
+
loop.append(curLine)
|
|
369
|
+
|
|
370
|
+
linesCopy.pop(linesCopy.index(curLine))
|
|
371
|
+
while len(linesCopy) > 0:
|
|
372
|
+
nextLineIx = next(i for i, line in enumerate(linesCopy) if isAdjacentLine(line, curLine))
|
|
373
|
+
loop.append(linesCopy[nextLineIx])
|
|
374
|
+
curLine = linesCopy[nextLineIx]
|
|
375
|
+
linesCopy.pop(nextLineIx)
|
|
376
|
+
|
|
377
|
+
lineOrientation = [1]
|
|
378
|
+
cmpFuncDict = {-1: eq, 1: ne}
|
|
379
|
+
for i, line in enumerate(loop[1:], 1):
|
|
380
|
+
|
|
381
|
+
cmpFunc = cmpFuncDict[lineOrientation[i - 1]]
|
|
382
|
+
|
|
383
|
+
prevLinePoints = loop[i - 1].keypoints[:: lineOrientation[i - 1]]
|
|
384
|
+
|
|
385
|
+
if cmpFunc(line.p1.id, prevLinePoints[-1].id):
|
|
386
|
+
muliplier = -1
|
|
387
|
+
else:
|
|
388
|
+
muliplier = 1
|
|
389
|
+
|
|
390
|
+
lineOrientation.append(lineOrientation[i - 1] * muliplier)
|
|
391
|
+
|
|
392
|
+
return loop, lineOrientation
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def getAngleBetweenVectors(vector1, vector2):
|
|
396
|
+
"""calcuclates the angle [°] between two vectors
|
|
397
|
+
|
|
398
|
+
>>> angle = getAngleBetweenVectors([1,0,0],[0,1,0])
|
|
399
|
+
>>> round(angle,1)
|
|
400
|
+
90.0
|
|
401
|
+
>>> angle = getAngleBetweenVectors([1,0,0],[1,1,0])
|
|
402
|
+
>>> round(angle,1)
|
|
403
|
+
45.0
|
|
404
|
+
>>> angle = getAngleBetweenVectors([1,0,0],[1,0,0])
|
|
405
|
+
>>> round(angle,1)
|
|
406
|
+
0.0
|
|
407
|
+
"""
|
|
408
|
+
vector1, vector2 = np.array(vector1), np.array(vector2)
|
|
409
|
+
|
|
410
|
+
nominator = np.dot(vector1, vector2)
|
|
411
|
+
denominator = np.linalg.norm(vector1) * np.linalg.norm(vector2)
|
|
412
|
+
fraction = nominator / denominator
|
|
413
|
+
if abs(fraction - 1) < epsilon:
|
|
414
|
+
angleRad = 0.0
|
|
415
|
+
elif abs(fraction + 1) < epsilon:
|
|
416
|
+
angleRad = np.pi
|
|
417
|
+
else:
|
|
418
|
+
angleRad = np.arccos(fraction)
|
|
419
|
+
angle = np.degrees(angleRad)
|
|
420
|
+
return angle
|