engeom 0.2.4__cp38-abi3-win_amd64.whl → 0.2.6__cp38-abi3-win_amd64.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/geom3.pyi CHANGED
@@ -1,59 +1,115 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from pathlib import Path
4
- from typing import Tuple, Iterable, List, TypeVar
4
+ from typing import Tuple, Iterable, List, TypeVar, Iterator, Any
5
5
 
6
6
  import numpy
7
- from engeom import DeviationMode, Resample, SelectOp
8
- from .metrology import Length3
7
+ from numpy.typing import NDArray
8
+ import engeom
9
+ import metrology
9
10
 
10
11
  Transformable3 = TypeVar("Transformable3", Vector3, Point3, Plane3, Iso3, SurfacePoint3)
11
12
  PointOrVector3 = TypeVar("PointOrVector3", Vector3, Point3)
12
13
 
13
14
 
14
- class Vector3:
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
+
15
28
  def __init__(self, x: float, y: float, z: float):
16
29
  """
17
-
18
- :param x:
19
- :param y:
20
- :param z:
30
+ Create a vector in 3D space by specifying the x, y, and z components.
31
+ :param x: the x component of the vector
32
+ :param y: the y component of the vector
33
+ :param z: the z component of the vector
21
34
  """
22
35
  ...
23
36
 
24
37
  @property
25
38
  def x(self) -> float:
39
+ """
40
+ Get the x component of the vector as a floating point value.
41
+ """
26
42
  ...
27
43
 
28
44
  @property
29
45
  def y(self) -> float:
46
+ """
47
+ Get the y component of the vector as a floating point value.
48
+ """
30
49
  ...
31
50
 
32
51
  @property
33
52
  def z(self) -> float:
53
+ """
54
+ Get the z component of the vector as a floating point value.
55
+ """
34
56
  ...
35
57
 
36
- def __iter__(self) -> Iterable[float]:
58
+ def __iter__(self) -> Iterator[float]:
37
59
  ...
38
60
 
39
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
+ """
40
68
  ...
41
69
 
42
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
+ """
43
77
  ...
44
78
 
45
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
+ """
46
85
  ...
47
86
 
48
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
+ """
49
92
  ...
50
93
 
51
- def __mul__(self, x: float) -> Vector3:
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
+ """
52
100
  ...
53
101
 
54
- def as_numpy(self) -> numpy.ndarray[float]:
102
+ def __truediv__(self, other: float) -> Vector3:
55
103
  """
56
- Create a numpy array of shape (3,) from the vector.
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
+ """
108
+ ...
109
+
110
+ def as_numpy(self) -> NDArray[float]:
111
+ """
112
+ Create a numpy array of shape (3, ) from the vector.
57
113
  """
58
114
  ...
59
115
 
@@ -75,48 +131,71 @@ class Vector3:
75
131
 
76
132
  def norm(self) -> float:
77
133
  """
78
- Calculate the norm (length) of the vector.
79
- :return:
134
+ Calculate the Euclidian norm (aka magnitude, length) of the vector.
135
+ :return: the length of the vector as a floating point value.
80
136
  """
81
137
 
82
138
  def normalized(self) -> Vector3:
83
139
  """
84
- 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.
85
142
  :return: a new vector that has unit length
86
143
  """
87
144
 
88
145
  def angle_to(self, other: Vector3) -> float:
89
146
  """
90
- 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.
91
148
  :param other: the other vector to calculate the angle to.
92
149
  :return: the angle between the two vectors in radians.
93
150
  """
94
151
  ...
95
152
 
96
153
 
97
- class Point3:
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
+
98
167
  def __init__(self, x: float, y: float, z: float):
99
168
  """
169
+ Create a point in 3D space by specifying the x, y, and z coordinates.
100
170
 
