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/geom2.pyi CHANGED
@@ -1,128 +1,262 @@
1
1
  from __future__ import annotations
2
- import numpy
3
- from typing import Iterable, Tuple, Type, TypeVar
4
2
 
5
- from engeom.engeom import Resample
3
+ from typing import Iterable, Tuple, TypeVar, Iterator, Any
4
+
5
+ from numpy.typing import NDArray
6
+ from engeom.engeom import ResampleEnum
7
+
8
+ from engeom import geom3
6
9
 
7
10
  Transformable2 = TypeVar("Transformable2", Vector2, Point2, Iso2, SurfacePoint2)
8
11
  PointOrVec2 = TypeVar("PointOrVec2", Point2, Vector2)
9
12
 
10
13
 
11
14
  class Vector2(Iterable[float]):
15
+ """
16
+ A class representing a vector in 2D space. The vector contains an x and y component. It is iterable and will
17
+ yield the x and y components in order, allowing the Python unpacking operator `*` to be used to compensate for the
18
+ lack of function overloading through some other parts of the library.
19
+
20
+ A vector has different semantics than a point when it comes to transformations and some mathematical operations.
21
+ """
22
+
23
+ def __iter__(self) -> Iterator[float]:
24
+ pass
25
+
12
26
  def __init__(self, x: float, y: float):
13
27
  """
14
-
15
- :param x:
16
- :param y:
28
+ Create a 2D vector from the given x and y components.
29
+ :param x: the x component of the vector.
30
+ :param y: the y component of the vector.
17
31
  """
18
32
  ...
19
33
 
20
34
  @property
21
35
  def x(self) -> float:
36
+ """
37
+ Access the x component of the vector as a floating point value.
38
+ """
22
39
  ...
23
40
 
24
41
  @property
25
42
  def y(self) -> float:
43
+ """
44
+ Access the y component of the vector as a floating point value.
45
+ """
26
46
  ...
27
47
 
28
48
  def __rmul__(self, other: float) -> Vector2:
49
+ """
50
+ Multiply the vector by a scalar value. This allows the scalar to be on the left side of the multiplication
51
+ operator.
52
+ :param other: a scalar value to multiply the vector by.
53
+ :return: a new vector that is the result of the multiplication.
54
+ """
55
+ ...
56
+
57
+ def __mul__(self, other: float) -> Vector2:
58
+ """
59
+ Multiply the vector by a scalar value.
60
+ :param other: a scalar value to multiply the vector by.
61
+ :return: a new vector that is the result of the multiplication.
62
+ """
29
63
  ...
30
64
 
31
65
  def __add__(self, other: PointOrVec2) -> PointOrVec2:
66
+ """
67
+ Add a vector to a point or another vector. Adding a vector to a point will return a new point, while
68
+ adding a vector to a vector will return a new vector.
69
+ :param other: a point or vector to add to the vector.
70
+ :return: a new point or vector that is the result of the addition.
71
+ """
32
72
  ...
33
73
 
34
74
  def __sub__(self, other: Vector2) -> Vector2:
75
+ """
76
+ Subtract a vector from this vector.
77
+ :param other: the vector to subtract from this vector.
78
+ :return: a new vector that is the result of the subtraction.
79
+ """
35
80
  ...
36
81
 
37
82
  def __neg__(self) -> Vector2:
83
+ """
84
+ Invert the vector by negating the x and y components.
85
+ :return: a new vector in which the x and y components are negated.
86
+ """
38
87
  ...
39
88
 
40
- def __mul__(self, x: float) -> Vector2:
89
+ def __truediv__(self, other: float) -> Vector2:
90
+ """
91
+ Divide the vector by a scalar value.
92
+ :param other: a scalar value to divide the vector by.
93
+ :return: a new vector that is the result of the division.
94
+ """
41
95
  ...
42
96
 
43
- def as_numpy(self) -> numpy.ndarray[float]:
97
+ def as_numpy(self) -> NDArray[float]:
44
98
  """
45
- Create a numpy array of shape (2,) from the vector.
99
+ Create a numpy array of shape (2, ) from the vector.
46
100
  """
47
101
  ...
48
102
 
49
103
  def dot(self, other: Vector2) -> float:
