engeom 0.2.5__cp38-abi3-macosx_11_0_arm64.whl → 0.2.6__cp38-abi3-macosx_11_0_arm64.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.
- engeom/_plot/__init__.py +1 -0
- engeom/_plot/common.py +17 -0
- engeom/{matplotlib.py → _plot/matplotlib.py} +88 -45
- engeom/_plot/pyvista.py +256 -0
- engeom/airfoil/__init__.py +4 -0
- engeom/airfoil.pyi +202 -51
- engeom/engeom.abi3.so +0 -0
- engeom/engeom.pyi +35 -2
- engeom/geom2/__init__.py +5 -0
- engeom/geom2.pyi +323 -85
- engeom/geom3/__init__.py +5 -0
- engeom/geom3.pyi +406 -117
- engeom/metrology/__init__.py +4 -0
- engeom/metrology.pyi +85 -12
- engeom/plot.py +26 -0
- {engeom-0.2.5.dist-info → engeom-0.2.6.dist-info}/METADATA +1 -1
- engeom-0.2.6.dist-info/RECORD +21 -0
- engeom/pyvista.py +0 -178
- engeom-0.2.5.dist-info/RECORD +0 -18
- {engeom-0.2.5.dist-info → engeom-0.2.6.dist-info}/WHEEL +0 -0
engeom/geom3.pyi
CHANGED
@@ -5,14 +5,26 @@ from typing import Tuple, Iterable, List, TypeVar, Iterator, Any
|
|
5
5
|
|
6
6
|
import numpy
|
7
7
|
from numpy.typing import NDArray
|
8
|
-
|
9
|
-
|
8
|
+
import engeom
|
9
|
+
import metrology
|
10
10
|
|
11
11
|
Transformable3 = TypeVar("Transformable3", Vector3, Point3, Plane3, Iso3, SurfacePoint3)
|
12
12
|
PointOrVector3 = TypeVar("PointOrVector3", Vector3, Point3)
|
13
13
|
|
14
14
|
|
15
15
|
class Vector3(Iterable[float]):
|
16
|
+
"""
|
17
|
+
A class representing a vector in 3D space. The vector is represented by its x, y, and z components. It is
|
18
|
+
iterable and will yield the x, y, and z components in that order, allowing the Python unpacking operator `*` to be
|
19
|
+
used to compensate for the lack of function overloading in other parts of the library.
|
20
|
+
|
21
|
+
A vector supports a number of mathematical operations, including addition, subtraction, scalar multiplication,
|
22
|
+
dot and cross products, and normalization. It also supports transformation by isometry.
|
23
|
+
|
24
|
+
Vectors have different semantics than points when it comes to transformations and some mathematical operations. Be
|
25
|
+
sure to use the type which matches the conceptual use of the object in your code.
|
26
|
+
"""
|
27
|
+
|
16
28
|
def __init__(self, x: float, y: float, z: float):
|
17
29
|
"""
|
18
30
|
Create a vector in 3D space by specifying the x, y, and z components.
|
@@ -24,40 +36,80 @@ class Vector3(Iterable[float]):
|
|
24
36
|
|
25
37
|
@property
|
26
38
|
def x(self) -> float:
|
39
|
+
"""
|
40
|
+
Get the x component of the vector as a floating point value.
|
41
|
+
"""
|
27
42
|
...
|
28
43
|
|
29
44
|
@property
|
30
45
|
def y(self) -> float:
|
46
|
+
"""
|
47
|
+
Get the y component of the vector as a floating point value.
|
48
|
+
"""
|
31
49
|
...
|
32
50
|
|
33
51
|
@property
|
34
52
|
def z(self) -> float:
|
53
|
+
"""
|
54
|
+
Get the z component of the vector as a floating point value.
|
55
|
+
"""
|
35
56
|
...
|
36
57
|
|
37
58
|
def __iter__(self) -> Iterator[float]:
|
38
59
|
...
|
39
60
|
|
40
61
|
def __rmul__(self, other: float) -> Vector3:
|
62
|
+
"""
|
63
|
+
Multiply the vector by a scalar value. This allows the scalar to be on the left side of the multiplication
|
64
|
+
operator.
|
65
|
+
:param other: the scalar value to multiply the vector by.
|
66
|
+
:return: a new vector that is the result of the multiplication.
|
67
|
+
"""
|
41
68
|
...
|
42
69
|
|
43
70
|
def __add__(self, other: PointOrVector3) -> PointOrVector3:
|
71
|
+
"""
|
72
|
+
Add a vector to another vector or a point. Adding a vector to a point will return a new point, and adding a
|
73
|
+
vector to a vector will return a new vector.
|
74
|
+
:param other: the other vector or point to add to this vector.
|
75
|
+
:return: a new vector or point that is the result of the addition.
|
76
|
+
"""
|
44
77
|
...
|
45
78
|
|
46
79
|
def __sub__(self, other: Vector3) -> Vector3:
|
80
|
+
"""
|
81
|
+
Subtract another vector from this vector.
|
82
|
+
:param other: the other vector to subtract from this vector.
|
83
|
+
:return: a new vector that is the result of the subtraction.
|
84
|
+
"""
|
47
85
|
...
|
48
86
|
|
49
87
|
def __neg__(self) -> Vector3:
|
88
|
+
"""
|
89
|
+
Invert the vector by negating all of its components.
|
90
|
+
:return: a new vector in which the x, y, and z components are negated.
|
91
|
+
"""
|
50
92
|
...
|
51
93
|
|
52
|
-
def __mul__(self,
|
94
|
+
def __mul__(self, other: float) -> Vector3:
|
95
|
+
"""
|
96
|
+
Multiply the vector by a scalar value.
|
97
|
+
:param other: the scalar value to multiply the vector by.
|
98
|
+
:return: a new vector that is the result of the multiplication.
|
99
|
+
"""
|
53
100
|
...
|
54
101
|
|
55
|
-
def __truediv__(self,
|
102
|
+
def __truediv__(self, other: float) -> Vector3:
|
103
|
+
"""
|
104
|
+
Divide the vector by a scalar value.
|
105
|
+
:param other: the scalar value to divide the vector by.
|
106
|
+
:return: a new vector that is the result of the division.
|
107
|
+
"""
|
56
108
|
...
|
57
109
|
|
58
110
|
def as_numpy(self) -> NDArray[float]:
|
59
111
|
"""
|
60
|
-
Create a numpy array of shape (3,) from the vector.
|
112
|
+
Create a numpy array of shape (3, ) from the vector.
|
61
113
|
"""
|
62
114
|
...
|
63
115
|
|
@@ -79,19 +131,20 @@ class Vector3(Iterable[float]):
|
|
79
131
|
|
80
132
|
def norm(self) -> float:
|
81
133
|
"""
|
82
|
-
Calculate the norm (length) of the vector.
|
83
|
-
:return:
|
134
|
+
Calculate the Euclidian norm (aka magnitude, length) of the vector.
|
135
|
+
:return: the length of the vector as a floating point value.
|
84
136
|
"""
|
85
137
|
|
86
138
|
def normalized(self) -> Vector3:
|
87
139
|
"""
|
88
|
-
Return a normalized version of the vector.
|
140
|
+
Return a normalized version of the vector. The normalized vector will have the same direction as the original
|
141
|
+
vector, but will have a length of 1.
|
89
142
|
:return: a new vector that has unit length
|
90
143
|
"""
|
91
144
|
|
92
145
|
def angle_to(self, other: Vector3) -> float:
|
93
146
|
"""
|
94
|
-
Calculate the smallest angle between this vector and another vector.
|
147
|
+
Calculate the smallest angle between this vector and another vector and return it in radians.
|
95
148
|
:param other: the other vector to calculate the angle to.
|
96
149
|
:return: the angle between the two vectors in radians.
|
97
150
|
"""
|
@@ -99,6 +152,18 @@ class Vector3(Iterable[float]):
|
|
99
152
|
|
100
153
|
|
101
154
|
class Point3(Iterable[float]):
|
155
|
+
"""
|
156
|
+
A class representing a point in 3D space. The point is represented by its x, y, and z coordinates. It is iterable
|
157
|
+
and will yield the x, y, and z coordinates in that order, allowing the Python unpacking operator `*` to be used to
|
158
|
+
compensate for the lack of function overloading in other parts of the library.
|
159
|
+
|
160
|
+
A point supports a number of mathematical operations, including addition and subtraction with vectors, subtraction
|
161
|
+
with other points, and scaling by a scalar value. It also supports transformation by isometry.
|
162
|
+
|
163
|
+
Points have different semantics than vectors when it comes to transformations and some mathematical operations. Be
|
164
|
+
sure to use the type which matches the conceptual use of the object in your code.
|
165
|
+
"""
|
166
|
+
|
102
167
|
def __init__(self, x: float, y: float, z: float):
|
103
168
|
"""
|
104
169
|
Create a point in 3D space by specifying the x, y, and z coordinates.
|
@@ -111,14 +176,23 @@ class Point3(Iterable[float]):
|
|
111
176
|
|
112
177
|
@property
|
113
178
|
def x(self) -> float:
|
179
|
+
"""
|
180
|
+
Get the x coordinate of the point as a floating point value.
|
181
|
+
"""
|
114
182
|
...
|
115
183
|
|
116
184
|
@property
|
117
185
|
def y(self) -> float:
|
186
|
+
"""
|
187
|
+
Get the y coordinate of the point as a floating point value.
|
188
|
+
"""
|
118
189
|
...
|
119
190
|
|
120
191
|
@property
|
121
192
|
def z(self) -> float:
|
193
|
+
"""
|
194
|
+
Get the z coordinate of the point as a floating point value.
|
195
|
+
"""
|
122
196
|
...
|
123
197
|
|
124
198
|
def __iter__(self) -> Iterator[float]:
|
@@ -133,31 +207,70 @@ class Point3(Iterable[float]):
|
|
133
207
|
...
|
134
208
|
|
135
209
|
def __sub__(self, other: PointOrVector3) -> PointOrVector3:
|
210
|
+
"""
|
211
|
+
Subtract a vector from a point to get a new point, or subtract a point from a point to get a new vector.
|
212
|
+
:param other: the other point or vector to subtract from this point.
|
213
|
+
:return: a new point or vector that is the result of the subtraction.
|
214
|
+
"""
|
136
215
|
...
|
137
216
|
|
138
217
|
def __add__(self, other: Vector3) -> Vector3:
|
218
|
+
"""
|
219
|
+
Add a vector to a point to get a new point.
|
220
|
+
:param other: the vector to add to this point.
|
221
|
+
:return: a new point that is the result of the addition.
|
222
|
+
"""
|
139
223
|
...
|
140
224
|
|
141
225
|
def __neg__(self) -> Point3:
|
226
|
+
"""
|
227
|
+
Invert the point by negating all of its components.
|
228
|
+
:return: a new point in which the x, y, and z components are negated
|
229
|
+
"""
|
142
230
|
...
|
143
231
|
|
144
|
-
def __mul__(self,
|
232
|
+
def __mul__(self, other: float) -> Point3:
|
233
|
+
"""
|
234
|
+
Multiply the coordinates of the point by a scalar value.
|
235
|
+
:param other: the scalar value to multiply the point by.
|
236
|
+
:return: a new point that is the result of the multiplication.
|
237
|
+
"""
|
145
238
|
...
|
146
239
|
|
147
|
-
def __rmul__(self,
|
240
|
+
def __rmul__(self, other: float) -> Point3:
|
241
|
+
"""
|
242
|
+
Multiply the coordinates of the point by a scalar value. This allows the scalar to be on the left side of the
|
243
|
+
multiplication operator.
|
244
|
+
:param other: the scalar value to multiply the point by.
|
245
|
+
:return: a new point that is the result of the multiplication.
|
246
|
+
"""
|
148
247
|
...
|
149
248
|
|
150
|
-
def __truediv__(self,
|
249
|
+
def __truediv__(self, other: float) -> Point3:
|
250
|
+
"""
|
251
|
+
Divide the coordinates of the point by a scalar value.
|
252
|
+
:param other: the scalar value to divide the point by.
|
253
|
+
:return: a new point that is the result of the division.
|
254
|
+
"""
|
151
255
|
...
|
152
256
|
|
153
257
|
def as_numpy(self) -> NDArray[float]:
|
154
258
|
"""
|
155
|
-
Create a numpy array of shape (2,) from the point.
|
259
|
+
Create a numpy array of shape (2, ) from the point.
|
156
260
|
"""
|
157
261
|
...
|
158
262
|
|
159
263
|
|
160
264
|
class SurfacePoint3:
|
265
|
+
"""
|
266
|
+
This class is used to represent a surface point in 3D space.
|
267
|
+
|
268
|
+
Surface points are a composite structure that consist of a point in space and a normal direction. Conceptually, they
|
269
|
+
come from metrology as a means of representing a point on the surface of an object along with the normal direction
|
270
|
+
of the surface at that point. However, they are also isomorphic with the concept of a ray or a parameterized line
|
271
|
+
with a direction of unit length, and can be used in that way as well.
|
272
|
+
"""
|
273
|
+
|
161
274
|
def __init__(self, x: float, y: float, z: float, nx: float, ny: float, nz: float):
|
162
275
|
"""
|
163
276
|
Create a surface point in 3D space by specifying the x, y, and z coordinates of the point, as well as the x, y,
|
@@ -278,10 +391,25 @@ class SurfacePoint3:
|
|
278
391
|
|
279
392
|
|
280
393
|
class Iso3:
|
281
|
-
"""
|
394
|
+
"""
|
395
|
+
A class representing an isometry in 3D space. An isometry is a transformation that preserves distances and angles,
|
396
|
+
and is also sometimes known as a rigid body transformation. It is composed of a translation and a rotation, with
|
397
|
+
the rotation part being internally represented by a unit quaternion.
|
398
|
+
|
399
|
+
`Iso3` objects can be used to transform 3D points, vectors, surface points, other isometries, and a few other types
|
400
|
+
of objects. They can also be inverted and decomposed.
|
401
|
+
"""
|
282
402
|
|
283
403
|
def __init__(self, matrix: NDArray[float]):
|
284
|
-
"""
|
404
|
+
"""
|
405
|
+
Attempt to create an isometry from a 4x4 matrix in the form of a numpy array. If the matrix is not a valid
|
406
|
+
isometry (it is not orthogonal, it has scale or shear, etc.), an exception will be raised.
|
407
|
+
|
408
|
+
Use this method if you explicitly have a known matrix to convert to an isometry, otherwise consider using a
|
409
|
+
composition of the `from_translation` and `from_rotation` methods.
|
410
|
+
|
411
|
+
:param matrix: a numpy array of shape (4, 4) containing the matrix representation of the isometry.
|
412
|
+
"""
|
285
413
|
...
|
286
414
|
|
287
415
|
@staticmethod
|
@@ -291,18 +419,28 @@ class Iso3:
|
|
291
419
|
|
292
420
|
@staticmethod
|
293
421
|
def from_translation(x: float, y: float, z: float) -> Iso3:
|
294
|
-
"""
|
422
|
+
"""
|
423
|
+
Create an isometry representing a translation by the specified x, y, and z components.
|
424
|
+
:param x: the x component of the translation.
|
425
|
+
:param y: the y component of the translation.
|
426
|
+
:param z: the z component of the translation.
|
427
|
+
:return: an isometry containing only a translation component
|
428
|
+
"""
|
295
429
|
...
|
296
430
|
|
297
431
|
@staticmethod
|
298
|
-
def from_rotation(angle: float,
|
432
|
+
def from_rotation(angle: float, ax: float, ay: float, az: float) -> Iso3:
|
299
433
|
"""
|
300
|
-
Create an isometry representing a rotation around an axis
|
301
|
-
applied.
|
434
|
+
Create an isometry representing a rotation around an axis defined by a vector direction and the origin. The
|
435
|
+
components of the direction will be automatically normalized before the rotation applied.
|
436
|
+
|
437
|
+
When looking down the axis of rotation (the axis is pointing towards the observer), the rotation will be
|
438
|
+
counter-clockwise.
|
439
|
+
|
302
440
|
:param angle: the angle to rotate by in radians.
|
303
|
-
:param
|
304
|
-
:param
|
305
|
-
:param
|
441
|
+
:param ax: the x component of the rotation axis.
|
442
|
+
:param ay: the y component of the rotation axis.
|
443
|
+
:param az: the z component of the rotation axis.
|
306
444
|
:return: the isometry representing the rotation.
|
307
445
|
"""
|
308
446
|
...
|
@@ -316,51 +454,84 @@ class Iso3:
|
|
316
454
|
...
|
317
455
|
|
318
456
|
def inverse(self) -> Iso3:
|
319
|
-
"""
|
457
|
+
"""
|
458
|
+
Get the inverse of the isometry. The inverse is the isometry that will undo the transformation of the original
|
459
|
+
isometry, or the isometry that when applied to the original isometry will return the identity isometry.
|
460
|
+
"""
|
320
461
|
...
|
321
462
|
|
322
463
|
def transform_points(self, points: NDArray[float]) -> NDArray[float]:
|
323
|
-
"""
|
324
|
-
of the isometry.
|
464
|
+
"""
|
465
|
+
Transform an array of points using the isometry. The semantics of transforming points are such that the full
|
466
|
+
matrix is applied, first rotating the point around the origin and then translating it by the translation vector.
|
467
|
+
|
468
|
+
To transform vectors, use the `transform_vectors` method instead.
|
325
469
|
|
326
|
-
|
327
|
-
|
470
|
+
This is an efficient way to transform a large number of points at once, rather than using the `@` operator
|
471
|
+
individually on a large number of `Point3` objects.
|
472
|
+
|
473
|
+
:param points: a numpy array of shape (N, 3)
|
474
|
+
:return: a numpy array of shape (N, 3) containing the transformed points in the same order as the input.
|
328
475
|
"""
|
329
476
|
...
|
330
477
|
|
331
|
-
def transform_vectors(self,
|
332
|
-
"""
|
333
|
-
|
478
|
+
def transform_vectors(self, vectors: NDArray[float]) -> NDArray[float]:
|
479
|
+
"""
|
480
|
+
Transform an array of vectors using the isometry. The semantics of transforming vectors are such that only the
|
481
|
+
rotation matrix is applied, and the translation vector is not used. The vectors retain their original
|
482
|
+
magnitude, but their direction is rotated by the isometry.
|
483
|
+
|
484
|
+
To transform points, use the `transform_points` method instead.
|
334
485
|
|
335
|
-
|
336
|
-
|
486
|
+
This is an efficient way to transform a large number of vectors at once, rather than using the `@` operator
|
487
|
+
individually on a large number of `Vector3` objects.
|
488
|
+
|
489
|
+
:param vectors: a numpy array of shape (N, 3)
|
490
|
+
:return: a numpy array of shape (N, 3) containing the transformed vectors in the same order as the input.
|
337
491
|
"""
|
338
492
|
...
|
339
493
|
|
340
494
|
def as_numpy(self) -> NDArray[float]:
|
341
|
-
"""
|
495
|
+
"""
|
496
|
+
Return a copy of the 4x4 matrix representation of the isometry.
|
497
|
+
"""
|
342
498
|
...
|
343
499
|
|
344
500
|
def flip_around_x(self) -> Iso3:
|
345
|
-
"""
|
346
|
-
|
501
|
+
"""
|
502
|
+
Return a new isometry that flips the isometry 180° around the x-axis. The origin of the isometry will be
|
503
|
+
preserved, but the y and z axes will point in the opposite directions.
|
504
|
+
:return: a new isometry that is the result of the flip.
|
505
|
+
"""
|
347
506
|
...
|
348
507
|
|
349
508
|
def flip_around_y(self) -> Iso3:
|
350
|
-
"""
|
351
|
-
|
509
|
+
"""
|
510
|
+
Return a new isometry that flips the isometry 180° around the y-axis. The origin of the isometry will be
|
511
|
+
preserved, but the x and z axes will point in the opposite directions.
|
512
|
+
:return: a new isometry that is the result of the flip.
|
513
|
+
"""
|
352
514
|
...
|
353
515
|
|
354
516
|
def flip_around_z(self) -> Iso3:
|
355
|
-
"""
|
356
|
-
|
517
|
+
"""
|
518
|
+
Return a new isometry that flips the isometry 180° around the z-axis. The origin of the isometry will be
|
519
|
+
preserved, but the x and y axes will point in the opposite directions.
|
520
|
+
:return: a new isometry that is the result of the flip.
|
521
|
+
"""
|
357
522
|
...
|
358
523
|
|
359
524
|
|
360
525
|
class SvdBasis3:
|
361
526
|
"""
|
362
|
-
A class
|
363
|
-
|
527
|
+
A class which creates a set of orthonormal basis vectors from a set of points in 3D space. The basis is created
|
528
|
+
using a singular value decomposition of the points, and is very similar to the statistical concept of principal
|
529
|
+
component analysis.
|
530
|
+
|
531
|
+
The basis can be used to determine the rank of the point set, the variance of the points along the basis vectors,
|
532
|
+
and to extract an isometry that will transform points from the world space to the basis space. It is useful for
|
533
|
+
orienting unknown point sets in a consistent way, for finding best-fit lines or planes, and for other similar
|
534
|
+
tasks.
|
364
535
|
"""
|
365
536
|
|
366
537
|
def __init__(self, points: NDArray[float], weights: NDArray[float] | None = None):
|
@@ -369,7 +540,7 @@ class SvdBasis3:
|
|
369
540
|
points.
|
370
541
|
|
371
542
|
:param points: a numpy array of shape (n, 3) containing the points to calculate the basis from.
|
372
|
-
:param weights: a numpy array of shape (n,) containing the weights of the points. If None, all points will be
|
543
|
+
:param weights: a numpy array of shape (n, ) containing the weights of the points. If None, all points will be
|
373
544
|
weighted equally.
|
374
545
|
"""
|
375
546
|
...
|
@@ -401,14 +572,14 @@ class SvdBasis3:
|
|
401
572
|
def basis_variances(self) -> NDArray[float]:
|
402
573
|
"""
|
403
574
|
Return the variances of the basis vectors.
|
404
|
-
:return: a numpy array of shape (3,) containing the variances of the basis vectors.
|
575
|
+
:return: a numpy array of shape (3, ) containing the variances of the basis vectors.
|
405
576
|
"""
|
406
577
|
...
|
407
578
|
|
408
579
|
def basis_stdevs(self) -> NDArray[float]:
|
409
580
|
"""
|
410
581
|
Return the standard deviations of the basis vectors.
|
411
|
-
:return: a numpy array of shape (3,) containing the standard deviations of the basis vectors.
|
582
|
+
:return: a numpy array of shape (3, ) containing the standard deviations of the basis vectors.
|
412
583
|
"""
|
413
584
|
...
|
414
585
|
|
@@ -494,6 +665,10 @@ class Mesh:
|
|
494
665
|
triangle. The triangles should be specified in counter-clockwise order when looking at the triangle from the
|
495
666
|
front/outside.
|
496
667
|
|
668
|
+
!!! tip
|
669
|
+
If you get an error `TypeError: argument 'faces': 'ndarray' object cannot be converted to 'PyArray<T, D>'`,
|
670
|
+
make sure to convert the faces array to an unsigned integer type, e.g. `numpy.uint32`.
|
671
|
+
|
497
672
|
:param vertices: a numpy array of shape (n, 3) containing the vertices of the mesh.
|
498
673
|
:param faces: a numpy array of shape (m, 3) containing the triangles of the mesh, should be uint.
|
499
674
|
:param merge_duplicates: merge duplicate vertices and triangles
|
@@ -503,7 +678,7 @@ class Mesh:
|
|
503
678
|
|
504
679
|
@property
|
505
680
|
def aabb(self) -> Aabb3:
|
506
|
-
"""
|
681
|
+
""" Get the axis-aligned bounding box of the mesh. """
|
507
682
|
...
|
508
683
|
|
509
684
|
@staticmethod
|
@@ -513,6 +688,11 @@ class Mesh:
|
|
513
688
|
file. Optional parameters can be used to control the behavior of the loader when handling duplicate vertices/
|
514
689
|
triangles and degenerate triangles.
|
515
690
|
|
691
|
+
!!! note
|
692
|
+
The STL loader will automatically merge duplicate vertices due to the extreme redundancy of the STL format,
|
693
|
+
but the `merge_duplicates` flag will control whether to merge vertices when new meshes are appended to the
|
694
|
+
existing mesh.
|
695
|
+
|
516
696
|
:param path: the path to the STL file to load.
|
517
697
|
:param merge_duplicates: merge duplicate vertices and triangles. If None, the default behavior is to do nothing
|
518
698
|
:param delete_degenerate: delete degenerate triangles. If None, the default behavior is to do nothing
|
@@ -534,7 +714,7 @@ class Mesh:
|
|
534
714
|
Will return a copy of the mesh. This is a copy of the data, so modifying the returned mesh will not modify the
|
535
715
|
original mesh.
|
536
716
|
|
537
|
-
:return:
|
717
|
+
:return: an independent copy of the mesh.
|
538
718
|
"""
|
539
719
|
|
540
720
|
def transform_by(self, iso: Iso3):
|
@@ -542,7 +722,6 @@ class Mesh:
|
|
542
722
|
Transforms the vertices of the mesh by an isometry. This will modify the mesh in place. Any copies made of
|
543
723
|
the vertices will no longer match the mesh after this operation.
|
544
724
|
:param iso: the isometry to transform the mesh by.
|
545
|
-
:return: None
|
546
725
|
"""
|
547
726
|
...
|
548
727
|
|
@@ -551,6 +730,9 @@ class Mesh:
|
|
551
730
|
Append another mesh to this mesh. This will add the vertices and triangles from the other mesh to this mesh,
|
552
731
|
changing this one and leaving the other one unmodified.
|
553
732
|
|
733
|
+
The `merge_duplicates` and `delete_degenerate` flags will control whether to merge duplicate vertices/triangles
|
734
|
+
and delete degenerate triangles when appending the mesh.
|
735
|
+
|
554
736
|
:param other: the mesh to append to this mesh, will not be modified in this operation
|
555
737
|
"""
|
556
738
|
...
|
@@ -574,26 +756,38 @@ class Mesh:
|
|
574
756
|
def split(self, plane: Plane3) -> Tuple[Mesh | None, Mesh | None]:
|
575
757
|
"""
|
576
758
|
Split the mesh by a plane. The plane will divide the mesh into two possible parts and return them as two new
|
577
|
-
objects. If the part lies entirely on one side of the plane, the other part will be None
|
759
|
+
objects. If the part lies entirely on one side of the plane, the other part will be `None`.
|
578
760
|
|
579
761
|
:param plane: the plane to split the mesh by.
|
580
762
|
|
581
|
-
:return: a tuple of two optional meshes, the first being that on the negative side of the plane, the second
|
582
|
-
that on the positive side of the plane.
|
763
|
+
:return: a tuple of two optional meshes, the first being that on the negative side of the plane, the second
|
764
|
+
being that on the positive side of the plane.
|
583
765
|
"""
|
584
766
|
...
|
585
767
|
|
586
|
-
def deviation(self, points: NDArray[float], mode: DeviationMode) -> NDArray[float]:
|
768
|
+
def deviation(self, points: NDArray[float], mode: engeom.DeviationMode) -> NDArray[float]:
|
587
769
|
"""
|
588
|
-
Calculate the deviation between a set of points and their respective closest points on the mesh surface.
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
770
|
+
Calculate the deviation between a set of points and their respective closest points on the mesh surface.
|
771
|
+
There are two possible modes of computing the distance, specified using the `DeviationMode` enum. The two
|
772
|
+
modes are essentially the same except for how they treat points which are beyond the edge of the closest face.
|
773
|
+
|
774
|
+
- `DeviationMode.Point`: The deviation is calculated as the direct distance from the test point to the closest
|
775
|
+
point on the face.
|
776
|
+
|
777
|
+
- `DeviationMode.Plane`: The deviation is calculated as the distance from the test point to the plane of the
|
778
|
+
face on which the closest point lies. This allows for points that are slightly beyond the edge of the closest
|
779
|
+
face to have a deviation which would be the same as if the edge of the face extended to beyond the test point.
|
780
|
+
|
781
|
+
In both cases, the deviation will be positive if the point is outside the surface and negative if the point is
|
782
|
+
inside the surface.
|
783
|
+
|
784
|
+
This is a means of efficiently calculating the deviation of a large number of points from a mesh surface. For
|
785
|
+
a single point, the `measure_point_deviation` method will provide a `Distance3` result, which is more suitable
|
786
|
+
for visualization and reporting.
|
593
787
|
|
594
788
|
:param points: a numpy array of shape (n, 3) containing the points to calculate the deviation for.
|
595
789
|
:param mode: the mode to calculate the deviation in.
|
596
|
-
:return: a numpy array of shape (n,) containing the deviation for each point.
|
790
|
+
:return: a numpy array of shape (n, ) containing the deviation for each point.
|
597
791
|
"""
|
598
792
|
...
|
599
793
|
|
@@ -617,13 +811,14 @@ class Mesh:
|
|
617
811
|
"""
|
618
812
|
Calculate and return the intersection curves between the mesh and a plane.
|
619
813
|
|
620
|
-
:param plane:
|
621
|
-
:param tol:
|
622
|
-
|
814
|
+
:param plane: The plane to intersect the mesh with.
|
815
|
+
:param tol: The curve tolerance to use when constructing the intersection curves. See the `Curve3` class
|
816
|
+
initializer for more information on the tolerance parameter.
|
817
|
+
:return: a list of `Curve3` objects representing the intersection curves.
|
623
818
|
"""
|
624
819
|
...
|
625
820
|
|
626
|
-
def face_select_none(self) ->
|
821
|
+
def face_select_none(self) -> FaceFilterHandle:
|
627
822
|
"""
|
628
823
|
Start a filter operation on the faces of the mesh beginning with no faces selected. This will return a filter
|
629
824
|
object that can be used to further add or remove faces from the selection.
|
@@ -632,7 +827,7 @@ class Mesh:
|
|
632
827
|
"""
|
633
828
|
...
|
634
829
|
|
635
|
-
def face_select_all(self) ->
|
830
|
+
def face_select_all(self) -> FaceFilterHandle:
|
636
831
|
"""
|
637
832
|
Start a filter operation on the faces of the mesh beginning with all faces selected. This will return a filter
|
638
833
|
object that can be used to further add or remove faces from the selection.
|
@@ -646,7 +841,7 @@ class Mesh:
|
|
646
841
|
Separate the mesh into connected patches. This will return a list of new mesh objects, each containing one
|
647
842
|
connected patch of the original mesh. These objects will be clones of the original mesh, so modifying them will
|
648
843
|
have no effect on the original mesh.
|
649
|
-
:return:
|
844
|
+
:return: a list of new mesh objects containing the connected patches.
|
650
845
|
"""
|
651
846
|
|
652
847
|
def create_from_indices(self, indices: List[int]) -> Mesh:
|
@@ -655,36 +850,76 @@ class Mesh:
|
|
655
850
|
triangles (and their respective vertices) identified by the given list of indices. Do not allow duplicate
|
656
851
|
indices in the list.
|
657
852
|
:param indices: the triangle indices to include in the new mesh
|
658
|
-
:return:
|
853
|
+
:return: a new mesh object containing only the specified triangles
|
659
854
|
"""
|
660
855
|
...
|
661
856
|
|
662
|
-
def measure_point_deviation(self, x: float, y: float, z: float,
|
857
|
+
def measure_point_deviation(self, x: float, y: float, z: float,
|
858
|
+
dist_mode: engeom.DeviationMode) -> metrology.Distance3:
|
663
859
|
"""
|
664
860
|
Compute the deviation of a point from this mesh's surface and return it as a measurement object.
|
665
861
|
|
666
|
-
The deviation is the distance from the point to its closest projection onto the mesh using
|
667
|
-
|
668
|
-
|
669
|
-
|
862
|
+
The deviation is the distance from the point to its closest projection onto the mesh using the specified
|
863
|
+
distance mode. The direction of the measurement is the direction between the point and the projection,
|
864
|
+
flipped so that it points in the same direction as the mesh surface normal.
|
865
|
+
|
866
|
+
If the distance is less than a very small floating point epsilon, the direction will be taken directly from the
|
867
|
+
mesh surface normal.
|
868
|
+
|
869
|
+
The first point `.a` of the measurement is the point on the mesh, and the second point `.b` is the test point
|
870
|
+
that was given as an argument.
|
670
871
|
|
671
|
-
|
672
|
-
|
872
|
+
There are two possible modes of computing the distance, specified using the `DeviationMode` enum. The two
|
873
|
+
modes are essentially the same except for how they treat points which are beyond the edge of the closest face.
|
673
874
|
|
674
|
-
The
|
675
|
-
|
875
|
+
- `DeviationMode.Point`: The deviation is calculated as the direct distance from the test point to the closest
|
876
|
+
point on the face.
|
877
|
+
|
878
|
+
- `DeviationMode.Plane`: The deviation is calculated as the distance from the test point to the plane of the
|
879
|
+
face on which the closest point lies. This allows for points that are slightly beyond the edge of the closest
|
880
|
+
face to have a deviation which would be the same as if the edge of the face extended to beyond the test point.
|
881
|
+
|
882
|
+
In both cases, the deviation will be positive if the point is outside the surface and negative if the point is
|
883
|
+
inside the surface.
|
884
|
+
|
885
|
+
This method is appropriate for measuring the deviation at a few points of interest, returning a rich object
|
886
|
+
that contains features to aid in visualization or analysis. For bulk measurement of large numbers of points,
|
887
|
+
use the `deviation` method instead.
|
676
888
|
|
677
889
|
:param x: the x component of the point to measure
|
678
890
|
:param y: the y component of the point to measure
|
679
891
|
:param z: the z component of the point to measure
|
680
892
|
:param dist_mode: the deviation mode to use
|
681
|
-
:return:
|
893
|
+
:return: a `Distance3` object containing the deviation measurement
|
682
894
|
"""
|
683
895
|
|
684
896
|
def boundary_first_flatten(self) -> NDArray[float]:
|
685
897
|
"""
|
898
|
+
This method will perform a conformal mapping of the mesh to the XY plane using the boundary-first flattening
|
899
|
+
algorithm developed by Crane et al. This mapping attempts to preserve angles from the original mesh to the
|
900
|
+
flattened mesh, and is useful for applications such as texture mapping or transformation to an image/raster
|
901
|
+
space for analysis.
|
686
902
|
|
687
|
-
:
|
903
|
+
There are a number of limitations to this method based on the implementation:
|
904
|
+
|
905
|
+
* There can be no non-manifold edges in the mesh. Non-manifold edges are edges that have more than two faces
|
906
|
+
connected to them. If there are non-manifold edges, the method will raise an exception.
|
907
|
+
|
908
|
+
* There must be a single patch (sets of faces connected by common edges) in the mesh. If there are multiple
|
909
|
+
patches, the method will raise an exception.
|
910
|
+
|
911
|
+
* There can be only one boundary loop in the mesh, meaning that there can be no holes. If there are holes, the
|
912
|
+
method will raise an exception.
|
913
|
+
|
914
|
+
The method will return a numpy array of shape (n, 2) containing the flattened vertices of the mesh in the XY
|
915
|
+
plane. There is no specific orientation or position guarantee to the output vertices, so they may need to be
|
916
|
+
transformed, scaled, and/or rotated to fit a specific application.
|
917
|
+
|
918
|
+
The 2D vertices in the output will be in the exact same order as those in the mesh and will have a 1:1
|
919
|
+
correspondence by index, meaning that the `faces` array from the mesh also describes the triangles in the
|
920
|
+
flattened output.
|
921
|
+
|
922
|
+
:return: a numpy array of shape (n, 2) containing the flattened vertices of the mesh.
|
688
923
|
"""
|
689
924
|
|
690
925
|
def surface_closest_to(self, x: float, y: float, z: float) -> SurfacePoint3:
|
@@ -699,11 +934,21 @@ class Mesh:
|
|
699
934
|
...
|
700
935
|
|
701
936
|
|
702
|
-
class
|
937
|
+
class FaceFilterHandle:
|
938
|
+
"""
|
939
|
+
A class that acts as a handle to a filtering (selection/deselection) operation of faces on a mesh.
|
940
|
+
|
941
|
+
A filtering operation is started using the `face_select_all` or `face_select_none` methods on a `Mesh` object, and
|
942
|
+
then further filtering operations can be done on the handle to select or deselect faces based on various criteria.
|
943
|
+
|
944
|
+
Once finished, the handle can be finalized into a list of the final indices of the triangles that passed the filter,
|
945
|
+
or used to directly create a new mesh containing only the filtered triangles.
|
946
|
+
"""
|
947
|
+
|
703
948
|
def collect(self) -> List[int]:
|
704
949
|
"""
|
705
|
-
|
706
|
-
:return:
|
950
|
+
Finalize the handle by collecting the final indices of the triangles that passed the filter.
|
951
|
+
:return: a list of the final indices of the triangles that passed the filter.
|
707
952
|
"""
|
708
953
|
...
|
709
954
|
|
@@ -711,19 +956,23 @@ class MeshTriangleFilter:
|
|
711
956
|
"""
|
712
957
|
Create a new mesh from the filtered triangles. This will build a new mesh object containing only the triangles
|
713
958
|
(and their respective vertices) that are still retained in the filter.
|
714
|
-
:return:
|
959
|
+
:return: a new mesh object containing only the filtered triangles.
|
715
960
|
"""
|
716
961
|
...
|
717
962
|
|
718
|
-
def facing(self, x: float, y: float, z: float, angle: float, mode: SelectOp) ->
|
963
|
+
def facing(self, x: float, y: float, z: float, angle: float, mode: engeom.SelectOp) -> FaceFilterHandle:
|
719
964
|
"""
|
965
|
+
Add, remove, or keep only the faces whose normals are facing a given direction within a certain angle tolerance.
|
720
966
|
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
:param
|
725
|
-
:param
|
726
|
-
:
|
967
|
+
This method will alter the filter handle object in place and return `self` to allow for the use of a fluent-like
|
968
|
+
interface if desired.
|
969
|
+
|
970
|
+
:param x: the x component of the direction to check against
|
971
|
+
:param y: the y component of the direction to check against
|
972
|
+
:param z: the z component of the direction to check against
|
973
|
+
:param angle: the maximum angle in radians between the face normal and the filter direction
|
974
|
+
:param mode: the operation to perform on the faces, one of `SelectOp.Add`, `SelectOp.Remove`, or `SelectOp.Keep`
|
975
|
+
:return: the altered filter handle object
|
727
976
|
"""
|
728
977
|
...
|
729
978
|
|
@@ -732,28 +981,28 @@ class MeshTriangleFilter:
|
|
732
981
|
other: Mesh,
|
733
982
|
all_points: bool,
|
734
983
|
distance_tol: float,
|
735
|
-
mode: SelectOp,
|
984
|
+
mode: engeom.SelectOp,
|
736
985
|
planar_tol: float | None = None,
|
737
986
|
angle_tol: float | None = None,
|
738
|
-
) ->
|
987
|
+
) -> FaceFilterHandle:
|
739
988
|
"""
|
740
|
-
|
741
|
-
|
742
|
-
triangle are within the tolerance, or just one.
|
989
|
+
Add, remove, or keep only the faces that are within a certain distance of their closest projection onto another
|
990
|
+
mesh. The distance can require that all three vertices of the triangle are within the tolerance, or just one.
|
743
991
|
|
744
992
|
There are two additional optional tolerances that can be applied.
|
745
993
|
|
746
|
-
1. A planar tolerance, which checks the distance of the vertex projected onto the plane of
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
are not facing the same direction as the
|
994
|
+
1. A planar tolerance, which checks the distance of the vertex projected onto the plane of the reference mesh
|
995
|
+
triangle and looks at how far it is from the projection point. This is useful to filter out triangles
|
996
|
+
that go past the edge of the reference mesh.
|
997
|
+
|
998
|
+
2. An angle tolerance, which checks the angle between the normal of the current triangle and the normal of the
|
999
|
+
reference triangle. This is useful to filter out triangles that are not facing the same direction as the
|
1000
|
+
reference mesh.
|
752
1001
|
|
753
1002
|
:param other: the mesh to use as a reference
|
754
1003
|
:param all_points: if True, all points of the triangle must be within the tolerance, if False, only one point
|
755
1004
|
:param distance_tol: the maximum distance between the triangle and its projection onto the reference mesh
|
756
|
-
:param mode:
|
1005
|
+
:param mode: the operation to perform on the faces, one of `SelectOp.Add`, `SelectOp.Remove`, or `SelectOp.Keep`
|
757
1006
|
:param planar_tol: the maximum in-plane distance between the triangle and its projection onto the reference mesh
|
758
1007
|
:param angle_tol: the maximum angle between the normals of the triangle and the reference mesh
|
759
1008
|
"""
|
@@ -764,16 +1013,25 @@ class CurveStation3:
|
|
764
1013
|
"""
|
765
1014
|
A class representing a station along a curve in 3D space. The station is represented by a point on the curve, a
|
766
1015
|
tangent (direction) vector, and a length along the curve.
|
1016
|
+
|
1017
|
+
These are created as the result of position finding operations on `Curve3` objects.
|
767
1018
|
"""
|
768
1019
|
|
769
1020
|
@property
|
770
1021
|
def point(self) -> Point3:
|
771
|
-
"""
|
1022
|
+
"""
|
1023
|
+
Get the point in 3D world space where the station is located.
|
1024
|
+
:return: the point in 3D world space
|
1025
|
+
"""
|
772
1026
|
...
|
773
1027
|
|
774
1028
|
@property
|
775
1029
|
def direction(self) -> Vector3:
|
776
|
-
"""
|
1030
|
+
"""
|
1031
|
+
Get the direction vector of the curve at the location of the station. This is the tangent vector of the curve,
|
1032
|
+
and is typically the direction from the previous vertex to the next vertex.
|
1033
|
+
:return: the direction vector of the curve at the station.
|
1034
|
+
"""
|
777
1035
|
...
|
778
1036
|
|
779
1037
|
@property
|
@@ -785,19 +1043,30 @@ class CurveStation3:
|
|
785
1043
|
|
786
1044
|
@property
|
787
1045
|
def index(self) -> int:
|
788
|
-
"""
|
1046
|
+
"""
|
1047
|
+
Get the index of the previous vertex on the curve, at or before the station.
|
1048
|
+
:return: the index of the previous vertex on the curve.
|
1049
|
+
"""
|
789
1050
|
...
|
790
1051
|
|
791
1052
|
@property
|
792
1053
|
def length_along(self) -> float:
|
793
|
-
"""
|
1054
|
+
"""
|
1055
|
+
Get the length along the curve to the station, starting at the first vertex of the curve.
|
1056
|
+
:return: the length along the curve to the station.
|
1057
|
+
"""
|
794
1058
|
...
|
795
1059
|
|
796
1060
|
|
797
1061
|
class Curve3:
|
798
1062
|
"""
|
799
|
-
A class representing a polyline in 3D space. The curve is represented by a set of vertices and the segments
|
800
|
-
between them.
|
1063
|
+
A class representing a polyline in 3D space. The curve is represented by a set of vertices and the lien segments
|
1064
|
+
between them (also known as a polyline).
|
1065
|
+
|
1066
|
+
!!! note
|
1067
|
+
Because this curve is a simplicial 1-complex in 3D space it cannot divide space the way a `Curve2` can in 2D
|
1068
|
+
space. As a result, it lacks the concept of left/right or inside/outside, and so does not have all the same
|
1069
|
+
features that exist in the 2D curve class.
|
801
1070
|
"""
|
802
1071
|
|
803
1072
|
def __init__(self, vertices: NDArray[float], tol: float = 1.0e-6):
|
@@ -880,7 +1149,7 @@ class Curve3:
|
|
880
1149
|
"""
|
881
1150
|
...
|
882
1151
|
|
883
|
-
def resample(self, resample:
|
1152
|
+
def resample(self, resample: engeom.ResampleEnum) -> Curve3:
|
884
1153
|
"""
|
885
1154
|
Resample the curve using the given resampling method. The resampling method can be one of the following:
|
886
1155
|
|
@@ -915,7 +1184,15 @@ class Curve3:
|
|
915
1184
|
|
916
1185
|
class Aabb3:
|
917
1186
|
"""
|
918
|
-
A class representing an axis-aligned bounding box in 3D space. The box is defined by
|
1187
|
+
A class representing an axis-aligned bounding box in 3D space. The bounding box is defined by a minimum point and a
|
1188
|
+
maximum point, which are the lower-left and upper-right corners of the box, respectively.
|
1189
|
+
|
1190
|
+
Bounding boxes are typically used for accelerating intersection and distance queries and are used internally inside
|
1191
|
+
the Rust language `engeom` library for this purpose. However, they have other useful applications and so are
|
1192
|
+
exposed here in the Python API.
|
1193
|
+
|
1194
|
+
Typically, `Aabb3` objects will be retrieved from other `engeom` objects which use them internally, such as
|
1195
|
+
`Curve3` and `Mesh` entities. However, they can also be created and manipulated directly.
|
919
1196
|
"""
|
920
1197
|
|
921
1198
|
def __init__(self, x_min: float, y_min: float, z_min: float, x_max: float, y_max: float, z_max: float):
|
@@ -932,35 +1209,47 @@ class Aabb3:
|
|
932
1209
|
|
933
1210
|
@property
|
934
1211
|
def min(self) -> Point3:
|
935
|
-
"""
|
1212
|
+
"""
|
1213
|
+
Get the minimum point of the AABB.
|
1214
|
+
:return: the minimum point of the AABB.
|
1215
|
+
"""
|
936
1216
|
...
|
937
1217
|
|
938
1218
|
@property
|
939
1219
|
def max(self) -> Point3:
|
940
|
-
"""
|
1220
|
+
"""
|
1221
|
+
Get the maximum point of the AABB.
|
1222
|
+
:return: the maximum point of the AABB.
|
1223
|
+
"""
|
941
1224
|
...
|
942
1225
|
|
943
1226
|
@property
|
944
1227
|
def center(self) -> Point3:
|
945
|
-
"""
|
1228
|
+
"""
|
1229
|
+
Get the center point of the AABB.
|
1230
|
+
:return: the center point of the AABB.
|
1231
|
+
"""
|
946
1232
|
...
|
947
1233
|
|
948
1234
|
@property
|
949
1235
|
def extent(self) -> Vector3:
|
950
|
-
"""
|
1236
|
+
"""
|
1237
|
+
The extent of the box (equivalent to `self.max - self.min`).
|
1238
|
+
:return: A vector representing the extent of the box.
|
1239
|
+
"""
|
951
1240
|
...
|
952
1241
|
|
953
1242
|
@staticmethod
|
954
1243
|
def at_point(x: float, y: float, z: float, w: float, h: float | None = None, l: float | None = None) -> Aabb3:
|
955
1244
|
"""
|
956
1245
|
Create an AABB centered at a point with a given width and height.
|
957
|
-
:param x:
|
958
|
-
:param y:
|
959
|
-
:param z:
|
960
|
-
:param w:
|
961
|
-
:param h:
|
962
|
-
:param l:
|
963
|
-
:return:
|
1246
|
+
:param x: The x-coordinate of the center of the AABB.
|
1247
|
+
:param y: The y-coordinate of the center of the AABB.
|
1248
|
+
:param z: The z-coordinate of the center of the AABB.
|
1249
|
+
:param w: The width (x extent) of the AABB.
|
1250
|
+
:param h: The height (y extent) of the AABB. If not provided, it will have the same value as the width.
|
1251
|
+
:param l: The length (z extent) of the AABB. If not provided, it will have the same value as the width.
|
1252
|
+
:return: A new axis aligned bounding box object.
|
964
1253
|
"""
|
965
1254
|
...
|
966
1255
|
|