101
- :param x:
102
- :param y:
103
- :param z:
171
+ :param x: the x coordinate of the point
172
+ :param y: the y coordinate of the point
173
+ :param z: the z coordinate of the point
104
174
  """
105
175
  ...
106
176
 
107
177
  @property
108
178
  def x(self) -> float:
179
+ """
180
+ Get the x coordinate of the point as a floating point value.
181
+ """
109
182
  ...
110
183
 
111
184
  @property
112
185
  def y(self) -> float:
186
+ """
187
+ Get the y coordinate of the point as a floating point value.
188
+ """
113
189
  ...
114
190
 
115
191
  @property
116
192
  def z(self) -> float:
193
+ """
194
+ Get the z coordinate of the point as a floating point value.
195
+ """
117
196
  ...
118
197
 
119
- def __iter__(self) -> Iterable[float]:
198
+ def __iter__(self) -> Iterator[float]:
120
199
  ...
121
200
 
122
201
  @property
@@ -128,28 +207,82 @@ class Point3:
128
207
  ...
129
208
 
130
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
+ """
131
215
  ...
132
216
 
133
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
+ """
134
223
  ...
135
224
 
136
- def as_numpy(self) -> numpy.ndarray[float]:
225
+ def __neg__(self) -> Point3:
137
226
  """
138
- Create a numpy array of shape (2,) from the point.
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
+ """
230
+ ...
231
+
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
+ """
238
+ ...
239
+
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
+ """
247
+ ...
248
+
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
+ """
255
+ ...
256
+
257
+ def as_numpy(self) -> NDArray[float]:
258
+ """
259
+ Create a numpy array of shape (2, ) from the point.
139
260
  """
140
261
  ...
141
262
 
142
263
 
143
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
+
144
274
  def __init__(self, x: float, y: float, z: float, nx: float, ny: float, nz: float):
145
275
  """
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,
277
+ and z components of the normal vector. The normal components will be normalized before being stored, so they
278
+ do not need to be scaled to unit length before being passed to this constructor.
146
279
 
147
- :param x:
148
- :param y:
149
- :param z:
150
- :param nx:
151
- :param ny:
152
- :param nz:
280
+ :param x: the x coordinate of the point
281
+ :param y: the y coordinate of the point
282
+ :param z: the z coordinate of the point
283
+ :param nx: the x component of the normal vector (will be normalized after construction)
284
+ :param ny: the y component of the normal vector (will be normalized after construction)
285
+ :param nz: the z component of the normal vector (will be normalized after construction)
153
286
  """
154
287
  ...
155
288
 
@@ -223,12 +356,60 @@ class SurfacePoint3:
223
356
  """
224
357
  ...
225
358
 
359
+ def __mul__(self, other: float) -> SurfacePoint3:
360
+ """
361
+ Multiply the position of the surface point by a scalar value. The normal vector is not affected unless the
362
+ scalar is negative, in which case the normal vector is inverted.
363
+ :param other:
364
+ :return:
365
+ """
366
+ ...
367
+
368
+ def __rmul__(self, other: float) -> SurfacePoint3:
369
+ """
370
+ Multiply the position of the surface point by a scalar value. The normal vector is not affected unless the
371
+ scalar is negative, in which case the normal vector is inverted.
372
+ :param other:
373
+ :return:
374
+ """
375
+ ...
376
+
377
+ def __truediv__(self, other: float) -> SurfacePoint3:
378
+ """
379
+ Divide the position of the surface point by a scalar value. The normal vector is not affected unless the
380
+ scalar is negative, in which case the normal vector is inverted.
381
+ :param other:
382
+ :return:
383
+ """
384
+ ...
385
+
386
+ def __neg__(self) -> SurfacePoint3:
387
+ """
388
+ Invert both the position AND the normal vector of the surface point.
389
+ """
390
+ ...
391
+
226
392
 
227
393
  class Iso3:
228
- """ An isometry (rigid body transformation) in 3D space. """
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.
229
398
 
230
- def __init__(self, matrix: numpy.ndarray[float]):
231
- """ Create an isometry from a 4x4 matrix. """
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
+ """
402
+
403
+ def __init__(self, matrix: NDArray[float]):
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
+ """
232
413
  ...
233
414
 
234
415
  @staticmethod
@@ -238,18 +419,28 @@ class Iso3:
238
419
 
239
420
  @staticmethod
240
421
  def from_translation(x: float, y: float, z: float) -> Iso3:
241
- """ Create an isometry representing a translation. """
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
+ """
242
429
  ...