50
104
  """
51
- Compute the dot product of two vectors.
105
+ Compute the dot product of two vectors. The result is a scalar value.
106
+ :param other: the vector to compute the dot product with.
107
+ :return: the scalar dot product of the two vectors.
52
108
  """
53
109
  ...
54
110
 
55
111
  def cross(self, other: Vector2) -> float:
56
112
  """
57
113
  Compute the cross product of two vectors.
114
+ :param other: the vector to compute the cross product with.
115
+ :return: the scalar cross product of the two vectors.
58
116
  """
59
117
  ...
60
118
 
61
119
  def norm(self) -> float:
62
120
  """
63
- Compute the norm of the vector.
121
+ Compute the Euclidian norm (aka magnitude, length) of the vector.
64
122
  """
65
123
  ...
66
124
 
67
125
  def normalized(self) -> Vector2:
68
126
  """
69
- Return a normalized version of the vector.
127
+ Return a normalized version of the vector. The normalized vector will have the same direction as the original
128
+ vector, but with a magnitude of 1.
70
129
  """
71
130
  ...
72
131
 
73
132
  def angle_to(self, other: Vector2) -> float:
74
133
  """
75
134
  Compute the smallest angle between two vectors and return it in radians.
135
+
136
+ :param other: the vector to compute the angle to.
137
+ :return: the angle between the two vectors in radians.
76
138
  """
77
139
  ...
78
140
 
79
141
 
80
142
  class Point2(Iterable[float]):
143
+ """
144
+ A class representing a point in 2D space. The point contains an x and y component. It is iterable and will yield
145
+ the x and y components in order, allowing the Python unpacking operator `*` to be used to compensate for the lack
146
+ of function overloading through some other parts of the library.
147
+
148
+ A point has different semantics than a vector when it comes to transformations and some mathematical operations.
149
+ """
150
+
151
+ def __iter__(self) -> Iterator[float]:
152
+ pass
153
+
81
154
  def __init__(self, x: float, y: float):
82
155
  """
83
-
84
- :param x:
85
- :param y:
156
+ Create a 2D point from the given x and y components.
157
+ :param x: the x component of the point.
158
+ :param y: the y component of the point.
86
159
  """
87
160
  ...
88
161
 
89
162
  @property
90
163
  def x(self) -> float:
164
+ """
165
+ Access the x component of the point as a floating point value.
166
+ """
91
167
  ...
92
168
 
93
169
  @property
94
170
  def y(self) -> float:
171
+ """
172
+ Access the y component of the point as a floating point value.
173
+ """
95
174
  ...
96
175
 
97
176
  @property
98
177
  def coords(self) -> Vector2:
99
178
  """
100
- Get the coordinates of the point as a Vector2 object.
101
- :return: a Vector2 object
179
+ Get the coordinates of the point as a `Vector2` object.
180
+ :return: a `Vector2` object with the same x and y components as the point.
102
181
  """
103
182
  ...
104
183
 
105
184
  def __sub__(self, other: PointOrVec2) -> PointOrVec2:
185
+ """
186
+ Subtract a point or vector from this point. Subtracting a point from a point will return a new vector, while
187
+ subtracting a vector from a point will return a new point.
188
+ :param other: a point or vector to subtract from the point.
189
+ :return: a new point or vector that is the result of the subtraction.
190
+ """
106
191
  ...
107
192
 
108
193
  def __add__(self, other: Vector2) -> Vector2:
194
+ """
195
+ Add a vector to this point.
196
+ :param other: the vector to add to the point.
197
+ :return: a new point that is the result of the addition.
198
+ """
109
199
  ...
110
200
 
111
- def as_numpy(self) -> numpy.ndarray[float]:
201
+ def __mul__(self, other: float) -> Point2:
112
202
  """
113
- Create a numpy array of shape (2,) from the point.
203
+ Multiply the point's x and y components by a scalar value, returning a new point.
204
+ :param other: the scalar value to multiply the point by.
205
+ :return: a new point that is the result of the multiplication.
206
+ """
207
+ ...
208
+
209
+ def __truediv__(self, other) -> Point2:
210
+ """
211
+ Divide the point's x and y components by a scalar value, returning a new point.
212
+ :param other: the scalar value to divide the point by.
213
+ :return: a new point that is the result of the division.
214
+ """
215
+ ...
216
+
217
+ def __rmul__(self, other) -> Point2:
218
+ """
219
+ Multiply the point's x and y components by a scalar value, returning a new point. This allows the scalar to be
220
+ on the left side of the multiplication.
221
+ :param other: the scalar value to multiply the point by.
222
+ :return: a new point that is the result of the multiplication.
223
+ """
224
+ ...
225
+
226
+ def __neg__(self) -> Point2:
227
+ """
228
+ Invert the point by negating the x and y components.
229
+ :return: a new point in which the x and y components are negated.
230
+ """
231
+ ...
232
+
233
+ def as_numpy(self) -> NDArray[float]:
234
+ """
235
+ Create a numpy array of shape (2, ) from the point.
114
236
  """
