engeom 0.2.8__cp38-abi3-macosx_11_0_arm64.whl → 0.2.10__cp38-abi3-macosx_11_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- engeom/_plot/matplotlib.py +112 -15
- engeom/_plot/pyvista.py +26 -5
- engeom/engeom.abi3.so +0 -0
- engeom/geom2.pyi +40 -0
- engeom/geom3.pyi +295 -0
- engeom/plot.py +1 -1
- engeom/sensor/__init__.py +6 -0
- engeom/sensor.pyi +58 -0
- {engeom-0.2.8.dist-info → engeom-0.2.10.dist-info}/METADATA +1 -1
- {engeom-0.2.8.dist-info → engeom-0.2.10.dist-info}/RECORD +11 -9
- {engeom-0.2.8.dist-info → engeom-0.2.10.dist-info}/WHEEL +0 -0
engeom/_plot/matplotlib.py
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
from typing import List, Iterable, Tuple, Union
|
2
2
|
import numpy
|
3
|
+
|
3
4
|
from .common import LabelPlace
|
4
5
|
from engeom.geom2 import Curve2, Circle2, Aabb2, Point2, Vector2, SurfacePoint2
|
6
|
+
from engeom.geom3 import Vector3, Mesh, Point3
|
5
7
|
from engeom.metrology import Distance2
|
6
8
|
|
7
9
|
PlotCoords = Union[Point2, Vector2, Iterable[float]]
|
10
|
+
PointLike = Union[Point2, Tuple[float, float], Point3]
|
8
11
|
|
9
12
|
try:
|
10
13
|
from matplotlib.pyplot import Axes, Circle
|
@@ -207,7 +210,7 @@ else:
|
|
207
210
|
|
208
211
|
value = distance.value * scale_value
|
209
212
|
box_style = dict(boxstyle="round,pad=0.3", ec="black", fc="white")
|
210
|
-
self.
|
213
|
+
self.text(template.format(value=value), label_coords, bbox=box_style, **kwargs)
|
211
214
|
|
212
215
|
def _line_if_needed(self, pad: float, actual: Point2, leader_end: Point2):
|
213
216
|
half_pad = pad * 0.5
|
@@ -218,31 +221,78 @@ else:
|
|
218
221
|
t1 = work.scalar_projection(leader_end) + half_pad
|
219
222
|
self.arrow(actual, work.at_distance(t1), arrow="-")
|
220
223
|
|
221
|
-
def
|
224
|
+
def text(self, text: str, pos: PlotCoords, shift: PlotCoords | None = None, ha: str = "center",
|
225
|
+
va: str = "center", **kwargs):
|
222
226
|
"""
|
223
|
-
Annotate a Matplotlib Axes object with text only.
|
227
|
+
Annotate a Matplotlib Axes object with text only, by default in the xy data plane.
|
224
228
|
:param text: the text to annotate
|
225
229
|
:param pos: the position of the annotation
|
230
|
+
:param shift: an optional shift vector to apply to the position
|
231
|
+
:param ha: horizontal alignment
|
232
|
+
:param va: vertical alignment
|
226
233
|
:param kwargs: keyword arguments to pass to the annotate function
|
227
234
|
:return: the annotation object
|
228
235
|
"""
|
229
|
-
|
236
|
+
xy = _tuplefy(pos)
|
237
|
+
if shift is not None:
|
238
|
+
shift = _tuplefy(shift)
|
239
|
+
xy = (xy[0] + shift[0], xy[1] + shift[1])
|
240
|
+
|
241
|
+
return self.ax.annotate(text, xy=xy, ha=ha, va=va, **kwargs)
|
230
242
|
|
231
|
-
def
|
243
|
+
def points(self, *points: PlotCoords, marker="o", markersize="5", **kwargs):
|
244
|
+
x, y = zip(*[_tuplefy(p) for p in points])
|
245
|
+
return self.ax.plot(x, y, marker, markersize=markersize, **kwargs)
|
246
|
+
|
247
|
+
def labeled_arrow(self, start: PlotCoords, end: PlotCoords, text: str, fraction: float = 0.5,
|
248
|
+
shift: PlotCoords | None = None,
|
249
|
+
arrow="->", color="black", linewidth: float | None = None, linestyle="-",
|
250
|
+
**text_kwargs):
|
232
251
|
"""
|
233
|
-
|
234
|
-
:param start:
|
235
|
-
:param end:
|
236
|
-
:param
|
237
|
-
:
|
252
|
+
|
253
|
+
:param start:
|
254
|
+
:param end:
|
255
|
+
:param text:
|
256
|
+
:param shift:
|
257
|
+
:param fraction:
|
258
|
+
:param arrow:
|
259
|
+
:param color:
|
260
|
+
:param linewidth:
|
261
|
+
:param linestyle:
|
262
|
+
:param text_kwargs: parameters to pass to the text function
|
263
|
+
:return:
|
238
264
|
"""
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
265
|
+
start = Point2(*_tuplefy(start))
|
266
|
+
end = Point2(*_tuplefy(end))
|
267
|
+
|
268
|
+
self.arrow(start, end, arrow=arrow, color=color, linewidth=linewidth, linestyle=linestyle)
|
269
|
+
|
270
|
+
v: Vector2 = end - start
|
271
|
+
position = start + v * fraction
|
272
|
+
self.text(text, position, shift=shift, color=color, **text_kwargs)
|
273
|
+
|
274
|
+
def arrow(self, start: PlotCoords, end: PlotCoords, arrow="->", color="black", linewidth: float | None = None,
|
275
|
+
linestyle="-"):
|
276
|
+
"""
|
277
|
+
Draw an arrow on a Matplotlib Axes object from `start` to `end`.
|
278
|
+
:param start:
|
279
|
+
:param end:
|
280
|
+
:param arrow:
|
281
|
+
:param color:
|
282
|
+
:param linewidth:
|
283
|
+
:param linestyle:
|
284
|
+
:return:
|
285
|
+
"""
|
286
|
+
props = dict(
|
287
|
+
arrowstyle=arrow,
|
288
|
+
fc=color,
|
289
|
+
ec=color,
|
290
|
+
linewidth=linewidth,
|
291
|
+
linestyle=linestyle,
|
244
292
|
)
|
245
293
|
|
294
|
+
return self.ax.annotate("", xy=_tuplefy(end), xytext=_tuplefy(start), arrowprops=props)
|
295
|
+
|
246
296
|
def _font_height(self, font_size: int) -> float:
|
247
297
|
# Get the height of a font in data units
|
248
298
|
fig_dpi = self.ax.figure.dpi
|
@@ -273,3 +323,50 @@ def _tuplefy(item: PlotCoords) -> Tuple[float, float]:
|
|
273
323
|
else:
|
274
324
|
x, y, *_ = item
|
275
325
|
return x, y
|
326
|
+
|
327
|
+
|
328
|
+
class TraceBuilder:
|
329
|
+
def __init__(self):
|
330
|
+
self.xs = []
|
331
|
+
self.ys = []
|
332
|
+
self.c = []
|
333
|
+
|
334
|
+
def bounds(self) -> Aabb2:
|
335
|
+
xs = [x for x in self.xs if x is not None]
|
336
|
+
ys = [y for y in self.ys if y is not None]
|
337
|
+
return Aabb2(
|
338
|
+
x_min=min(xs),
|
339
|
+
x_max=max(xs),
|
340
|
+
y_min=min(ys),
|
341
|
+
y_max=max(ys),
|
342
|
+
)
|
343
|
+
|
344
|
+
def add_segment(self, *points: PointLike):
|
345
|
+
self.add_points(*points)
|
346
|
+
self.add_blank()
|
347
|
+
|
348
|
+
def add_blank(self):
|
349
|
+
self.xs.append(None)
|
350
|
+
self.ys.append(None)
|
351
|
+
self.c.append(None)
|
352
|
+
|
353
|
+
def add_points(self, *points: PointLike):
|
354
|
+
for x, y, *_ in points:
|
355
|
+
self.xs.append(x)
|
356
|
+
self.ys.append(y)
|
357
|
+
|
358
|
+
def add_point_and_color(self, point: PointLike, color: float):
|
359
|
+
self.xs.append(point[0])
|
360
|
+
self.ys.append(point[1])
|
361
|
+
self.c.append(color)
|
362
|
+
|
363
|
+
def invert_y(self):
|
364
|
+
self.ys = [-y if y is not None else None for y in self.ys]
|
365
|
+
|
366
|
+
@property
|
367
|
+
def kwargs(self):
|
368
|
+
return dict(x=self.xs, y=self.ys)
|
369
|
+
|
370
|
+
@property
|
371
|
+
def xy(self):
|
372
|
+
return self.xs, self.ys
|
engeom/_plot/pyvista.py
CHANGED
@@ -40,6 +40,25 @@ else:
|
|
40
40
|
"""
|
41
41
|
self.plotter = plotter
|
42
42
|
|
43
|
+
def add_points(self, *points, color: pyvista.ColorLike = "b", point_size: float = 5.0,
|
44
|
+
render_points_as_spheres: bool = True, **kwargs) -> pyvista.vtkActor:
|
45
|
+
"""
|
46
|
+
Add one or more points to be plotted.
|
47
|
+
:param points: The points to add.
|
48
|
+
:param color: The color to use for the point(s).
|
49
|
+
:param point_size: The size of the point(s).
|
50
|
+
:param render_points_as_spheres: Whether to render the points as spheres or not.
|
51
|
+
:param kwargs: Additional keyword arguments to pass to the PyVista `Plotter.add_points` method.
|
52
|
+
:return: The PyVista actor that was added to the plotter.
|
53
|
+
"""
|
54
|
+
return self.plotter.add_points(
|
55
|
+
numpy.array([_tuplefy(p) for p in points], dtype=numpy.float64),
|
56
|
+
color=color,
|
57
|
+
point_size=point_size,
|
58
|
+
render_points_as_spheres=render_points_as_spheres,
|
59
|
+
**kwargs
|
60
|
+
)
|
61
|
+
|
43
62
|
def add_curves(
|
44
63
|
self,
|
45
64
|
*curves: Curve3,
|
@@ -204,12 +223,12 @@ else:
|
|
204
223
|
points = numpy.array([[0, 0, 0], [size, 0, 0], [0, size, 0], [0, 0, size]], dtype=numpy.float64)
|
205
224
|
points = iso.transform_points(points)
|
206
225
|
|
207
|
-
self.plotter.add_lines(points[[0, 1]], color="red", width=line_width)
|
208
|
-
|
209
|
-
|
226
|
+
actors = [self.plotter.add_lines(points[[0, 1]], color="red", width=line_width),
|
227
|
+
self.plotter.add_lines(points[[0, 2]], color="green", width=line_width),
|
228
|
+
self.plotter.add_lines(points[[0, 3]], color="blue", width=line_width)]
|
210
229
|
|
211
230
|
if label:
|
212
|
-
self.plotter.add_point_labels(
|
231
|
+
actors.append(self.plotter.add_point_labels(
|
213
232
|
[points[0]],
|
214
233
|
[label],
|
215
234
|
show_points=False,
|
@@ -217,7 +236,9 @@ else:
|
|
217
236
|
font_family="courier",
|
218
237
|
font_size=label_size,
|
219
238
|
bold=False,
|
220
|
-
)
|
239
|
+
))
|
240
|
+
|
241
|
+
return actors
|
221
242
|
|
222
243
|
def label(self, point: PlotCoords, text: str, **kwargs):
|
223
244
|
"""
|
engeom/engeom.abi3.so
CHANGED
Binary file
|
engeom/geom2.pyi
CHANGED
@@ -236,6 +236,13 @@ class Point2(Iterable[float]):
|
|
236
236
|
"""
|
237
237
|
...
|
238
238
|
|
239
|
+
@staticmethod
|
240
|
+
def mid(a: Point2, b: Point2) -> Point2:
|
241
|
+
"""
|
242
|
+
Return the midpoint between two points. This is the average of the x and y components of the two points.
|
243
|
+
"""
|
244
|
+
...
|
245
|
+
|
239
246
|
|
240
247
|
class SurfacePoint2:
|
241
248
|
"""
|
@@ -377,6 +384,23 @@ class SurfacePoint2:
|
|
377
384
|
"""
|
378
385
|
...
|
379
386
|
|
387
|
+
def offset(self, offset: Vector2) -> SurfacePoint2:
|
388
|
+
"""
|
389
|
+
Offset the surface point by a given vector. The normal vector is not affected.
|
390
|
+
:param offset: the vector to offset the surface point by.
|
391
|
+
:return: a new surface point with the position offset by the given vector.
|
392
|
+
"""
|
393
|
+
...
|
394
|
+
|
395
|
+
def shift(self, distance: float) -> SurfacePoint2:
|
396
|
+
"""
|
397
|
+
Shift the surface point by a given distance along the normal vector. The position of the surface point is
|
398
|
+
affected, but the normal vector is not.
|
399
|
+
:param distance: the distance to shift the surface point.
|
400
|
+
:return: a new surface point with the position shifted by the given distance.
|
401
|
+
"""
|
402
|
+
...
|
403
|
+
|
380
404
|
|
381
405
|
class Iso2:
|
382
406
|
"""
|
@@ -855,6 +879,14 @@ class Curve2:
|
|
855
879
|
"""
|
856
880
|
...
|
857
881
|
|
882
|
+
@property
|
883
|
+
def aabb(self) -> Aabb2:
|
884
|
+
"""
|
885
|
+
Get the axis-aligned bounding box of the curve.
|
886
|
+
:return: the axis-aligned bounding box of the curve.
|
887
|
+
"""
|
888
|
+
...
|
889
|
+
|
858
890
|
|
859
891
|
class Circle2:
|
860
892
|
"""
|
@@ -910,6 +942,14 @@ class Circle2:
|
|
910
942
|
"""
|
911
943
|
...
|
912
944
|
|
945
|
+
def point_at_angle(self, angle: float) -> Point2:
|
946
|
+
"""
|
947
|
+
Get the point on the circle at a given angle.
|
948
|
+
:param angle: the angle in radians.
|
949
|
+
:return: the point on the circle at the given angle.
|
950
|
+
"""
|
951
|
+
...
|
952
|
+
|
913
953
|
|
914
954
|
class Arc2:
|
915
955
|
"""
|
engeom/geom3.pyi
CHANGED
@@ -260,6 +260,13 @@ class Point3(Iterable[float]):
|
|
260
260
|
"""
|
261
261
|
...
|
262
262
|
|
263
|
+
@staticmethod
|
264
|
+
def mid(a: Point3, b: Point3) -> Point3:
|
265
|
+
"""
|
266
|
+
Return the midpoint between two points. This is the average of the x, y, and z coordinates of the two points.
|
267
|
+
"""
|
268
|
+
...
|
269
|
+
|
263
270
|
|
264
271
|
class SurfacePoint3:
|
265
272
|
"""
|
@@ -389,6 +396,15 @@ class SurfacePoint3:
|
|
389
396
|
"""
|
390
397
|
...
|
391
398
|
|
399
|
+
def shift(self, offset: float) -> SurfacePoint3:
|
400
|
+
"""
|
401
|
+
Return a new surface point with the position shifted by the specified distance along the normal vector. The
|
402
|
+
normal vector is unchanged.
|
403
|
+
:param offset: the distance to shift the position by along the normal vector.
|
404
|
+
:return:
|
405
|
+
"""
|
406
|
+
...
|
407
|
+
|
392
408
|
|
393
409
|
class Iso3:
|
394
410
|
"""
|
@@ -521,6 +537,153 @@ class Iso3:
|
|
521
537
|
"""
|
522
538
|
...
|
523
539
|
|
540
|
+
def translation(self) -> Iso3:
|
541
|
+
"""
|
542
|
+
Return the translation component of the isometry as a separate isometry.
|
543
|
+
"""
|
544
|
+
...
|
545
|
+
|
546
|
+
def rotation(self) -> Iso3:
|
547
|
+
"""
|
548
|
+
Return the rotation component of the isometry as a separate isometry.
|
549
|
+
"""
|
550
|
+
...
|
551
|
+
|
552
|
+
@staticmethod
|
553
|
+
def from_rx(angle: float) -> Iso3:
|
554
|
+
"""
|
555
|
+
Create an isometry representing a rotation around the x-axis by the specified angle in radians.
|
556
|
+
:param angle: the angle to rotate by in radians.
|
557
|
+
:return: an isometry containing only a rotation component
|
558
|
+
"""
|
559
|
+
...
|
560
|
+
|
561
|
+
@staticmethod
|
562
|
+
def from_ry(angle: float) -> Iso3:
|
563
|
+
"""
|
564
|
+
Create an isometry representing a rotation around the y-axis by the specified angle in radians.
|
565
|
+
:param angle: the angle to rotate by in radians.
|
566
|
+
:return: an isometry containing only a rotation component
|
567
|
+
"""
|
568
|
+
...
|
569
|
+
|
570
|
+
@staticmethod
|
571
|
+
def from_rz(angle: float) -> Iso3:
|
572
|
+
"""
|
573
|
+
Create an isometry representing a rotation around the z-axis by the specified angle in radians.
|
574
|
+
:param angle: the angle to rotate by in radians.
|
575
|
+
:return: an isometry containing only a rotation component
|
576
|
+
"""
|
577
|
+
...
|
578
|
+
|
579
|
+
@staticmethod
|
580
|
+
def from_basis_xy(e0: Vector3, e1: Vector3, origin: Point3 | None = None) -> Iso3:
|
581
|
+
"""
|
582
|
+
Create an isometry from two vectors defining the X and Y axes.
|
583
|
+
|
584
|
+
This method creates an isometry where:
|
585
|
+
- The X axis aligns with e0 (normalized)
|
586
|
+
- The Y axis aligns with e1 (normalized and adjusted to be orthogonal to e0)
|
587
|
+
- The Z axis is calculated as the cross product of X and Y
|
588
|
+
|
589
|
+
:param e0: Vector defining the X axis direction
|
590
|
+
:param e1: Vector defining the approximate Y axis direction (will be orthogonalized)
|
591
|
+
:param origin: Optional point to use as the origin of the isometry (defaults to world origin)
|
592
|
+
:return: A new Iso3 representing the coordinate system
|
593
|
+
:raises: ValueError if the vectors are parallel or too small to create a valid basis
|
594
|
+
"""
|
595
|
+
...
|
596
|
+
|
597
|
+
@staticmethod
|
598
|
+
def from_basis_xz(e0: Vector3, e2: Vector3, origin: Point3 | None = None) -> Iso3:
|
599
|
+
"""
|
600
|
+
Create an isometry from two vectors defining the X and Z axes.
|
601
|
+
|
602
|
+
This method creates an isometry where:
|
603
|
+
- The X axis aligns with e0 (normalized)
|
604
|
+
- The Z axis aligns with e2 (normalized and adjusted to be orthogonal to e0)
|
605
|
+
- The Y axis is calculated as the cross product of Z and X
|
606
|
+
|
607
|
+
:param e0: Vector defining the X axis direction
|
608
|
+
:param e2: Vector defining the approximate Z axis direction (will be orthogonalized)
|
609
|
+
:param origin: Optional point to use as the origin of the isometry (defaults to world origin)
|
610
|
+
:return: A new Iso3 representing the coordinate system
|
611
|
+
:raises: ValueError if the vectors are parallel or too small to create a valid basis
|
612
|
+
"""
|
613
|
+
...
|
614
|
+
|
615
|
+
@staticmethod
|
616
|
+
def from_basis_yz(e1: Vector3, e2: Vector3, origin: Point3 | None = None) -> Iso3:
|
617
|
+
"""
|
618
|
+
Create an isometry from two vectors defining the Y and Z axes.
|
619
|
+
|
620
|
+
This method creates an isometry where:
|
621
|
+
- The Y axis aligns with e1 (normalized)
|
622
|
+
- The Z axis aligns with e2 (normalized and adjusted to be orthogonal to e1)
|
623
|
+
- The X axis is calculated as the cross product of Y and Z
|
624
|
+
|
625
|
+
:param e1: Vector defining the Y axis direction
|
626
|
+
:param e2: Vector defining the approximate Z axis direction (will be orthogonalized)
|
627
|
+
:param origin: Optional point to use as the origin of the isometry (defaults to world origin)
|
628
|
+
:return: A new Iso3 representing the coordinate system
|
629
|
+
:raises: ValueError if the vectors are parallel or too small to create a valid basis
|
630
|
+
"""
|
631
|
+
...
|
632
|
+
|
633
|
+
@staticmethod
|
634
|
+
def from_basis_yx(e1: Vector3, e0: Vector3, origin: Point3 | None = None) -> Iso3:
|
635
|
+
"""
|
636
|
+
Create an isometry from two vectors defining the Y and X axes.
|
637
|
+
|
638
|
+
This method creates an isometry where:
|
639
|
+
- The Y axis aligns with e1 (normalized)
|
640
|
+
- The X axis aligns with e0 (normalized and adjusted to be orthogonal to e1)
|
641
|
+
- The Z axis is calculated as the cross product of X and Y
|
642
|
+
|
643
|
+
:param e1: Vector defining the Y axis direction
|
644
|
+
:param e0: Vector defining the approximate X axis direction (will be orthogonalized)
|
645
|
+
:param origin: Optional point to use as the origin of the isometry (defaults to world origin)
|
646
|
+
:return: A new Iso3 representing the coordinate system
|
647
|
+
:raises: ValueError if the vectors are parallel or too small to create a valid basis
|
648
|
+
"""
|
649
|
+
...
|
650
|
+
|
651
|
+
@staticmethod
|
652
|
+
def from_basis_zx(e2: Vector3, e0: Vector3, origin: Point3 | None = None) -> Iso3:
|
653
|
+
"""
|
654
|
+
Create an isometry from two vectors defining the Z and X axes.
|
655
|
+
|
656
|
+
This method creates an isometry where:
|
657
|
+
- The Z axis aligns with e2 (normalized)
|
658
|
+
- The X axis aligns with e0 (normalized and adjusted to be orthogonal to e2)
|
659
|
+
- The Y axis is calculated as the cross product of Z and X
|
660
|
+
|
661
|
+
:param e2: Vector defining the Z axis direction
|
662
|
+
:param e0: Vector defining the approximate X axis direction (will be orthogonalized)
|
663
|
+
:param origin: Optional point to use as the origin of the isometry (defaults to world origin)
|
664
|
+
:return: A new Iso3 representing the coordinate system
|
665
|
+
:raises: ValueError if the vectors are parallel or too small to create a valid basis
|
666
|
+
"""
|
667
|
+
...
|
668
|
+
|
669
|
+
@staticmethod
|
670
|
+
def from_basis_zy(e2: Vector3, e1: Vector3, origin: Point3 | None = None) -> Iso3:
|
671
|
+
"""
|
672
|
+
Create an isometry from two vectors defining the Z and Y axes.
|
673
|
+
|
674
|
+
This method creates an isometry where:
|
675
|
+
- The Z axis aligns with e2 (normalized)
|
676
|
+
- The Y axis aligns with e1 (normalized and adjusted to be orthogonal to e2)
|
677
|
+
- The X axis is calculated as the cross product of Y and Z
|
678
|
+
|
679
|
+
:param e2: Vector defining the Z axis direction
|
680
|
+
:param e1: Vector defining the approximate Y axis direction (will be orthogonalized)
|
681
|
+
:param origin: Optional point to use as the origin of the isometry (defaults to world origin)
|
682
|
+
:return: A new Iso3 representing the coordinate system
|
683
|
+
:raises: ValueError if the vectors are parallel or too small to create a valid basis
|
684
|
+
"""
|
685
|
+
...
|
686
|
+
|
524
687
|
|
525
688
|
class SvdBasis3:
|
526
689
|
"""
|
@@ -646,6 +809,14 @@ class Plane3:
|
|
646
809
|
"""
|
647
810
|
...
|
648
811
|
|
812
|
+
def intersection_distance(self, sp: SurfacePoint3) -> float | None:
|
813
|
+
"""
|
814
|
+
|
815
|
+
:param sp:
|
816
|
+
:return:
|
817
|
+
"""
|
818
|
+
...
|
819
|
+
|
649
820
|
|
650
821
|
class Mesh:
|
651
822
|
"""
|
@@ -745,6 +916,25 @@ class Mesh:
|
|
745
916
|
"""
|
746
917
|
...
|
747
918
|
|
919
|
+
@property
|
920
|
+
def face_normals(self) -> NDArray[float]:
|
921
|
+
"""
|
922
|
+
Will return an immutable view of the face normals of the mesh as a numpy array of shape (m, 3), where m is the
|
923
|
+
number of triangles in the mesh.
|
924
|
+
:return: a numpy array of shape (m, 3) containing the normals of the triangles of the mesh.
|
925
|
+
"""
|
926
|
+
...
|
927
|
+
|
928
|
+
@property
|
929
|
+
def vertex_normals(self) -> NDArray[float]:
|
930
|
+
"""
|
931
|
+
Will return an immutable view of the vertex normals of the mesh as a numpy array of shape (n, 3), where n is the
|
932
|
+
number of vertices in the mesh. If a vertex has no faces, the normal will be (0, 0, 0), otherwise the normal
|
933
|
+
will have been averaged from the normals of the faces that share the vertex.
|
934
|
+
:return: a numpy array of shape (n, 3) containing the normals of the vertices of the mesh.
|
935
|
+
"""
|
936
|
+
...
|
937
|
+
|
748
938
|
@property
|
749
939
|
def faces(self) -> NDArray[numpy.uint32]:
|
750
940
|
"""
|
@@ -933,6 +1123,66 @@ class Mesh:
|
|
933
1123
|
"""
|
934
1124
|
...
|
935
1125
|
|
1126
|
+
def visual_outline(
|
1127
|
+
self,
|
1128
|
+
facing: Vector3,
|
1129
|
+
max_edge_length: float,
|
1130
|
+
corner_angle: float | None = None
|
1131
|
+
) -> Tuple[NDArray[float], NDArray[numpy.uint8]]:
|
1132
|
+
"""
|
1133
|
+
Capture the edges of a visual outline of the mesh, used to draw a line diagram of the mesh in a 2D space. The
|
1134
|
+
returned results will consist of two numpy arrays. The first will be a floating point array of shape (N, 6)
|
1135
|
+
where N is the number of edges in the outline. The first three columns will be the start point of the edge, and
|
1136
|
+
the last three columns will be the end point of the edge. The second array will be a numpy array of shape (N,)
|
1137
|
+
containing a 0 or 1 for each edge at the associated index. A 0 indicates that the edge is unobstructed in the
|
1138
|
+
view direction, while a 1 indicates that the edge is obstructed by the mesh.
|
1139
|
+
|
1140
|
+
:param facing: A vector with the direction to look at the mesh
|
1141
|
+
:param max_edge_length: The maximum length of an edge to be included in the outline. Edges longer than this
|
1142
|
+
will be broken up into smaller edges.
|
1143
|
+
:param corner_angle: The minimum angle between two adjacent faces for the common edge to be considered a corner
|
1144
|
+
and included in the outline. If None, the default value is 45 degrees.
|
1145
|
+
:return: a tuple of two numpy arrays. The first array is the outline edges, and the second array is a mask
|
1146
|
+
indicating whether the edge is obstructed or not.
|
1147
|
+
"""
|
1148
|
+
...
|
1149
|
+
|
1150
|
+
def convex_hull(self) -> Mesh:
|
1151
|
+
"""
|
1152
|
+
Calculate the convex hull of the mesh. The convex hull is the smallest convex shape that contains all the
|
1153
|
+
vertices of the mesh. This will return a new mesh object containing the vertices and triangles of the convex
|
1154
|
+
hull. This method will not modify the original mesh.
|
1155
|
+
:return: a new mesh object containing the convex hull of the original mesh.
|
1156
|
+
"""
|
1157
|
+
...
|
1158
|
+
|
1159
|
+
@staticmethod
|
1160
|
+
def create_box(width: float, height: float, depth: float) -> Mesh:
|
1161
|
+
"""
|
1162
|
+
Creates a box with the corner at the origin and the specified width, height, and depth all positive.
|
1163
|
+
|
1164
|
+
:param width:
|
1165
|
+
:param height:
|
1166
|
+
:param depth:
|
1167
|
+
:return:
|
1168
|
+
"""
|
1169
|
+
...
|
1170
|
+
|
1171
|
+
@staticmethod
|
1172
|
+
def create_cylinder(radius: float, height: float, steps: int) -> Mesh:
|
1173
|
+
"""
|
1174
|
+
Creates a cylinder with a radius and height. The cylinder will be centered at the origin and oriented along the
|
1175
|
+
Z-axis. The bottom of the cylinder will be at Z = 0 and the top will be at Z = height. The cylinder will be
|
1176
|
+
created with a number of steps around the circumference, which will determine the number of vertices used to
|
1177
|
+
create the cylinder. The number of steps should be at least 3. The first set of vertices will be at X = radius,
|
1178
|
+
Y = 0.
|
1179
|
+
:param radius:
|
1180
|
+
:param height:
|
1181
|
+
:param steps:
|
1182
|
+
:return:
|
1183
|
+
"""
|
1184
|
+
...
|
1185
|
+
|
936
1186
|
|
937
1187
|
class FaceFilterHandle:
|
938
1188
|
"""
|
@@ -1009,6 +1259,51 @@ class FaceFilterHandle:
|
|
1009
1259
|
...
|
1010
1260
|
|
1011
1261
|
|
1262
|
+
class MeshCollisionSet:
|
1263
|
+
"""
|
1264
|
+
A class holding a set of meshes for collision detection. This class is used to perform collision detection between
|
1265
|
+
a set of moving and stationary meshes in 3D space.
|
1266
|
+
"""
|
1267
|
+
|
1268
|
+
def __init__(self):
|
1269
|
+
"""
|
1270
|
+
Creates an empty collision set.
|
1271
|
+
"""
|
1272
|
+
...
|
1273
|
+
|
1274
|
+
def add_stationary(self, mesh: Mesh) -> int:
|
1275
|
+
"""
|
1276
|
+
Add a stationary mesh to the collision set. This mesh will be used as a reference for collision detection.
|
1277
|
+
:param mesh: the mesh to add to the collision set.
|
1278
|
+
:return: the ID of the mesh in the collision set.
|
1279
|
+
"""
|
1280
|
+
...
|
1281
|
+
|
1282
|
+
def add_moving(self, mesh: Mesh) -> int:
|
1283
|
+
"""
|
1284
|
+
Add a moving mesh to the collision set. This mesh will be used to check for collisions against the stationary
|
1285
|
+
meshes in the set.
|
1286
|
+
:param mesh: the mesh to add to the collision set.
|
1287
|
+
:return: the ID of the mesh in the collision set.
|
1288
|
+
"""
|
1289
|
+
...
|
1290
|
+
|
1291
|
+
def add_exception(self, id1: int, id2: int):
|
1292
|
+
"""
|
1293
|
+
Add an exception to the collision set. This will prevent the two meshes from being checked for collisions.
|
1294
|
+
:param id1: the ID of the first mesh.
|
1295
|
+
:param id2: the ID of the second mesh.
|
1296
|
+
"""
|
1297
|
+
...
|
1298
|
+
|
1299
|
+
def check_all(self, transforms: List[Tuple[int, Iso3]], stop_at_first: bool) -> List[Tuple[int, int]]:
|
1300
|
+
"""
|
1301
|
+
Check all moving meshes against all stationary meshes for collisions. This will return a list of tuples
|
1302
|
+
containing the IDs of the two meshes that are colliding.
|
1303
|
+
:return: a list of tuples containing the IDs of the colliding meshes.
|
1304
|
+
"""
|
1305
|
+
...
|
1306
|
+
|
1012
1307
|
class CurveStation3:
|
1013
1308
|
"""
|
1014
1309
|
A class representing a station along a curve in 3D space. The station is represented by a point on the curve, a
|
engeom/plot.py
CHANGED
engeom/sensor.pyi
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from .geom3 import Point3, Mesh, Iso3, Vector3
|
3
|
+
from numpy.typing import NDArray
|
4
|
+
|
5
|
+
|
6
|
+
class LaserLine:
|
7
|
+
def __init__(
|
8
|
+
self,
|
9
|
+
ray_origin: Point3,
|
10
|
+
detect_origin: Point3,
|
11
|
+
line_start: Point3,
|
12
|
+
line_end: Point3,
|
13
|
+
min_range: float,
|
14
|
+
max_range: float,
|
15
|
+
rays: int,
|
16
|
+
angle_limit: float | None = None,
|
17
|
+
):
|
18
|
+
"""
|
19
|
+
|
20
|
+
:param ray_origin:
|
21
|
+
:param detect_origin:
|
22
|
+
:param line_start:
|
23
|
+
:param line_end:
|
24
|
+
:param min_range:
|
25
|
+
:param max_range:
|
26
|
+
:param rays:
|
27
|
+
:param angle_limit:
|
28
|
+
"""
|
29
|
+
...
|
30
|
+
|
31
|
+
def get_points(self, target: Mesh, obstruction: Mesh | None, iso: Iso3) -> NDArray[float]:
|
32
|
+
"""
|
33
|
+
|
34
|
+
:param target:
|
35
|
+
:param obstruction:
|
36
|
+
:param iso:
|
37
|
+
:return:
|
38
|
+
"""
|
39
|
+
...
|
40
|
+
|
41
|
+
|
42
|
+
class PanningLaserLine:
|
43
|
+
def __init__(self, laser_line: LaserLine, pan_vector: Vector3, steps: int):
|
44
|
+
"""
|
45
|
+
:param laser_line:
|
46
|
+
:param pan_vector:
|
47
|
+
:param steps:
|
48
|
+
"""
|
49
|
+
...
|
50
|
+
|
51
|
+
def get_points(self, target: Mesh, obstruction: Mesh | None, iso: Iso3) -> NDArray[float]:
|
52
|
+
"""
|
53
|
+
:param target:
|
54
|
+
:param obstruction:
|
55
|
+
:param iso:
|
56
|
+
:return:
|
57
|
+
"""
|
58
|
+
...
|
@@ -1,23 +1,25 @@
|
|
1
|
-
engeom-0.2.
|
2
|
-
engeom-0.2.
|
1
|
+
engeom-0.2.10.dist-info/METADATA,sha256=pspg1fX2bXgPBv5Ohzo6PabHa0gSDMki855BQddwTOo,495
|
2
|
+
engeom-0.2.10.dist-info/WHEEL,sha256=KOb2uEsUFKFV_GOdT9ev2YJZGn1-e8xqWy3VUx4M6FQ,102
|
3
3
|
engeom/raster3.pyi,sha256=sBXXYXcDBiDU_OFDQiwa7Q3GcwSiUc4CLy6nJ1MwFqM,790
|
4
|
-
engeom/geom2.pyi,sha256=
|
5
|
-
engeom/geom3.pyi,sha256=
|
4
|
+
engeom/geom2.pyi,sha256=uMi4WD6-DtFx8H-RZsKIi-p1fN4p8SzOfBZmEuvNGDU,44357
|
5
|
+
engeom/geom3.pyi,sha256=VYxc9kZQhfHNN13E1DpAzea9z6R6gvpKtxyL8Wm9oWU,69140
|
6
6
|
engeom/geom3/__init__.py,sha256=l8B0iDhJ4YiRbslJLN791XWai2DWrpmZptnzIETMS9g,370
|
7
7
|
engeom/geom2/__init__.py,sha256=JFpiLyROUh6vyakG-7JDSlCMCn4QB2MQ8bz3uVCaAIk,373
|
8
|
-
engeom/plot.py,sha256=
|
8
|
+
engeom/plot.py,sha256=LTqqO-h1EJL6wanM0hB79s9ohWwaCIijMOHVplY3vmc,1079
|
9
9
|
engeom/metrology/__init__.py,sha256=XvEhG8uDm1olWwZHDDrQv9LFP5zXhbsGx27PqRq8WE0,304
|
10
10
|
engeom/airfoil.pyi,sha256=VTeJBoS9Iij7p-92R7jCqzPasHmvAUocyzc6BSx7mvM,23557
|
11
|
-
engeom/_plot/pyvista.py,sha256=
|
12
|
-
engeom/_plot/matplotlib.py,sha256=
|
11
|
+
engeom/_plot/pyvista.py,sha256=PylGVOa9RtRIYPyHLy969eyW8yIgAk-URVZT0R7WoQ8,12980
|
12
|
+
engeom/_plot/matplotlib.py,sha256=rFL1CPNMUqGO-fwD45V3-shektBMeNq5U15Zxp96hYw,14824
|
13
13
|
engeom/_plot/__init__.py,sha256=F_KviZtxzZGwfEjjn8Ep46N4UVl8VpFJWBzbBUE_J7A,30
|
14
14
|
engeom/_plot/common.py,sha256=Py78ufN3yi59hPwv21SoGcqyZUJS-_PmK8tlAKgSG7Q,517
|
15
15
|
engeom/airfoil/__init__.py,sha256=gpS9pVepUu90XJ-ePndNupbUMKI0RGxNXPxD9x0iVHY,274
|
16
16
|
engeom/raster3/__init__.py,sha256=iaayLrvco-ZMZPyeK47ox7rYne_51DNb2T2Q0iNNeKE,289
|
17
17
|
engeom/__init__.py,sha256=QN5uETqrN442w41foyrcCPV_x6NP-mrxkPJhdvdey1g,109
|
18
18
|
engeom/align/__init__.py,sha256=SEeMqeqLKqJC73Mg8GwPwd9NwWnl-dcCqJ4rPdh8yyc,196
|
19
|
+
engeom/sensor/__init__.py,sha256=p-1osXrlBX_hXSSlvySszSimMv_4_n273joBcTFx2V0,179
|
19
20
|
engeom/engeom.pyi,sha256=BtUBtYZ_MX8Xk2x_FyzVxRXjJQIazQ1xscbCLO_Y3HA,1516
|
21
|
+
engeom/sensor.pyi,sha256=a9y62FqhG-CFFHnJiC03PqBpFtxtfkH0zoDkk9LXWnU,1399
|
20
22
|
engeom/metrology.pyi,sha256=9I5un86VB_2gmQBrVYhX8JzILTUADMLB9Em8ttJxrWg,4044
|
21
23
|
engeom/align.pyi,sha256=QCSKrTLkCoaIubcrPU9J-wDZe1lRP0GbPgWZmonXjo0,997
|
22
|
-
engeom/engeom.abi3.so,sha256=
|
23
|
-
engeom-0.2.
|
24
|
+
engeom/engeom.abi3.so,sha256=AUo6dtmxpTG-aw6385vW5HNhzwKIJbxJd6FXLExO3ug,2983744
|
25
|
+
engeom-0.2.10.dist-info/RECORD,,
|
File without changes
|