243
430
 
244
431
  @staticmethod
245
- def from_rotation(angle: float, a: float, b: float, c: float) -> Iso3:
432
+ def from_rotation(angle: float, ax: float, ay: float, az: float) -> Iso3:
246
433
  """
247
- Create an isometry representing a rotation around an axis. The axis will be normalized before the rotation is
248
- 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
+
249
440
  :param angle: the angle to rotate by in radians.
250
- :param a: the x component of the rotation axis.
251
- :param b: the y component of the rotation axis.
252
- :param c: the z component of the rotation axis.
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.
253
444
  :return: the isometry representing the rotation.
254
445
  """
255
446
  ...
@@ -263,64 +454,93 @@ class Iso3:
263
454
  ...
264
455
 
265
456
  def inverse(self) -> Iso3:
266
- """ Return the inverse of the isometry. """
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
+ """
267
461
  ...
268
462
 
269
- def transform_points(self, points: numpy.ndarray[float]) -> numpy.ndarray[float]:
270
- """ Transform a set of points by the isometry. This will transform the points by the rotation and translation
271
- of the isometry.
463
+ def transform_points(self, points: NDArray[float]) -> NDArray[float]:
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.
272
469
 
273
- :param points: a numpy array of shape (n, 3) containing the points to transform.
274
- :return: a numpy array of shape (n, 3) containing the transformed points.
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.
275
475
  """
276
476
  ...
277
477
 
278
- def transform_vectors(self, vector: numpy.ndarray[float]) -> numpy.ndarray[float]:
279
- """ Transform a set of vectors by the isometry. This will only transform the direction of the vectors, not
280
- their magnitude.
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.
281
485
 
282
- :param vector: a numpy array of shape (n, 3) containing the vectors to transform.
283
- :return: a numpy array of shape (n, 3) containing the transformed vectors.
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.
284
491
  """
285
492
  ...
286
493
 
287
- def as_numpy(self) -> numpy.ndarray[float]:
288
- """ Return a copy of the 4x4 matrix representation of the isometry. This is a copy operation. """
494
+ def as_numpy(self) -> NDArray[float]:
495
+ """
496
+ Return a copy of the 4x4 matrix representation of the isometry.
497
+ """
289
498
  ...
290
499
 
291
500
  def flip_around_x(self) -> Iso3:
292
- """ Return a new isometry that flips the isometry 180° around the x-axis. The origin of the isometry will be
293
- preserved, but the y and z axes will point in the opposite directions. """
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
+ """
294
506
  ...
295
507
 
296
508
  def flip_around_y(self) -> Iso3:
297
- """ Return a new isometry that flips the isometry 180° around the y-axis. The origin of the isometry will be
298
- preserved, but the x and z axes will point in the opposite directions. """
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
+ """
299
514
  ...
300
515
 
301
516
  def flip_around_z(self) -> Iso3:
302
- """ Return a new isometry that flips the isometry 180° around the z-axis. The origin of the isometry will be
303
- preserved, but the x and y axes will point in the opposite directions. """
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
+ """
304
522
  ...
305
523
 
306
524
 
307
525
  class SvdBasis3:
308
526
  """
309
- A class representing a basis in 3D space. This class is created from a set of points and will calculate the best
310
- fitting basis for the points using a singular value decomposition.
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.
311
535
  """
312
536
 