115
237
  ...
116
238
 
117
239
 
118
240
  class SurfacePoint2:
241
+ """
242
+ This class is used to represent a surface point in 2D space.
243
+
244
+ Surface points are a composite structure that consist of a point in space and a normal direction. Conceptually, they
245
+ come from metrology as a means of representing a point on the surface of an object along with the normal direction
246
+ of the surface at that point. However, they are also isomorphic with the concept of a ray or a parameterized line
247
+ with a direction of unit length, and can be used in that way as well.
248
+ """
249
+
119
250
  def __init__(self, x: float, y: float, nx: float, ny: float):
120
251
  """
252
+ Create a surface point from the given x and y components and the normal vector components. The normal vector
253
+ components will be normalized automatically upon creation. If the normal vector is the zero vector, an
254
+ exception will be thrown.
121
255
 
122
- :param x:
123
- :param y:
124
- :param nx:
125
- :param ny:
256
+ :param x: the x component of the point.
257
+ :param y: the y component of the point.
258
+ :param nx: the x component of the normal vector.
259
+ :param ny: the y component of the normal vector.
126
260
  """
127
261
  ...
128
262
 
@@ -200,14 +334,72 @@ class SurfacePoint2:
200
334
  """
201
335
  ...
202
336
 
337
+ def rot_normal(self, angle: float) -> SurfacePoint2:
338
+ """
339
+ Rotate the normal vector of the surface point by a given angle in radians and return a new surface point. The
340
+ position of the surface point is not affected. The angle is positive for counter-clockwise rotation and negative
341
+ for clockwise rotation.
342
+
343
+ :param angle: the angle to rotate the normal vector by.
344
+ :return: a new surface point with the rotated normal vector.
345
+ """
346
+
347
+ def __mul__(self, other: float) -> SurfacePoint2:
348
+ """
349
+ Multiply the position of the surface point by a scalar value. The normal vector is not affected unless the
350
+ scalar is negative, in which case the normal vector is inverted.
351
+ :param other: the scalar value to multiply the position by.
352
+ :return: a new surface point with the position multiplied by the scalar.
353
+ """
354
+ ...
355
+
356
+ def __rmul__(self, other: float) -> SurfacePoint2:
357
+ """
358
+ Multiply the position of the surface point by a scalar value. The normal vector is not affected unless the
359
+ scalar is negative, in which case the normal vector is inverted.
360
+ :param other: the scalar value to multiply the position by.
361
+ :return: a new surface point with the position multiplied by the scalar.
362
+ """
363
+ ...
364
+
365
+ def __truediv__(self, other: float) -> SurfacePoint2:
366
+ """
367
+ Divide the position of the surface point by a scalar value. The normal vector is not affected unless the
368
+ scalar is negative, in which case the normal vector is inverted.
369
+ :param other: the scalar value to divide the position by.
370
+ :return: a new surface point with the position divided by the scalar.
371
+ """
372
+ ...
373
+
374
+ def __neg__(self) -> SurfacePoint2:
375
+ """
376
+ Invert both the position AND the normal vector of the surface point.
377
+ """
378
+ ...
379
+
203
380
 
204
381
  class Iso2:
382
+ """
383
+ A class representing an isometry in 2D space. An isometry is a transformation that preserves distances and angles,
384
+ also sometimes known as a rigid body transformation. It is composed of a translation and a rotation.
385
+
386
+ `Iso2` objects can be used to transform points, vectors, surface points, other isometries, and a number of other
387
+ 2D geometric constructs.
388
+ """
389
+
205
390
  def __init__(self, tx: float, ty: float, r: float):
206
391
  """
