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
|
@@ -0,0 +1,284 @@
|
|
|
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
|
+
Created on Sep 21, 2011
|
|
7
|
+
|
|
8
|
+
@author: hein_fa
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import math
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
from numpy.linalg import inv
|
|
15
|
+
|
|
16
|
+
from patme import epsilon
|
|
17
|
+
from patme.geometry.line import Line
|
|
18
|
+
from patme.geometry.misc import areVectorsParallel, getPointDistanceToLine
|
|
19
|
+
from patme.service.exceptions import ImproperParameterError, InternalError
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Intersection:
|
|
23
|
+
"""
|
|
24
|
+
This class is intended to provide the functionality to intersect two linear curves with each other
|
|
25
|
+
and to return the corresponding intersection point. The calculation of the intersection is limited to 2D
|
|
26
|
+
due to the fact that spars, ribs and stringer are located within the areodynamic loft. Therefore, they
|
|
27
|
+
categorical intersect each other. That means the z-coordinate of the points is dispensable for the
|
|
28
|
+
intersection calculation. The first two position vectors from the first linear curve and the second two
|
|
29
|
+
position vectors establish the second linear curve, respectively.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def getIntersection(lines=None, areas=None, plane="xy"):
|
|
36
|
+
"""doc"""
|
|
37
|
+
if lines is None:
|
|
38
|
+
lines = []
|
|
39
|
+
if areas is None:
|
|
40
|
+
areas = []
|
|
41
|
+
|
|
42
|
+
if len(lines) == 2:
|
|
43
|
+
return Intersection._intersectLines(*lines, plane=plane)
|
|
44
|
+
elif len(areas) == 2:
|
|
45
|
+
return Intersection._intersectAreas(*areas)
|
|
46
|
+
elif len(lines) == 1 and len(areas) == 1:
|
|
47
|
+
return Intersection._intersectLineArea(lines[0], areas[0])
|
|
48
|
+
else:
|
|
49
|
+
raise ImproperParameterError(
|
|
50
|
+
"The specified parameters don't fit the specification of intersection instance"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def _intersectLines(line1, line2, plane="xy"):
|
|
55
|
+
"""This method generates vectorial equations from specified initial vectors (vector1, ...vector4).
|
|
56
|
+
Afterwards, these two functions are intersected with each other. The resulting intersection point will be returned as result of this method.
|
|
57
|
+
The generated equations will look like: f: xVector = aVectror+phi*bVector ; g: xVector = cVectror+chi*dVector. Subsequently, the xVector of
|
|
58
|
+
f will be equalized with the xVector of g. The resulting coefficient matrix will be inverted and from left multiplied by the difference of aVector and cVector.
|
|
59
|
+
Following system of equation should appear:
|
|
60
|
+
aVector = vector1 ; bVector = vector2-vector1
|
|
61
|
+
cVector = vector3 ; dVector = vector4-vector3
|
|
62
|
+
|
|
63
|
+
|- -| |- -| |- -|
|
|
64
|
+
|bVector[0] -cVector[0]| |phi| |dVector[0]-aVector[0]|
|
|
65
|
+
|bVector[1] -cVector[1]| * |chi| = |dVector[1]-aVector[1]|
|
|
66
|
+
|- -| |- -| |- -|
|
|
67
|
+
->coeffMatrix ->vector function parameters ->difference of position vector
|
|
68
|
+
|
|
69
|
+
>>> from patme.geometry.translate import Translation
|
|
70
|
+
>>> line1 = Line(Translation( [0., 0., 0.] ), Translation( [1., 0., 0.] ))
|
|
71
|
+
>>> line2 = Line(Translation( [0., 0., 0.] ), Translation( [0., 1., 0.] ))
|
|
72
|
+
>>> Intersection._intersectLines(line1, line2)
|
|
73
|
+
Translation([0., 0., 0.])
|
|
74
|
+
>>> line2 = Line(Translation( [0., 0., 0.] ), Translation( [0., 0., 1.] ))
|
|
75
|
+
>>> Intersection._intersectLines(line1, line2, plane='xz')
|
|
76
|
+
Translation([0., 0., 0.])
|
|
77
|
+
>>> line2 = Line(Translation( [0., 0., 0.] ), Translation( [1., 0., 0.] ))
|
|
78
|
+
>>> line1 is Intersection._intersectLines(line1, line2)
|
|
79
|
+
True
|
|
80
|
+
>>> line2 = Line(Translation( [0., 1., 0.] ), Translation( [1., 1., 0.] ))
|
|
81
|
+
>>> print(Intersection._intersectLines(line1, line2))
|
|
82
|
+
None
|
|
83
|
+
"""
|
|
84
|
+
if plane not in ["xy", "xz", "yz"]:
|
|
85
|
+
raise ImproperParameterError("plane not implemented: " + plane)
|
|
86
|
+
else:
|
|
87
|
+
coeff11 = getattr(line1.p2, plane[0]) - getattr(line1.p1, plane[0])
|
|
88
|
+
coeff21 = getattr(line1.p2, plane[1]) - getattr(line1.p1, plane[1])
|
|
89
|
+
coeff12 = getattr(line2.p2, plane[0]) - getattr(line2.p1, plane[0])
|
|
90
|
+
coeff22 = getattr(line2.p2, plane[1]) - getattr(line2.p1, plane[1])
|
|
91
|
+
|
|
92
|
+
if np.linalg.norm(np.cross([coeff11, coeff21], [coeff12, coeff22])) < epsilon:
|
|
93
|
+
# line is parallel
|
|
94
|
+
distance = getPointDistanceToLine(line1.p1, line1.p2, line2.p1)
|
|
95
|
+
if distance < epsilon:
|
|
96
|
+
return line1
|
|
97
|
+
else:
|
|
98
|
+
return None
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
coeffMatrix = np.array([[coeff11, -coeff12], [coeff21, -coeff22]])
|
|
102
|
+
coeffMatrixInv = inv(coeffMatrix)
|
|
103
|
+
except:
|
|
104
|
+
raise InternalError(
|
|
105
|
+
"Can't calculate intersection! The matrix to be inverted is singular! Either the two vectors are parallel or the specified data is corrupted."
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# ---CALCULATE UNKNOWN PARAMETERS
|
|
109
|
+
if plane == "xy":
|
|
110
|
+
vec = np.dot(coeffMatrixInv, (line2.p1 - line1.p1)[:2])
|
|
111
|
+
elif plane == "xz":
|
|
112
|
+
vec = np.dot(coeffMatrixInv, np.array([(line2.p1 - line1.p1)[0], (line2.p1 - line1.p1)[2]]))
|
|
113
|
+
elif plane == "yz":
|
|
114
|
+
vec = np.dot(coeffMatrixInv, (line2.p1 - line1.p1)[1:])
|
|
115
|
+
|
|
116
|
+
# ---USE ONE OF THE UNKNOWN PARAMETERS TO CALCULATE THE INTERSECTION POINT
|
|
117
|
+
return line1.p1 + vec[0] * (line1.p2 - line1.p1)
|
|
118
|
+
|
|
119
|
+
@staticmethod
|
|
120
|
+
def intersectLinesMethod2d(line1, line2):
|
|
121
|
+
"""method returns the intersection of two 2D lines with wing positions in xsi,eta coordinate system
|
|
122
|
+
|
|
123
|
+
The coordinates are in xy-plane. It can also handle lines that are parallel
|
|
124
|
+
|
|
125
|
+
returns a tuple: (xi, yi, valid, r, s), where
|
|
126
|
+
(xi, yi) is the intersection
|
|
127
|
+
r is the scalar multiple such that (xi,yi) = pt1 + r*(pt2-pt1)
|
|
128
|
+
s is the scalar multiple such that (xi,yi) = pt1 + s*(ptB-ptA)
|
|
129
|
+
valid == 0 if there are 0 or inf. intersections (invalid)
|
|
130
|
+
valid == 1 if it has a unique intersection ON the segment"""
|
|
131
|
+
|
|
132
|
+
# source: https://www.cs.hmc.edu/ACM/lectures/intersections.html
|
|
133
|
+
# method is used for spar crossing. _intersectLines method should be investigated if
|
|
134
|
+
# it can be used instead of this method for this purpose
|
|
135
|
+
|
|
136
|
+
# the first line is pt1 + r*(pt2-pt1)
|
|
137
|
+
# in component form:
|
|
138
|
+
x1, y1 = line1.p1.x, line1.p1.y
|
|
139
|
+
x2, y2 = line1.p2.x, line1.p2.y
|
|
140
|
+
dx1 = x2 - x1
|
|
141
|
+
dy1 = y2 - y1
|
|
142
|
+
|
|
143
|
+
# the second line is ptA + s*(ptB-ptA)
|
|
144
|
+
x, y = line2.p1.x, line2.p1.y
|
|
145
|
+
xB, yB = line2.p2.x, line2.p2.y
|
|
146
|
+
|
|
147
|
+
dx = xB - x
|
|
148
|
+
dy = yB - y
|
|
149
|
+
|
|
150
|
+
# we need to find the (typically unique) values of r and s
|
|
151
|
+
# that will satisfy
|
|
152
|
+
#
|
|
153
|
+
# (x1, y1) + r(dx1, dy1) = (x, y) + s(dx, dy)
|
|
154
|
+
#
|
|
155
|
+
# which is the same as
|
|
156
|
+
#
|
|
157
|
+
# [ dx1 -dx ][ r ] = [ x-x1 ]
|
|
158
|
+
# [ dy1 -dy ][ s ] = [ y-y1 ]
|
|
159
|
+
#
|
|
160
|
+
# whose solution is
|
|
161
|
+
#
|
|
162
|
+
# [ r ] = _1_ [ -dy dx ] [ x-x1 ]
|
|
163
|
+
# [ s ] = DET [ -dy1 dx1 ] [ y-y1 ]
|
|
164
|
+
#
|
|
165
|
+
# where DET = (-dx1 * dy + dy1 * dx)
|
|
166
|
+
#
|
|
167
|
+
# if DET is too small, they're parallel
|
|
168
|
+
#
|
|
169
|
+
DET = -dx1 * dy + dy1 * dx
|
|
170
|
+
|
|
171
|
+
if math.fabs(DET) < epsilon:
|
|
172
|
+
return (0, 0, 0, 0, 0)
|
|
173
|
+
|
|
174
|
+
# find the scalar amount along the "self" segment
|
|
175
|
+
r = (1.0 / DET) * (-dy * (x - x1) + dx * (y - y1))
|
|
176
|
+
|
|
177
|
+
# find the scalar amount along the input line
|
|
178
|
+
s = (1.0 / DET) * (-dy1 * (x - x1) + dx1 * (y - y1))
|
|
179
|
+
|
|
180
|
+
# return the average of the two descriptions
|
|
181
|
+
xi = (x1 + r * dx1 + x + s * dx) / 2.0
|
|
182
|
+
yi = (y1 + r * dy1 + y + s * dy) / 2.0
|
|
183
|
+
return (xi, yi, 1, r, s)
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def _intersectAreas(plane1, plane2):
|
|
187
|
+
"""Calculate intersection of two planes.
|
|
188
|
+
|
|
189
|
+
:return: If the planes cross each other, a line instance is returned. If the
|
|
190
|
+
planes are coincident, the first plane is returned. If the planes have the same
|
|
191
|
+
normal direction but the planes do not coincide, None is returned.
|
|
192
|
+
|
|
193
|
+
>>> from patme.geometry.plane import Plane
|
|
194
|
+
>>> plane1, plane2 = Plane(), Plane()
|
|
195
|
+
>>> plane1 = plane1.generatePlane([0,0,0], planeNormalVector = [0,0,1])
|
|
196
|
+
>>> plane2 = plane2.generatePlane([0,0,0], planeNormalVector = [0,1,0])
|
|
197
|
+
>>> print(Intersection._intersectAreas(plane1, plane2))
|
|
198
|
+
[0. 0. 0.], [-1. 0. 0.]
|
|
199
|
+
>>> plane2 = plane2.generatePlane([0,0,0], planeNormalVector = [0,0,1])
|
|
200
|
+
>>> plane1 is Intersection._intersectAreas(plane1, plane2)
|
|
201
|
+
True
|
|
202
|
+
>>> plane2 = plane2.generatePlane([0,0,1], planeNormalVector = [0,0,1])
|
|
203
|
+
>>> print(Intersection._intersectAreas(plane1, plane2))
|
|
204
|
+
None
|
|
205
|
+
"""
|
|
206
|
+
# get direction of resulting line
|
|
207
|
+
directionVector = np.cross(plane1.planeNormalVector, plane2.planeNormalVector)
|
|
208
|
+
if np.linalg.norm(directionVector) < epsilon:
|
|
209
|
+
# normal vectors are parallel to each other
|
|
210
|
+
if plane1.getDistanceToPoint(plane2.planePositioningVector) < epsilon:
|
|
211
|
+
# planes are coincident
|
|
212
|
+
return plane1
|
|
213
|
+
else:
|
|
214
|
+
return None
|
|
215
|
+
|
|
216
|
+
# =======================================================================
|
|
217
|
+
# get base vector of the desired line
|
|
218
|
+
# This is done by using the plane orientation vector that is not parallel to the other plane.
|
|
219
|
+
# Then the intersection of a line with this orientation vector and the other plane is calculated.
|
|
220
|
+
# =======================================================================
|
|
221
|
+
|
|
222
|
+
lineToIntersect = Line(
|
|
223
|
+
plane1.planePositioningVector,
|
|
224
|
+
plane1.planePositioningVector + plane1.planeOrientationVector1,
|
|
225
|
+
)
|
|
226
|
+
intersectionPoint = Intersection.getIntersection([lineToIntersect], [plane2])
|
|
227
|
+
if intersectionPoint is None or intersectionPoint is lineToIntersect:
|
|
228
|
+
# orientation vector1 is parallel to the other plane
|
|
229
|
+
lineToIntersect = Line(
|
|
230
|
+
plane1.planePositioningVector,
|
|
231
|
+
plane1.planePositioningVector + plane1.planeOrientationVector2,
|
|
232
|
+
)
|
|
233
|
+
intersectionPoint = Intersection.getIntersection([lineToIntersect], [plane2])
|
|
234
|
+
|
|
235
|
+
return Line(intersectionPoint, (intersectionPoint + directionVector))
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def _intersectLineArea(line, plane):
|
|
239
|
+
"""doc
|
|
240
|
+
|
|
241
|
+
>>> from patme.geometry.translate import Translation
|
|
242
|
+
>>> from patme.geometry.plane import Plane
|
|
243
|
+
>>> plane = Plane()
|
|
244
|
+
>>> plane = plane.generatePlane([0,0,0], planeNormalVector = [0,0,1])
|
|
245
|
+
>>> Intersection._intersectLineArea(Line(Translation([0,0,0]),Translation([0,0,1])), plane)
|
|
246
|
+
Translation([0., 0., 0.])
|
|
247
|
+
>>> print(Intersection._intersectLineArea(Line(Translation([0,0,1]),Translation([1,0,1])), plane))
|
|
248
|
+
None
|
|
249
|
+
>>> line = Line(Translation([0,0,0]),Translation([1,0,0]))
|
|
250
|
+
>>> line is Intersection._intersectLineArea(line, plane)
|
|
251
|
+
True
|
|
252
|
+
"""
|
|
253
|
+
# check if line is parallel to plane
|
|
254
|
+
usedVector = (
|
|
255
|
+
plane.planeOrientationVector2
|
|
256
|
+
if areVectorsParallel(plane.planeOrientationVector1, line.vector)
|
|
257
|
+
else plane.planeOrientationVector1
|
|
258
|
+
)
|
|
259
|
+
crossProduct = np.cross(usedVector, line.vector)
|
|
260
|
+
|
|
261
|
+
if areVectorsParallel(crossProduct, plane.planeNormalVector):
|
|
262
|
+
# line is parallel to plane
|
|
263
|
+
distance = np.linalg.norm(
|
|
264
|
+
np.dot(plane.planeNormalVector, (line.p1 - plane.planePositioningVector))
|
|
265
|
+
) / np.linalg.norm(plane.planeNormalVector)
|
|
266
|
+
if -1.0 * epsilon <= distance <= epsilon:
|
|
267
|
+
# Line lays within plane. Infinite number of intersection point calculable.
|
|
268
|
+
return line
|
|
269
|
+
else:
|
|
270
|
+
return None
|
|
271
|
+
else:
|
|
272
|
+
phi = np.dot(plane.planeNormalVector, (plane.planePositioningVector - line.p1)) / np.dot(
|
|
273
|
+
plane.planeNormalVector, line.vector
|
|
274
|
+
)
|
|
275
|
+
intersectionPoint = line.p1 + phi * line.vector
|
|
276
|
+
|
|
277
|
+
return intersectionPoint
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
if __name__ == "__main__":
|
|
281
|
+
|
|
282
|
+
import doctest
|
|
283
|
+
|
|
284
|
+
doctest.testmod()
|
patme/geometry/line.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
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
|
+
from operator import attrgetter
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
from patme import epsilon
|
|
12
|
+
from patme.geometry.translate import Translation
|
|
13
|
+
from patme.service.exceptions import InternalError
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Lines(list):
|
|
17
|
+
"""doc"""
|
|
18
|
+
|
|
19
|
+
def sort(self, cmp=None, key=None, reverse=False, attribute=None):
|
|
20
|
+
"""doc"""
|
|
21
|
+
if key == None:
|
|
22
|
+
try:
|
|
23
|
+
# make attribute an iterable item if needed
|
|
24
|
+
a = attribute[0]
|
|
25
|
+
if isinstance(attribute, str):
|
|
26
|
+
raise
|
|
27
|
+
except:
|
|
28
|
+
attribute = [attribute]
|
|
29
|
+
key = attrgetter(*attribute)
|
|
30
|
+
list.sort(self, key=key, reverse=reverse)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Line:
|
|
34
|
+
"""classdocs"""
|
|
35
|
+
|
|
36
|
+
# Attention: when changing parameters, please also edit them in model.mechanicalproperties.Beam.__init__
|
|
37
|
+
def __init__(self, point1=None, point2=None, lineID=None, doZeroLengthCheck=True):
|
|
38
|
+
"""Creates a line based on two points
|
|
39
|
+
|
|
40
|
+
:param point1: first point, should be instance of model.geometry.translate.Translation
|
|
41
|
+
:param point2: first point, should be instance of model.geometry.translate.Translation
|
|
42
|
+
:param lineID: id of the point used in FEM
|
|
43
|
+
:param connectVertex: Flag if common neighbors of point1 and point2 should be connected with the new line
|
|
44
|
+
:param doZeroLengthCheck: geometrical lines should not have a length of zero. Hence an exception is risen in this case.
|
|
45
|
+
If a length of zero is on purpose (e.g. to constrain two nodes), this flag should be set to False
|
|
46
|
+
"""
|
|
47
|
+
# init ac graph vertex
|
|
48
|
+
if point1 is point2:
|
|
49
|
+
raise InternalError("The same points are given as point1 and point2.")
|
|
50
|
+
|
|
51
|
+
self._keypoints = []
|
|
52
|
+
if point1 is not None and point2 is not None:
|
|
53
|
+
self._keypoints = [point1, point2]
|
|
54
|
+
|
|
55
|
+
# check if lines have a length higher than zero
|
|
56
|
+
if doZeroLengthCheck and self._keypoints and self.length < epsilon:
|
|
57
|
+
raise InternalError(f"The given line has a length of zero! line, length: {lineID}, {self.length}")
|
|
58
|
+
|
|
59
|
+
self.id = lineID
|
|
60
|
+
self.cutout = None
|
|
61
|
+
|
|
62
|
+
def copy(self, old2NewDict, copyProperties):
|
|
63
|
+
"""returns a copy of this instance.
|
|
64
|
+
:param old2NewDict: references attributes of self with the newly created instances to
|
|
65
|
+
reestablish the connections between instances
|
|
66
|
+
:param copyProperties: Flag if sheetproperties and profiles should be copied or
|
|
67
|
+
if the references should be kept.
|
|
68
|
+
"""
|
|
69
|
+
return self.__class__(old2NewDict[self.p1], old2NewDict[self.p2], lineID=self.id)
|
|
70
|
+
|
|
71
|
+
def __str__(self):
|
|
72
|
+
"""doc"""
|
|
73
|
+
if self._keypoints:
|
|
74
|
+
return ", ".join(str(k) for k in self._keypoints)
|
|
75
|
+
else:
|
|
76
|
+
return ""
|
|
77
|
+
|
|
78
|
+
def _getp1(self):
|
|
79
|
+
"""doc"""
|
|
80
|
+
self._checkKeypoints()
|
|
81
|
+
return self.keypoints[0]
|
|
82
|
+
|
|
83
|
+
def _getp2(self):
|
|
84
|
+
"""doc"""
|
|
85
|
+
self._checkKeypoints()
|
|
86
|
+
return self.keypoints[-1]
|
|
87
|
+
|
|
88
|
+
def _getxLesser(self):
|
|
89
|
+
"""Returns the lesser x-coordinate of the two keypoints."""
|
|
90
|
+
self._checkKeypoints()
|
|
91
|
+
return min(self.p1.x, self.p2.x)
|
|
92
|
+
|
|
93
|
+
def _getyLesser(self):
|
|
94
|
+
"""Returns the lesser y-coordinate of the two keypoints."""
|
|
95
|
+
self._checkKeypoints()
|
|
96
|
+
return min(self.p1.y, self.p2.y)
|
|
97
|
+
|
|
98
|
+
def _getzLesser(self):
|
|
99
|
+
"""Returns the lesser z-coordinate of the two keypoints."""
|
|
100
|
+
self._checkKeypoints()
|
|
101
|
+
return min(self.p1.z, self.p2.z)
|
|
102
|
+
|
|
103
|
+
def _getxGreater(self):
|
|
104
|
+
"""Returns the greater x-coordinate of the two keypoints."""
|
|
105
|
+
self._checkKeypoints()
|
|
106
|
+
return max(self.p1.x, self.p2.x)
|
|
107
|
+
|
|
108
|
+
def _getyGreater(self):
|
|
109
|
+
"""Returns the greater y-coordinate of the two keypoints."""
|
|
110
|
+
self._checkKeypoints()
|
|
111
|
+
return max(self.p1.y, self.p2.y)
|
|
112
|
+
|
|
113
|
+
def _getzGreater(self):
|
|
114
|
+
"""Returns the greater z-coordinate of the two keypoints."""
|
|
115
|
+
self._checkKeypoints()
|
|
116
|
+
return max(self.p1.z, self.p2.z)
|
|
117
|
+
|
|
118
|
+
def _getLength(self):
|
|
119
|
+
"""doc"""
|
|
120
|
+
return self.p1.distance(self.p2)
|
|
121
|
+
|
|
122
|
+
def _getKeypoints(self):
|
|
123
|
+
"""doc"""
|
|
124
|
+
return self._keypoints
|
|
125
|
+
|
|
126
|
+
def _checkKeypoints(self):
|
|
127
|
+
"""This method raises an InternalError if there are not exactly 2 keypoints."""
|
|
128
|
+
if len(self.keypoints) != 2:
|
|
129
|
+
raise InternalError("There are not exactly 2 keypoints for line %s" % repr(self))
|
|
130
|
+
|
|
131
|
+
def _getVector(self):
|
|
132
|
+
"""Retruns the vector of the line as p2=p1+vector"""
|
|
133
|
+
return self.p2 - self.p1
|
|
134
|
+
|
|
135
|
+
def _getNormalizedVector(self):
|
|
136
|
+
"""Retruns the vector of the line as p2=p1+vector"""
|
|
137
|
+
vector = self.vector
|
|
138
|
+
return vector / np.linalg.norm(vector)
|
|
139
|
+
|
|
140
|
+
def _getMeanPoint(self):
|
|
141
|
+
"""Retruns the mean of both points: mean(self.p1, self.p2)"""
|
|
142
|
+
return Translation(np.mean([self.p2, self.p1], 0))
|
|
143
|
+
|
|
144
|
+
keypoints = property(fget=_getKeypoints)
|
|
145
|
+
p1 = property(fget=_getp1)
|
|
146
|
+
p2 = property(fget=_getp2)
|
|
147
|
+
xLesser = property(fget=_getxLesser)
|
|
148
|
+
"""Returns the lesser x-coordinate of the two keypoints."""
|
|
149
|
+
yLesser = property(fget=_getyLesser)
|
|
150
|
+
"""Returns the lesser y-coordinate of the two keypoints."""
|
|
151
|
+
zLesser = property(fget=_getzLesser)
|
|
152
|
+
"""Returns the lesser z-coordinate of the two keypoints."""
|
|
153
|
+
xGreater = property(fget=_getxGreater)
|
|
154
|
+
"""Returns the greater x-coordinate of the two keypoints."""
|
|
155
|
+
yGreater = property(fget=_getyGreater)
|
|
156
|
+
"""Returns the greater y-coordinate of the two keypoints."""
|
|
157
|
+
zGreater = property(fget=_getzGreater)
|
|
158
|
+
"""Returns the greater z-coordinate of the two keypoints."""
|
|
159
|
+
length = property(fget=_getLength)
|
|
160
|
+
"""Returns the length of the line (2-norm)"""
|
|
161
|
+
vector = property(fget=_getVector)
|
|
162
|
+
"""Returns the vector of the line as p2=p1+vector"""
|
|
163
|
+
meanPoint = property(fget=_getMeanPoint)
|
|
164
|
+
"""Returns the mean of the two points: mean(self.p1, self.p2)"""
|
|
165
|
+
normalizedVector = property(fget=_getNormalizedVector)
|
|
166
|
+
"""Returns the mean of the two points: mean(self.p1, self.p2)"""
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def computePointsOnBezierCurve(bezierKnots, s_vals):
|
|
170
|
+
"""doc"""
|
|
171
|
+
from scipy.special import binom
|
|
172
|
+
|
|
173
|
+
npbezierKnots = np.array(bezierKnots)
|
|
174
|
+
degree = npbezierKnots.shape[0] - 1
|
|
175
|
+
|
|
176
|
+
kVals = np.arange(degree + 1)
|
|
177
|
+
tmp = np.power(1 - s_vals[:, np.newaxis], degree - kVals)
|
|
178
|
+
tmp2 = np.power(s_vals[:, np.newaxis], kVals)
|
|
179
|
+
|
|
180
|
+
binomStuff = binom([degree] * kVals.size, kVals)
|
|
181
|
+
res = binomStuff * tmp * tmp2
|
|
182
|
+
evaluatedPoints = np.dot(npbezierKnots.T, res.T).T
|
|
183
|
+
return evaluatedPoints
|