313
- def __init__(
314
- self,
315
- points: numpy.ndarray[float],
316
- weights: numpy.ndarray[float] | None = None
317
- ):
537
+ def __init__(self, points: NDArray[float], weights: NDArray[float] | None = None):
318
538
  """
319
539
  Create a basis from a set of points. The basis will be calculated using a singular value decomposition of the
320
540
  points.
321
541
 
322
542
  :param points: a numpy array of shape (n, 3) containing the points to calculate the basis from.
323
- :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
324
544
  weighted equally.
325
545
  """
326
546
  ...
@@ -335,31 +555,31 @@ class SvdBasis3:
335
555
  """
336
556
  ...
337
557
 
338
- def largest(self) -> numpy.ndarray[float]:
558
+ def largest(self) -> Vector3:
339
559
  """
340
560
  Return the largest normalized basis vector.
341
- :return: a numpy array of shape (3,) containing the largest basis vector.
561
+ :return: a Vector3 object containing the largest basis vector.
342
562
  """
343
563
  ...
344
564
 
345
- def smallest(self) -> numpy.ndarray[float]:
565
+ def smallest(self) -> Vector3:
346
566
  """
347
567
  Return the smallest normalized basis vector.
348
- :return: a numpy array of shape (3,) containing the smallest basis vector.
568
+ :return: a Vector3 object containing the smallest basis vector.
349
569
  """
350
570
  ...
351
571
 
352
- def basis_variances(self) -> numpy.ndarray[float]:
572
+ def basis_variances(self) -> NDArray[float]:
353
573
  """
354
574
  Return the variances of the basis vectors.
355
- :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.
356
576
  """
357
577
  ...
358
578
 
359
- def basis_stdevs(self) -> numpy.ndarray[float]:
579
+ def basis_stdevs(self) -> NDArray[float]:
360
580
  """
361
581
  Return the standard deviations of the basis vectors.
362
- :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.
363
583
  """
364
584
  ...
365
585
 
@@ -434,8 +654,8 @@ class Mesh:
434
654
 
435
655
  def __init__(
436
656
  self,
437
- vertices: numpy.ndarray[float],
438
- faces: numpy.ndarray[numpy.uint32],
657
+ vertices: NDArray[float],
658
+ faces: NDArray[numpy.uint32],
439
659
  merge_duplicates: bool = False,
440
660
  delete_degenerate: bool = False
441
661
  ):
@@ -445,6 +665,10 @@ class Mesh:
445
665
  triangle. The triangles should be specified in counter-clockwise order when looking at the triangle from the
446
666
  front/outside.
447
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
+
448
672
  :param vertices: a numpy array of shape (n, 3) containing the vertices of the mesh.
449
673
  :param faces: a numpy array of shape (m, 3) containing the triangles of the mesh, should be uint.
450
674
  :param merge_duplicates: merge duplicate vertices and triangles
@@ -454,7 +678,7 @@ class Mesh:
454
678
 
455
679
  @property
456
680
  def aabb(self) -> Aabb3:
457
- """ Return the axis-aligned bounding box of the mesh. """
681
+ """ Get the axis-aligned bounding box of the mesh. """
458
682
  ...
459
683
 
460
684
  @staticmethod
@@ -464,6 +688,11 @@ class Mesh:
464
688
  file. Optional parameters can be used to control the behavior of the loader when handling duplicate vertices/
465
689
  triangles and degenerate triangles.
466
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
+
467
696
  :param path: the path to the STL file to load.
468
697
  :param merge_duplicates: merge duplicate vertices and triangles. If None, the default behavior is to do nothing
469
698
  :param delete_degenerate: delete degenerate triangles. If None, the default behavior is to do nothing
@@ -485,7 +714,7 @@ class Mesh:
485
714
  Will return a copy of the mesh. This is a copy of the data, so modifying the returned mesh will not modify the
486
715
  original mesh.
487
716
 
488
- :return:
717
+ :return: an independent copy of the mesh.
489
718
  """
490
719
 