392
+ Create an isometry from a translation and a rotation. The translation is represented by the x and y components
393
+ of the translation vector. The rotation is represented by the angle in radians, and will be a rotation around
394
+ the origin of the coordinate system.
395
+
396
+ In convention with typical transformation matrices, transforming by an isometry constructed this way is the
397
+ equivalent of first rotating by the angle `r` and then translating by the vector `(tx, ty)`.
207
398
 
208
- :param tx:
209
- :param ty:
210
- :param r:
399
+ :param tx: the x component of the translation vector.
400
+ :param ty: the y component of the translation vector.
401
+ :param r: the angle of rotation in radians around the origin, where a positive value is a counter-clockwise
402
+ rotation.
211
403
  """
212
404
  ...
213
405
 
@@ -219,109 +411,240 @@ class Iso2:
219
411
  ...
220
412
 
221
413
  def __matmul__(self, other: Transformable2) -> Transformable2:
414
+ """
415
+ Transform a point, vector, or other transformable object by the isometry using the matrix multiplication
416
+ operator. The transform must be on the right side of the operator, and the object being transformed must be on
417
+ the left side. This is the equivalent of multiplying the object by the isometry matrix.
418
+
419
+ When composing multiple isometries together, remember that the order of operations is reversed. For example, if
420
+ you have isometries A, B, and C, and you want to compose them together such that they are the equivalent of
421
+ first applying A, then B, then C, you would write `D = C @ B @ A`.
422
+
423
+ :param other: the object to transform.
424
+ :return: an object of the same type as the input, transformed by the isometry.
425
+ """
222
426
  ...
223
427
 
224
428
  def inverse(self) -> Iso2:
225
429
  """
226
- Get the inverse of the isometry.
430
+ Get the inverse of the isometry, which is the isometry that undoes the transformation of the original isometry,
431
+ or the isometry that when composed with the original isometry produces the identity isometry.
227
432
  """
228
433
  ...
229
434
 
230
- def as_numpy(self) -> numpy.ndarray[float]:
435
+ def as_numpy(self) -> NDArray[float]:
231
436
  """
232
437
  Create a numpy array of shape (3, 3) from the isometry.
233
438
  """
234
439
  ...
235
440
 
236
- def transform_points(self, points: numpy.ndarray[float]) -> numpy.ndarray[float]:
441
+ def transform_points(self, points: NDArray[float]) -> NDArray[float]:
237
442
  """
238
- Transform an array of points using the isometry.
443
+ Transform an array of points using the isometry. The semantics of transforming points are such that the full
444
+ matrix is applied, first rotating the point around the origin and then translating it by the translation vector.
445
+
446
+ To transform vectors, use the `transform_vectors` method instead.
447
+
448
+ This is an efficient way to transform a large number of points at once, rather than using the `@` operator
449
+ individually on a large number of `Point2` objects.
450
+
239
451
  :param points: a numpy array of shape (N, 2)
240
- :return: a numpy array of shape (N, 2)
452
+ :return: a numpy array of shape (N, 2) containing the transformed points in the same order as the input.
241
453
  """
242
454
  ...
243
455
 
244
- def transform_vectors(self, vectors: numpy.ndarray[float]) -> numpy.ndarray[float]:
456
+ def transform_vectors(self, vectors: NDArray[float]) -> NDArray[float]:
245
457
  """
246
- Transform an array of vectors using the isometry. The translation part of the isometry is ignored.
247
- :param vectors:
248
- :return:
458
+ Transform an array of vectors using the isometry. The semantics of transforming vectors are such that only the
459
+ rotation matrix is applied, and the translation vector is not used. The vectors retain their original
460
+ magnitude, but their direction is rotated by the isometry.
461
+
462
+ To transform points, use the `transform_points` method instead.
463
+
464
+ This is an efficient way to transform a large number of vectors at once, rather than using the `@` operator
465
+ individually on a large number of `Vector2` objects.
466
+
467
+ :param vectors: a numpy array of shape (N, 2)
468
+ :return: a numpy array of shape (N, 2) containing the transformed vectors in the same order as the input.
249
469
  """
250
470
  ...
251
471
 
252
472
 
253
473
  class SvdBasis2:
474
+ """
475
+ A class which creates a set of orthonormal basis vectors from a set of points in 2D space. The basis is created
476
+ using a singular value decomposition of the points, and is very similar to the statistical concept of principal
477
+ component analysis.
478
+
479
+ The basis can be used to determine the rank of the point set, the variance of the points along the basis vectors,
480
+ and to extract an isometry that will transform points from the world space to the basis space. It is useful for
481
+ orienting unknown point sets in a consistent way, for finding best-fit lines or planes, and for other similar
482
+ tasks.
483
+ """
254
484
 
255
485
  def __init__(
256
486
  self,
257
- points: numpy.ndarray[float],
258
- weights: numpy.ndarray[float] | None = None
487
+ points: NDArray[float],
488
+ weights: NDArray[float] | None = None
259
489
  ):
260
490
  """
