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/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
- from engeom import DeviationMode, Resample, SelectOp
9
- from .metrology import Length3
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, 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
+ """
53
100
  ...
54
101
 
55
- def __truediv__(self, x: float) -> Vector3:
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, x: float) -> Point3:
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, x: float) -> Point3:
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, x: float) -> Point3:
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
- """ 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.
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
- """ Create an isometry from a 4x4 matrix. """
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
- """ 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
+ """
295
429
  ...
296
430
 
297
431
  @staticmethod
298
- 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:
299
433
  """
300
- Create an isometry representing a rotation around an axis. The axis will be normalized before the rotation is
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 a: the x component of the rotation axis.
304
- :param b: the y component of the rotation axis.
305
- :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.
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
- """ 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
+ """
320
461
  ...
321
462
 
322
463
  def transform_points(self, points: NDArray[float]) -> NDArray[float]:
323
- """ Transform a set of points by the isometry. This will transform the points by the rotation and translation
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
- :param points: a numpy array of shape (n, 3) containing the points to transform.
327
- :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.
328
475
  """
329
476
  ...
330
477
 
331
- def transform_vectors(self, vector: NDArray[float]) -> NDArray[float]:
332
- """ Transform a set of vectors by the isometry. This will only transform the direction of the vectors, not
333
- 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.
334
485
 
335
- :param vector: a numpy array of shape (n, 3) containing the vectors to transform.
336
- :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.
337
491
  """
338
492
  ...
339
493
 
340
494
  def as_numpy(self) -> NDArray[float]:
341
- """ Return a copy of the 4x4 matrix representation of the isometry. This is a copy operation. """
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
- """ Return a new isometry that flips the isometry 180° around the x-axis. The origin of the isometry will be
346
- 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
+ """
347
506
  ...
348
507
 
349
508
  def flip_around_y(self) -> Iso3:
350
- """ Return a new isometry that flips the isometry 180° around the y-axis. The origin of the isometry will be
351
- 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
+ """
352
514
  ...
353
515
 
354
516
  def flip_around_z(self) -> Iso3:
355
- """ Return a new isometry that flips the isometry 180° around the z-axis. The origin of the isometry will be
356
- 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
+ """
357
522
  ...
358
523
 
359
524
 
360
525
  class SvdBasis3:
361
526
  """
362
- A class representing a basis in 3D space. This class is created from a set of points and will calculate the best
363
- 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.
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
- """ Return the axis-aligned bounding box of the mesh. """
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 being
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. The
589
- deviation can be calculated in two modes: absolute and normal. In the absolute mode, the deviation is the
590
- linear distance between the point and the closest point on the mesh. In the normal mode, the deviation is the
591
- distance along the normal of the closest point on the mesh. In both cases, the deviation will be positive if
592
- 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.
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
- :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.
623
818
  """
624
819
  ...
625
820
 
626
- def face_select_none(self) -> MeshTriangleFilter:
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) -> MeshTriangleFilter:
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, dist_mode: DeviationMode) -> Length3:
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
- the specified distance mode. The direction of the measurement is the direction between the
668
- point and the projection, flipped into the positive half-space of the mesh surface at the
669
- 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.
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
- If the distance is less than a very small floating point epsilon, the direction will be
672
- taken directly from the mesh surface normal.
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 first point `.a` of the measurement is the reference point, and the second point `.b`
675
- is the test point.
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
- :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.
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 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
+
703
948
  def collect(self) -> List[int]:
704
949
  """
705
- Collect the final indices of the triangles that passed the filter.
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) -> MeshTriangleFilter:
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
- :param x:
722
- :param y:
723
- :param z:
724
- :param angle:
725
- :param mode:
726
- :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
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
- ) -> MeshTriangleFilter:
987
+ ) -> FaceFilterHandle:
739
988
  """
740
- Reduce the list of indices to only include triangles that are within a certain distance of
741
- their closest projection onto another mesh. The distance can require that all points of the
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
- the reference mesh triangle and looks at how far it is from the projection point. This
748
- is useful to filter out triangles that go past the edge of the reference mesh.
749
- 2. An angle tolerance, which checks the angle between the normal of the current triangle
750
- and the normal of the reference triangle. This is useful to filter out triangles that
751
- 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.
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
- """ 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
+ """
772
1026
  ...
773
1027
 
774
1028
  @property
775
1029
  def direction(self) -> Vector3:
776
- """ 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
+ """
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
- """ 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
+ """
789
1050
  ...
790
1051
 
791
1052
  @property
792
1053
  def length_along(self) -> float:
793
- """ 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
+ """
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: Resample) -> Curve3:
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 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.
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
- """ The minimum point of the box. """
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
- """ The maximum point of the box. """
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
- """ The center point of the box. """
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
- """ 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
+ """
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: the x-coordinate of the center of the AABB.
958
- :param y: the y-coordinate of the center of the AABB.
959
- :param z: the z-coordinate of the center of the AABB.
960
- :param w: the width of the AABB.
961
- :param h: the height of the AABB. If not provided, the AABB will be square.
962
- :param l: the length of the AABB. If not provided, the AABB will be square.
963
- :return: a new AABB object.
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