491
720
  def transform_by(self, iso: Iso3):
@@ -493,7 +722,6 @@ class Mesh:
493
722
  Transforms the vertices of the mesh by an isometry. This will modify the mesh in place. Any copies made of
494
723
  the vertices will no longer match the mesh after this operation.
495
724
  :param iso: the isometry to transform the mesh by.
496
- :return: None
497
725
  """
498
726
  ...
499
727
 
@@ -502,12 +730,15 @@ class Mesh:
502
730
  Append another mesh to this mesh. This will add the vertices and triangles from the other mesh to this mesh,
503
731
  changing this one and leaving the other one unmodified.
504
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
+
505
736
  :param other: the mesh to append to this mesh, will not be modified in this operation
506
737
  """
507
738
  ...
508
739
 
509
740
  @property
510
- def vertices(self) -> numpy.ndarray[float]:
741
+ def vertices(self) -> NDArray[float]:
511
742
  """
512
743
  Will return an immutable view of the vertices of the mesh as a numpy array of shape (n, 3).
513
744
  :return: a numpy array of shape (n, 3) containing the vertices of the mesh.
@@ -515,7 +746,7 @@ class Mesh:
515
746
  ...
516
747
 
517
748
  @property
518
- def faces(self) -> numpy.ndarray[numpy.uint32]:
749
+ def faces(self) -> NDArray[numpy.uint32]:
519
750
  """
520
751
  Will return an immutable view of the triangles of the mesh as a numpy array of shape (m, 3).
521
752
  :return: a numpy array of shape (m, 3) containing the triangles of the mesh.
@@ -525,30 +756,42 @@ class Mesh:
525
756
  def split(self, plane: Plane3) -> Tuple[Mesh | None, Mesh | None]:
526
757
  """
527
758
  Split the mesh by a plane. The plane will divide the mesh into two possible parts and return them as two new
528
- 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`.
529
760
 
530
761
  :param plane: the plane to split the mesh by.
531
762
 
532
- :return: a tuple of two optional meshes, the first being that on the negative side of the plane, the second being
533
- 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.
534
765
  """
535
766
  ...
536
767
 
537
- def deviation(self, points: numpy.ndarray[float], mode: DeviationMode) -> numpy.ndarray[float]:
768
+ def deviation(self, points: NDArray[float], mode: engeom.DeviationMode) -> NDArray[float]:
538
769
  """
539
- Calculate the deviation between a set of points and their respective closest points on the mesh surface. The
540
- deviation can be calculated in two modes: absolute and normal. In the absolute mode, the deviation is the
541
- linear distance between the point and the closest point on the mesh. In the normal mode, the deviation is the
542
- distance along the normal of the closest point on the mesh. In both cases, the deviation will be positive if
543
- the point is outside the surface and negative if the point is inside the surface.
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.
544
787
 
545
788
  :param points: a numpy array of shape (n, 3) containing the points to calculate the deviation for.
546
789
  :param mode: the mode to calculate the deviation in.
547
- :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.
548
791
  """
549
792
  ...
550
793
 
551
- def sample_poisson(self, radius: float) -> numpy.ndarray[float]:
794
+ def sample_poisson(self, radius: float) -> NDArray[float]:
552
795
  """
553
796
  Sample the surface of the mesh using a Poisson disk sampling algorithm. This will return a numpy array of points
554
797
  and their normals that are approximately evenly distributed across the surface of the mesh. The radius parameter
@@ -568,13 +811,14 @@ class Mesh:
568
811
  """
569
812
  Calculate and return the intersection curves between the mesh and a plane.
570
813
 
571
- :param plane:
572
- :param tol:
573
- :return:
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.
574
818
  """
575
819
  ...
576
820
 
577
- def face_select_none(self) -> MeshTriangleFilter:
821
+ def face_select_none(self) -> FaceFilterHandle:
578
822
  """