491
+ Create a basis from a set of points. The basis will be calculated using a singular value decomposition of the
492
+ points.
493
+
494
+ :param points: a numpy array of shape (n, 2) containing the points to calculate the basis from.
495
+ :param weights: a numpy array of shape (n,) containing the weights of the points. If None, all points will be
496
+ weighted equally.
497
+ """
498
+ ...
499
+
500
+ def rank(self, tol: float) -> int:
501
+ """
502
+ Retrieve the rank of the decomposition by counting the number of singular values that are
503
+ greater than the provided tolerance. A rank of 0 indicates that all singular values are
504
+ less than the tolerance, and thus the point set is essentially a single point. A rank of 1
505
+ indicates that the point set is essentially a line. A rank of 2 indicates that the point
506
+ set exists roughly in a plane.
507
+
508
+ The singular values do not directly have a clear physical meaning. They are square roots of
509
+ the variance multiplied by the number of points used to compute the basis. Thus, they can
510
+ be interpreted in relation to each other, and when they are very small.
511
+
512
+ This method should be used either when you know roughly what a cutoff tolerance for the
513
+ problem you're working on should be, or when you know the cutoff value should be very
514
+ small. Otherwise, consider examining the standard deviations of the basis vectors
515
+ instead, as they will be easier to interpret (`basis_stdevs()`).
516
+ :param tol: the tolerance to use when determining the rank.
517
+ :return: the rank of the decomposition.
518
+ """
519
+ ...
520
+
521
+ def largest(self) -> Vector2:
522
+ """
523
+ Get the largest singular vector of the basis.
524
+ :return: the largest singular vector.
525
+ """
526
+ ...
527
+
528
+ def smallest(self) -> Vector2:
529
+ """
530
+ Get the smallest singular vector of the basis.
531
+ :return: the smallest singular vector.
532
+ """
533
+ ...
534
+
535
+ def basis_variances(self) -> NDArray[float]:
536
+ """
537
+ Get the variance of the points along the singular vectors.
538
+ :return: a numpy array of the variance of the points along the singular vectors.
539
+ """
540
+ ...
541
+
542
+ def basis_stdevs(self) -> NDArray[float]:
543
+ """
544
+ Get the standard deviation of the points along the singular vectors.
545
+ :return: a numpy array of the standard deviation of the points along the singular vectors.
546
+ """
547
+ ...
548
+
549
+ def to_iso2(self) -> Iso2:
550
+ """
551
+ Produce an isometry which will transform from the world space to the basis space.
261
552
 
262
- :param points:
263
- :param weights:
553
+ For example, if the basis is created from a set of points that lie roughly on an arbitrary line, multiplying
554
+ original points by this isometry will move the points such that all points are aligned with the x-axis.
555
+ :return: the isometry that transforms from the world space to the basis space.
264
556
  """
265
557
  ...
266
558
 
267
559
 
268
560
  class CurveStation2:
269
561
  """
270
- A class representing a station along a curve in 3D space. The station is represented by a point on the curve, a
562
+ A class representing a station along a curve in 2D space. The station is represented by a point on the curve, a
271
563
  tangent (direction) vector, and a length along the curve.