579
823
  Start a filter operation on the faces of the mesh beginning with no faces selected. This will return a filter
580
824
  object that can be used to further add or remove faces from the selection.
@@ -583,7 +827,7 @@ class Mesh:
583
827
  """
584
828
  ...
585
829
 
586
- def face_select_all(self) -> MeshTriangleFilter:
830
+ def face_select_all(self) -> FaceFilterHandle:
587
831
  """
588
832
  Start a filter operation on the faces of the mesh beginning with all faces selected. This will return a filter
589
833
  object that can be used to further add or remove faces from the selection.
@@ -597,7 +841,7 @@ class Mesh:
597
841
  Separate the mesh into connected patches. This will return a list of new mesh objects, each containing one
598
842
  connected patch of the original mesh. These objects will be clones of the original mesh, so modifying them will
599
843
  have no effect on the original mesh.
600
- :return:
844
+ :return: a list of new mesh objects containing the connected patches.
601
845
  """
602
846
 
603
847
  def create_from_indices(self, indices: List[int]) -> Mesh:
@@ -606,36 +850,76 @@ class Mesh:
606
850
  triangles (and their respective vertices) identified by the given list of indices. Do not allow duplicate
607
851
  indices in the list.
608
852
  :param indices: the triangle indices to include in the new mesh
609
- :return:
853
+ :return: a new mesh object containing only the specified triangles
610
854
  """
611
855
  ...
612
856
 
613
- def measure_point_deviation(self, x: float, y: float, z: float, dist_mode: DeviationMode) -> Length3:
857
+ def measure_point_deviation(self, x: float, y: float, z: float,
858
+ dist_mode: engeom.DeviationMode) -> metrology.Distance3:
614
859
  """
615
860
  Compute the deviation of a point from this mesh's surface and return it as a measurement object.
616
861
 
617
- The deviation is the distance from the point to its closest projection onto the mesh using
618
- the specified distance mode. The direction of the measurement is the direction between the
619
- point and the projection, flipped into the positive half-space of the mesh surface at the
620
- projection point.
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.
621
865
 
622
- If the distance is less than a very small floating point epsilon, the direction will be
623
- taken directly from the mesh surface normal.
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.
624
868
 
625
- The first point `.a` of the measurement is the reference point, and the second point `.b`
626
- is the test point.
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.
871
+
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.
874
+
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.
627
888
 
628
889
  :param x: the x component of the point to measure
629
890
  :param y: the y component of the point to measure
630
891
  :param z: the z component of the point to measure
631
892
  :param dist_mode: the deviation mode to use
632
- :return:
893
+ :return: a `Distance3` object containing the deviation measurement
633
894
  """
634
895
 
635
- def boundary_first_flatten(self) -> numpy.ndarray[float]:
896
+ def boundary_first_flatten(self) -> NDArray[float]:
636
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.
637
902
 
638
- :return:
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.
639
923
  """
640
924
 
641
925
  def surface_closest_to(self, x: float, y: float, z: float) -> SurfacePoint3:
@@ -650,11 +934,21 @@ class Mesh:
650
934
  ...
651
935
 
652
936
 
653
- class MeshTriangleFilter:
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
+
654
948
  def collect(self) -> List[int]:
655
949
  """
656
- Collect the final indices of the triangles that passed the filter.
657
- :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.
658
952
  """
659
953
  ...
660
954
 
@@ -662,19 +956,23 @@ class MeshTriangleFilter:
662
956
  """
663
957
  Create a new mesh from the filtered triangles. This will build a new mesh object containing only the triangles
664
958
  (and their respective vertices) that are still retained in the filter.
665
- :return:
959
+ :return: a new mesh object containing only the filtered triangles.
666
960
  """
667
961
  ...
668
962
 
669
- def facing(self, x: float, y: float, z: float, angle: float, mode: SelectOp) -> MeshTriangleFilter:
963
+ def facing(self, x: float, y: float, z: float, angle: float, mode: engeom.SelectOp) -> FaceFilterHandle:
670
964
  """
965
+ Add, remove, or keep only the faces whose normals are facing a given direction within a certain angle tolerance.
671
966
 
672
- :param x:
673
- :param y:
674
- :param z:
675
- :param angle:
676
- :param mode:
677
- :return:
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
678
976
  """
679
977
  ...
680
978
 
@@ -683,28 +981,28 @@ class MeshTriangleFilter:
683
981
  other: Mesh,
684
982
  all_points: bool,
685
983
  distance_tol: float,
686
- mode: SelectOp,
984
+ mode: engeom.SelectOp,
687
985
  planar_tol: float | None = None,
688
986
  angle_tol: float | None = None,
689
- ) -> MeshTriangleFilter:
987
+ ) -> FaceFilterHandle:
690
988
  """
691
- Reduce the list of indices to only include triangles that are within a certain distance of
692
- their closest projection onto another mesh. The distance can require that all points of the
693
- 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.
694
991
 
695
992
  There are two additional optional tolerances that can be applied.
696
993
 
697
- 1. A planar tolerance, which checks the distance of the vertex projected onto the plane of
698
- the reference mesh triangle and looks at how far it is from the projection point. This
699
- is useful to filter out triangles that go past the edge of the reference mesh.
700
- 2. An angle tolerance, which checks the angle between the normal of the current triangle
701
- and the normal of the reference triangle. This is useful to filter out triangles that
702
- are not facing the same direction as the reference mesh.
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.
703
1001
 
704
1002
  :param other: the mesh to use as a reference
705
1003
  :param all_points: if True, all points of the triangle must be within the tolerance, if False, only one point
706
1004
  :param distance_tol: the maximum distance between the triangle and its projection onto the reference mesh
707
- :param mode:
1005
+ :param mode: the operation to perform on the faces, one of `SelectOp.Add`, `SelectOp.Remove`, or `SelectOp.Keep`
708
1006
  :param planar_tol: the maximum in-plane distance between the triangle and its projection onto the reference mesh
709
1007
  :param angle_tol: the maximum angle between the normals of the triangle and the reference mesh
710
1008
  """
@@ -715,16 +1013,25 @@ class CurveStation3:
715
1013
  """
716
1014
  A class representing a station along a curve in 3D space. The station is represented by a point on the curve, a
717
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.
718
1018
  """
719
1019
 
720
1020
  @property
721
1021
  def point(self) -> Point3:
722
- """ The 3d position in space on the curve. """
1022
+ """
1023
+ Get the point in 3D world space where the station is located.
1024
+ :return: the point in 3D world space
1025
+ """
723
1026
  ...
724
1027
 
725
1028
  @property
726
1029
  def direction(self) -> Vector3:
727
- """ The tangent (direction) vector of the curve at the station. """
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
+ """
728
1035
  ...
729
1036
 
730
1037
  @property
@@ -736,22 +1043,33 @@ class CurveStation3:
736
1043
 
737
1044
  @property
738
1045
  def index(self) -> int:
739
- """ The index of the previous vertex on the curve, at or before the station. """
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
+ """
740
1050
  ...
741
1051
 
742
1052
  @property
743
1053
  def length_along(self) -> float:
744
- """ The length along the curve from the start of the curve to the station. """
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
+ """
745
1058
  ...
746
1059
 
747
1060
 
748
1061
  class Curve3:
749
1062
  """
750
- A class representing a polyline in 3D space. The curve is represented by a set of vertices and the segments
751
- 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.
752
1070
  """
753
1071
 
754
- def __init__(self, vertices: numpy.ndarray, tol: float = 1.0e-6):
1072
+ def __init__(self, vertices: NDArray[float], tol: float = 1.0e-6):
755
1073
  """
756
1074
  Create a curve from a set of vertices. The vertices should be a numpy array of shape (n, 3).
757
1075
 
@@ -778,7 +1096,7 @@ class Curve3:
778
1096
  ...
779
1097
 
780
1098
  @property
781
- def points(self) -> numpy.ndarray[float]:
1099
+ def points(self) -> NDArray[float]:
782
1100
  """