564
+
565
+ These are created as the result of position finding operations on `Curve2` objects.
272
566
  """
273
567
 
274
568
  @property
275
569
  def point(self) -> Point2:
276
- """ The 2d position in space on the curve. """
570
+ """
571
+ Get the point in 2D world space where the station is located.
572
+ :return: the point in 2D world space.
573
+ """
277
574
  ...
278
575
 
279
576
  @property
280
577
  def direction(self) -> Vector2:
281
- """ The tangent (direction) vector of the curve at the station. """
578
+ """
579
+ Get the direction vector of the curve at the location of the station. This is the tangent vector of the curve,
580
+ and is typically the direction from the previous vertex to the next vertex.
581
+ :return: the direction vector of the curve at the station.
582
+ """
282
583
  ...
283
584
 
284
585
  @property
285
586
  def normal(self) -> Vector2:
286
- """ The normal vector of the curve at the station. """
587
+ """
588
+ Get the normal vector of the curve at the location of the station. This is the vector that is orthogonal to the
589
+ direction vector, and is the direction vector at the station rotated by -90 degrees. When the curve represents
590
+ a manifold surface, this vector represents the direction of the surface normal.
591
+ :return: the surface normal vector of the curve at the station.
592
+ """
287
593
  ...
288
594
 
289
595
  @property
290
596
  def direction_point(self) -> SurfacePoint2:
291
597
  """
292
- A `SurfacePoint2` object representing the point on the curve and the curve's tangent/direction vector.
598
+ Get the combined point and direction vector of the curve at the location of the station, returned as a
599
+ `SurfacePoint2` object.
600
+ :return: the combined point and direction vector of the curve at the station.
293
601
  """
294
602
  ...
295
603
 
296
604
  @property
297
605
  def surface_point(self) -> SurfacePoint2:
298
606
  """
299
- A `SurfacePoint2` object representing the point on the curve and the curve's normal vector.
607
+ Get the combined point and normal vector of the curve at the location of the station, returned as a
608
+ `SurfacePoint2` object.
609
+ :return: the combined point and normal vector of the curve at the station.
300
610
  """
301
611
  ...
302
612
 
303
613
  @property
304
614
  def index(self) -> int:
305
- """ The index of the previous vertex on the curve, at or before the station. """
615
+ """
616
+ Get the index of the previous vertex on the curve, at or before the station.
617
+ :return: the index of the previous vertex on the curve.
618
+ """
306
619
  ...
307
620
 
308
621
  @property
309
622
  def length_along(self) -> float:
310
- """ The length along the curve from the start of the curve to the station. """
623
+ """
624
+ Get the length along the curve to the station, starting at the first vertex of the curve.
625
+ :return: the length along the curve to the station.
626
+ """
311
627
  ...
312
628
 
313
629
 
314
630
  class Curve2:
315
631
  """
316
632
  A class representing a curve in 2D space. The curve is defined by a set of vertices and the line segments between
317
- them. In two dimensions, the curve also has the concepts of closed/open, surface direction, and hull.
633
+ them (also known as a polyline).
318
634
 
635
+ Because the curve is in 2D space, it also has a concept of a surface normal direction, which is orthogonal to the
636
+ tangent direction of the curve at any point. This normal direction allows a `Curve2` to represent a 2D manifold
637
+ surface boundary, defining the concepts of inside and outside. It is commonly used to represent the surface of a
638
+ solid body in a 2D cross-section.
639
+
640
+ Additionally, the `Curve2` object can be used to represent closed regions by connecting the first and last vertices
641
+ and allowing the curve to be treated as a closed loop. This lets the `Curve2` also represent closed polygons.
319
642
  """
320
643
 
321
644
  def __init__(
322
645
  self,
323
- vertices: numpy.ndarray,
324
- normals: numpy.ndarray | None = None,
646
+ vertices: NDArray[float],
647
+ normals: NDArray[float] | None = None,
325
648
  tol: float = 1e-6,
326
649
  force_closed: bool = False,
327
650
  hull_ccw: bool = False,
@@ -334,11 +657,14 @@ class Curve2:
334
657
  to model a manifold surface.
335
658
 
336
659
  There are three ways to specify the winding order of the vertices:
660
+
337
661
  1. Control it manually by passing the vertices array with the rows already organized so that an exterior surface
338
662
  is counter-clockwise.
663
+
339
664
  2. If the vertices represent an exterior shape, pass `hull_ccw=True` to have the constructor automatically
340
665
  check the winding order and reverse it if point ordering in the convex hull does not match ordering in the
341
666
  original array.
667
+
342
668
  3. Pass a `normals` array the same size as the `vertices` array, where the normals are non-zero vectors pointed
343
669
  in the "outside" direction at each point. The constructor will reverse the winding if the majority of normals
344
670
  do not point in the same direction as the winding.
@@ -358,7 +684,7 @@ class Curve2:
358
684
 
359
685
  def length(self) -> float:
360
686
  """
361
- Get the length of the curve.
687
+ Get the total length of the curve as a scalar value.
362
688
  :return: the length of the curve.
363
689
  """
364
690
  ...
@@ -462,7 +788,7 @@ class Curve2:
462
788
  """
463
789
  ...
464
790
 
465
- def make_hull(self) -> numpy.ndarray[float]:
791
+ def make_hull(self) -> NDArray[int]:
466
792
  """
467
793
  Get the vertices of a convex hull of the curve, in counter-clockwise order.
468
794
  :return: a numpy array of shape (N, 2) representing the convex hull of the curve.
@@ -486,7 +812,7 @@ class Curve2:
486
812
  ...
487
813
 
488
814
  @property
489
- def points(self) -> numpy.ndarray[float]:
815
+ def points(self) -> NDArray[float]:
490
816
  """
491
817
  Get the points of the curve.
492
818
  :return: a numpy array of shape (N, 2) representing the points of the curve.
@@ -501,7 +827,7 @@ class Curve2:
501
827
  """
502
828
  ...
503
829
 
504
- def resample(self, resample: Resample) -> Curve2:
830
+ def resample(self, resample: ResampleEnum) -> Curve2:
505
831
  """
506
832
  Resample the curve using the given resampling method. The resampling method can be one of the following:
507
833
 
@@ -522,22 +848,32 @@ class Curve2:
522
848
  """
523
849
  ...
524
850
 
851
+ def to_3d(self) -> geom3.Curve3:
852
+ """
853
+ Convert the curve to a 3D curve by adding a z-coordinate of 0 to all points.
854
+ :return: a new `Curve3` object representing the curve in 3D space.
855
+ """
856
+ ...
857
+
525
858
 
526
859
  class Circle2:
860
+ """
861
+ A class representing a circle in 2D space. The circle is defined by a center point and a radius.
862
+ """
863
+
527
864
  def __init__(self, x: float, y: float, r: float):
528
865
  """
529
-
530
- :param x:
531
- :param y:
532
- :param r:
866
+ Create a circle from the given center point and radius.
867
+ :param x: the x-coordinate of the center of the circle.
868
+ :param y: the y-coordinate of the center of the circle.
869
+ :param r: the radius of the circle.
533
870
  """
534
871
  ...
535
872
 
536
-
537
873
  @property
538
874
  def center(self) -> Point2:
539
875
  """
540
- Get the center of the circle.
876
+ Get the `Point2` at the center of the circle.
541
877
  :return: the center of the circle.
542
878
  """
543
879
  ...
@@ -576,20 +912,36 @@ class Circle2:
576
912
 
577
913
 
578
914
  class Arc2:
915
+ """
916
+ An arc in 2D space. The arc is defined by a center point, a radius, a start angle, and a sweep angle.
917
+
918
+ * The center point and the radius define the circle of which the arc is part.
919
+
920
+ * The start angle is the angle in radians from the positive x-axis to the point where the arc begins. A positive
921
+ value is a counter-clockwise rotation, so a start angle of $\\pi / 2$ would start the arc at the top $y=r$ of the
922
+ circle.
923
+
924
+ * The sweep angle is the angle in radians that the arc covers, beginning at the starting point. A positive value is
925
+ a counter-clockwise rotation, a negative value is clockwise.
926
+ """
927
+
579
928
  def __init__(self, x: float, y: float, r: float, start_radians: float, sweep_radians: float):
580
929
  """
930
+ Create an arc from the given center point, radius, start angle, and sweep angle.
581
931
 
582
- :param x:
583
- :param y:
584
- :param r:
585
- :param start_radians:
586
- :param sweep_radians:
932
+ :param x: the x-coordinate of the center of the arc.
933
+ :param y: the y-coordinate of the center of the arc.
934
+ :param r: the radius of the arc.
935
+ :param start_radians: the start angle of the arc in radians, which is the angle from the positive x-axis to the
936
+ starting point of the arc. A positive value is a counter-clockwise rotation.
937
+ :param sweep_radians: the sweep angle of the arc in radians, which is the angle that the arc covers, beginning
938
+ at the starting point. A positive value is a counter-clockwise rotation, a negative value is clockwise.
587
939
  """
588
940
 
589
941
  @property
590
942
  def center(self) -> Point2:
591
943
  """