783
1101
  Will return an immutable view of the vertices of the mesh as a numpy array of shape (n, 3).
784
1102
  :return: a numpy array of shape (n, 3) containing the vertices of the mesh.
@@ -831,7 +1149,7 @@ class Curve3:
831
1149
  """
832
1150
  ...
833
1151
 
834
- def resample(self, resample: Resample) -> Curve3:
1152
+ def resample(self, resample: engeom.ResampleEnum) -> Curve3:
835
1153
  """
836
1154
  Resample the curve using the given resampling method. The resampling method can be one of the following:
837
1155
 
@@ -866,7 +1184,15 @@ class Curve3:
866
1184
 
867
1185
  class Aabb3:
868
1186
  """
869
- A class representing an axis-aligned bounding box in 3D space. The box is defined by its minimum and maximum
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.
870
1196
  """
871
1197
 
872
1198
  def __init__(self, x_min: float, y_min: float, z_min: float, x_max: float, y_max: float, z_max: float):
@@ -883,20 +1209,76 @@ class Aabb3:
883
1209
 
884
1210
  @property
885
1211
  def min(self) -> Point3:
886
- """ The minimum point of the box. """
1212
+ """
1213
+ Get the minimum point of the AABB.
1214
+ :return: the minimum point of the AABB.
1215
+ """
887
1216
  ...
888
1217
 
889
1218
  @property
890
1219
  def max(self) -> Point3:
891
- """ The maximum point of the box. """
1220
+ """
1221
+ Get the maximum point of the AABB.
1222
+ :return: the maximum point of the AABB.
1223
+ """
892
1224
  ...
893
1225
 
894
1226
  @property
895
1227
  def center(self) -> Point3:
896
- """ The center point of the box. """
1228
+ """
1229
+ Get the center point of the AABB.
1230
+ :return: the center point of the AABB.
1231
+ """
897
1232
  ...
898
1233
 
899
1234
  @property
900
1235
  def extent(self) -> Vector3:
901
- """ The extent of the box. """
1236
+ """
1237
+ The extent of the box (equivalent to `self.max - self.min`).
1238
+ :return: A vector representing the extent of the box.
1239
+ """
1240
+ ...
1241
+
1242
+ @staticmethod
1243
+ def at_point(x: float, y: float, z: float, w: float, h: float | None = None, l: float | None = None) -> Aabb3:
1244
+ """
1245
+ Create an AABB centered at a point with a given width and height.
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.
1253
+ """
1254
+ ...
1255
+
1256
+ @staticmethod
1257
+ def from_points(points: NDArray[float]) -> Aabb3:
1258
+ """
1259
+ Create an AABB that bounds a set of points. If the point array is empty or the wrong shape, an error will be
1260
+ thrown.
1261
+ :param points: a numpy array of shape (N, 2) containing the points to bound
1262
+ :return: a new AABB object
1263
+ """
1264
+ ...
1265
+
1266
+ def expand(self, d: float) -> Aabb3:
1267
+ """
1268
+ Expand the AABB by a given distance in all directions. The resulting height and
1269
+ width will be increased by 2 * d.
1270
+
1271
+ :param d: the distance to expand the AABB by.
1272
+ :return: a new AABB object with the expanded bounds.
1273
+ """
1274
+ ...
1275
+
1276
+ def shrink(self, d: float) -> Aabb3:
1277
+ """
1278
+ Shrink the AABB by a given distance in all directions. The resulting height and
1279
+ width will be decreased by 2 * d.
1280
+
1281
+ :param d: the distance to shrink the AABB by.
1282
+ :return: a new AABB object with the shrunk bounds.
1283
+ """
902
1284
  ...