592
- Get the center of the arc.
944
+ Get the center point of the arc.
593
945
  :return: the center of the arc.
594
946
  """
595
947
  ...
@@ -597,31 +949,31 @@ class Arc2:
597
949
  @property
598
950
  def x(self) -> float:
599
951
  """
600
- Get the x-coordinate of the arc.
601
- :return: the x-coordinate of the arc.
952
+ Get the x-coordinate of the center of the arc.
953
+ :return: the x-coordinate of the arc center.
602
954
  """
603
955
  ...
604
956
 
605
957
  @property
606
958
  def y(self) -> float:
607
959
  """
608
- Get the y-coordinate of the arc.
609
- :return: the y-coordinate of the arc.
960
+ Get the y-coordinate of the center of the arc.
961
+ :return: the y-coordinate of the arc center
610
962
  """
611
963
  ...
612
964
 
613
965
  @property
614
966
  def r(self) -> float:
615
967
  """
616
- Get the radius of the arc.
617
- :return: the radius of the arc.
968
+ Get the radius of the arc
969
+ :return: the radius of the arc
618
970
  """
619
971
  ...
620
972
 
621
973
  @property
622
974
  def start(self) -> float:
623
975
  """
624
- Get the start angle of the arc in radians.
976
+ Get the start angle of the arc, in radians.
625
977
  :return: the start angle of the arc in radians.
626
978
  """
627
979
  ...
@@ -629,7 +981,7 @@ class Arc2:
629
981
  @property
630
982
  def sweep(self) -> float:
631
983
  """
632
- Get the sweep angle of the arc in radians.
984
+ Get the sweep angle of the arc, in radians.
633
985
  :return: the sweep angle of the arc in radians.
634
986
  """
635
987
  ...
@@ -658,14 +1010,28 @@ class Arc2:
658
1010
  """
659
1011
  ...
660
1012
 
1013
+
661
1014
  class Aabb2:
662
- def __init__(self, x_min: float, x_max: float, y_min: float, y_max: float):
1015
+ """
1016
+ A class representing an axis-aligned bounding box in 2D space. The bounding box is defined by a minimum point and a
1017
+ maximum point, which are the lower-left and upper-right corners of the box, respectively.
1018
+
1019
+ Bounding boxes are typically used for accelerating intersection and distance queries and are used internally inside
1020
+ the Rust language `engeom` library for this purpose. However, they have other useful applications and so are
1021
+ exposed here in the Python API.
1022
+
1023
+ Typically, `Aabb2` objects will be retrieved from other `engeom` objects which use them internally, such as curves,
1024
+ circles, arcs, etc. However, they can also be created and manipulated directly.
1025
+ """
1026
+
1027
+ def __init__(self, x_min: float, y_min: float, x_max: float, y_max: float):
663
1028
  """
1029
+ Create an axis-aligned bounding box from the minimum and maximum coordinates.
664
1030
 
665
- :param x_min:
666
- :param x_max:
667
- :param y_min:
668
- :param y_max:
1031
+ :param x_min: the minimum x-coordinate of the AABB
1032
+ :param y_min: the minimum y-coordinate of the AABB
1033
+ :param x_max: the maximum x-coordinate of the AABB
1034
+ :param y_max: the maximum y-coordinate of the AABB
669
1035
  """
670
1036
  ...
671
1037
 
@@ -681,6 +1047,16 @@ class Aabb2:
681
1047
  """
682
1048
  ...
683
1049
 
1050
+ @staticmethod
1051
+ def from_points(points: NDArray[float]) -> Aabb2:
1052
+ """
1053
+ Create an AABB that bounds a set of points. If the point array is empty or the wrong shape, an error will be
1054
+ thrown.
1055
+ :param points: a numpy array of shape (N, 2) containing the points to bound
1056
+ :return: a new AABB object
1057
+ """
1058
+ ...
1059
+
684
1060
  @property
685
1061
  def min(self) -> Point2:
686
1062
  """
@@ -731,4 +1107,4 @@ class Aabb2:
731
1107
  :param d: the distance to shrink the AABB by.
732
1108
  :return: a new AABB object with the shrunk bounds.
733
1109
  """
734
- ...
1110
+ ...