engeom 0.2.1__cp38-abi3-win_amd64.whl → 0.2.4__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/engeom.pyd +0 -0
- engeom/engeom.pyi +5 -0
- engeom/geom3.pyi +143 -6
- engeom/matplotlib.py +8 -16
- engeom/metrology.pyi +32 -0
- engeom/pyvista.py +125 -11
- {engeom-0.2.1.dist-info → engeom-0.2.4.dist-info}/METADATA +1 -1
- {engeom-0.2.1.dist-info → engeom-0.2.4.dist-info}/RECORD +9 -9
- {engeom-0.2.1.dist-info → engeom-0.2.4.dist-info}/WHEEL +1 -1
engeom/engeom.pyd
CHANGED
Binary file
|
engeom/engeom.pyi
CHANGED
engeom/geom3.pyi
CHANGED
@@ -4,7 +4,8 @@ from pathlib import Path
|
|
4
4
|
from typing import Tuple, Iterable, List, TypeVar
|
5
5
|
|
6
6
|
import numpy
|
7
|
-
from engeom import DeviationMode, Resample
|
7
|
+
from engeom import DeviationMode, Resample, SelectOp
|
8
|
+
from .metrology import Length3
|
8
9
|
|
9
10
|
Transformable3 = TypeVar("Transformable3", Vector3, Point3, Plane3, Iso3, SurfacePoint3)
|
10
11
|
PointOrVector3 = TypeVar("PointOrVector3", Vector3, Point3)
|
@@ -434,7 +435,7 @@ class Mesh:
|
|
434
435
|
def __init__(
|
435
436
|
self,
|
436
437
|
vertices: numpy.ndarray[float],
|
437
|
-
|
438
|
+
faces: numpy.ndarray[numpy.uint32],
|
438
439
|
merge_duplicates: bool = False,
|
439
440
|
delete_degenerate: bool = False
|
440
441
|
):
|
@@ -445,7 +446,7 @@ class Mesh:
|
|
445
446
|
front/outside.
|
446
447
|
|
447
448
|
:param vertices: a numpy array of shape (n, 3) containing the vertices of the mesh.
|
448
|
-
:param
|
449
|
+
:param faces: a numpy array of shape (m, 3) containing the triangles of the mesh, should be uint.
|
449
450
|
:param merge_duplicates: merge duplicate vertices and triangles
|
450
451
|
:param delete_degenerate: delete degenerate triangles
|
451
452
|
"""
|
@@ -506,7 +507,7 @@ class Mesh:
|
|
506
507
|
...
|
507
508
|
|
508
509
|
@property
|
509
|
-
def
|
510
|
+
def vertices(self) -> numpy.ndarray[float]:
|
510
511
|
"""
|
511
512
|
Will return an immutable view of the vertices of the mesh as a numpy array of shape (n, 3).
|
512
513
|
:return: a numpy array of shape (n, 3) containing the vertices of the mesh.
|
@@ -514,7 +515,7 @@ class Mesh:
|
|
514
515
|
...
|
515
516
|
|
516
517
|
@property
|
517
|
-
def
|
518
|
+
def faces(self) -> numpy.ndarray[numpy.uint32]:
|
518
519
|
"""
|
519
520
|
Will return an immutable view of the triangles of the mesh as a numpy array of shape (m, 3).
|
520
521
|
:return: a numpy array of shape (m, 3) containing the triangles of the mesh.
|
@@ -573,6 +574,142 @@ class Mesh:
|
|
573
574
|
"""
|
574
575
|
...
|
575
576
|
|
577
|
+
def face_select_none(self) -> MeshTriangleFilter:
|
578
|
+
"""
|
579
|
+
Start a filter operation on the faces of the mesh beginning with no faces selected. This will return a filter
|
580
|
+
object that can be used to further add or remove faces from the selection.
|
581
|
+
|
582
|
+
:return: a filter object for the triangles of the mesh.
|
583
|
+
"""
|
584
|
+
...
|
585
|
+
|
586
|
+
def face_select_all(self) -> MeshTriangleFilter:
|
587
|
+
"""
|
588
|
+
Start a filter operation on the faces of the mesh beginning with all faces selected. This will return a filter
|
589
|
+
object that can be used to further add or remove faces from the selection.
|
590
|
+
|
591
|
+
:return: a filter object for the triangles of the mesh.
|
592
|
+
"""
|
593
|
+
...
|
594
|
+
|
595
|
+
def separate_patches(self) -> List[Mesh]:
|
596
|
+
"""
|
597
|
+
Separate the mesh into connected patches. This will return a list of new mesh objects, each containing one
|
598
|
+
connected patch of the original mesh. These objects will be clones of the original mesh, so modifying them will
|
599
|
+
have no effect on the original mesh.
|
600
|
+
:return:
|
601
|
+
"""
|
602
|
+
|
603
|
+
def create_from_indices(self, indices: List[int]) -> Mesh:
|
604
|
+
"""
|
605
|
+
Create a new mesh from a list of triangle indices. This will build a new mesh object containing only the
|
606
|
+
triangles (and their respective vertices) identified by the given list of indices. Do not allow duplicate
|
607
|
+
indices in the list.
|
608
|
+
:param indices: the triangle indices to include in the new mesh
|
609
|
+
:return:
|
610
|
+
"""
|
611
|
+
...
|
612
|
+
|
613
|
+
def measure_point_deviation(self, x: float, y: float, z: float, dist_mode: DeviationMode) -> Length3:
|
614
|
+
"""
|
615
|
+
Compute the deviation of a point from this mesh's surface and return it as a measurement object.
|
616
|
+
|
617
|
+
The deviation is the distance from the point to its closest projection onto the mesh using
|
618
|
+
the specified distance mode. The direction of the measurement is the direction between the
|
619
|
+
point and the projection, flipped into the positive half-space of the mesh surface at the
|
620
|
+
projection point.
|
621
|
+
|
622
|
+
If the distance is less than a very small floating point epsilon, the direction will be
|
623
|
+
taken directly from the mesh surface normal.
|
624
|
+
|
625
|
+
The first point `.a` of the measurement is the reference point, and the second point `.b`
|
626
|
+
is the test point.
|
627
|
+
|
628
|
+
:param x: the x component of the point to measure
|
629
|
+
:param y: the y component of the point to measure
|
630
|
+
:param z: the z component of the point to measure
|
631
|
+
:param dist_mode: the deviation mode to use
|
632
|
+
:return:
|
633
|
+
"""
|
634
|
+
|
635
|
+
def boundary_first_flatten(self) -> numpy.ndarray[float]:
|
636
|
+
"""
|
637
|
+
|
638
|
+
:return:
|
639
|
+
"""
|
640
|
+
|
641
|
+
def surface_closest_to(self, x: float, y: float, z: float) -> SurfacePoint3:
|
642
|
+
"""
|
643
|
+
Find the closest point on the surface of the mesh to a given point in space, returning the point and normal
|
644
|
+
in the form of a `SurfacePoint3` object.
|
645
|
+
:param x: the x coordinate of the point to find the closest point to
|
646
|
+
:param y: the y coordinate of the point to find the closest point to
|
647
|
+
:param z: the z coordinate of the point to find the closest point to
|
648
|
+
:return: a `SurfacePoint3` object containing the closest point and normal
|
649
|
+
"""
|
650
|
+
...
|
651
|
+
|
652
|
+
|
653
|
+
class MeshTriangleFilter:
|
654
|
+
def collect(self) -> List[int]:
|
655
|
+
"""
|
656
|
+
Collect the final indices of the triangles that passed the filter.
|
657
|
+
:return:
|
658
|
+
"""
|
659
|
+
...
|
660
|
+
|
661
|
+
def create_mesh(self) -> Mesh:
|
662
|
+
"""
|
663
|
+
Create a new mesh from the filtered triangles. This will build a new mesh object containing only the triangles
|
664
|
+
(and their respective vertices) that are still retained in the filter.
|
665
|
+
:return:
|
666
|
+
"""
|
667
|
+
...
|
668
|
+
|
669
|
+
def facing(self, x: float, y: float, z: float, angle: float, mode: SelectOp) -> MeshTriangleFilter:
|
670
|
+
"""
|
671
|
+
|
672
|
+
:param x:
|
673
|
+
:param y:
|
674
|
+
:param z:
|
675
|
+
:param angle:
|
676
|
+
:param mode:
|
677
|
+
:return:
|
678
|
+
"""
|
679
|
+
...
|
680
|
+
|
681
|
+
def near_mesh(
|
682
|
+
self,
|
683
|
+
other: Mesh,
|
684
|
+
all_points: bool,
|
685
|
+
distance_tol: float,
|
686
|
+
mode: SelectOp,
|
687
|
+
planar_tol: float | None = None,
|
688
|
+
angle_tol: float | None = None,
|
689
|
+
) -> MeshTriangleFilter:
|
690
|
+
"""
|
691
|
+
Reduce the list of indices to only include triangles that are within a certain distance of
|
692
|
+
their closest projection onto another mesh. The distance can require that all points of the
|
693
|
+
triangle are within the tolerance, or just one.
|
694
|
+
|
695
|
+
There are two additional optional tolerances that can be applied.
|
696
|
+
|
697
|
+
1. A planar tolerance, which checks the distance of the vertex projected onto the plane of
|
698
|
+
the reference mesh triangle and looks at how far it is from the projection point. This
|
699
|
+
is useful to filter out triangles that go past the edge of the reference mesh.
|
700
|
+
2. An angle tolerance, which checks the angle between the normal of the current triangle
|
701
|
+
and the normal of the reference triangle. This is useful to filter out triangles that
|
702
|
+
are not facing the same direction as the reference mesh.
|
703
|
+
|
704
|
+
:param other: the mesh to use as a reference
|
705
|
+
:param all_points: if True, all points of the triangle must be within the tolerance, if False, only one point
|
706
|
+
:param distance_tol: the maximum distance between the triangle and its projection onto the reference mesh
|
707
|
+
:param mode:
|
708
|
+
:param planar_tol: the maximum in-plane distance between the triangle and its projection onto the reference mesh
|
709
|
+
:param angle_tol: the maximum angle between the normals of the triangle and the reference mesh
|
710
|
+
"""
|
711
|
+
...
|
712
|
+
|
576
713
|
|
577
714
|
class CurveStation3:
|
578
715
|
"""
|
@@ -762,4 +899,4 @@ class Aabb3:
|
|
762
899
|
@property
|
763
900
|
def extent(self) -> Vector3:
|
764
901
|
""" The extent of the box. """
|
765
|
-
...
|
902
|
+
...
|
engeom/matplotlib.py
CHANGED
@@ -125,20 +125,12 @@ else:
|
|
125
125
|
self,
|
126
126
|
length: Length2,
|
127
127
|
side_shift: float = 0,
|
128
|
-
|
128
|
+
template: str = "{value:.3f}",
|
129
129
|
fontsize: int = 10,
|
130
130
|
label_place: LabelPlace = LabelPlace.Outside,
|
131
131
|
label_offset: float | None = None,
|
132
132
|
fontname: str | None = None,
|
133
133
|
):
|
134
|
-
"""
|
135
|
-
Plot a Length2 object on a Matplotlib Axes object.
|
136
|
-
:param side_shift:
|
137
|
-
:param length: a Length2 object
|
138
|
-
:return: None
|
139
|
-
"""
|
140
|
-
from matplotlib.pyplot import Line2D
|
141
|
-
|
142
134
|
pad_scale = self._font_height(12) * 1.5
|
143
135
|
center = length.center.shift_orthogonal(side_shift)
|
144
136
|
leader_a = center.projection(length.a)
|
@@ -169,7 +161,7 @@ else:
|
|
169
161
|
kwargs["fontname"] = fontname
|
170
162
|
|
171
163
|
result = self.annotate_text_only(
|
172
|
-
|
164
|
+
template.format(value=length.value),
|
173
165
|
label_coords,
|
174
166
|
bbox=dict(boxstyle="round,pad=0.3", ec="black", fc="white"),
|
175
167
|
**kwargs,
|
@@ -232,9 +224,9 @@ else:
|
|
232
224
|
|
233
225
|
return min(x_scale, y_scale)
|
234
226
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
227
|
+
def _tuplefy(item: PlotCoords) -> Tuple[float, float]:
|
228
|
+
if isinstance(item, (Point2, Vector2)):
|
229
|
+
return item.x, item.y
|
230
|
+
else:
|
231
|
+
x, y, *_ = item
|
232
|
+
return x, y
|
engeom/metrology.pyi
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
from .geom2 import Point2, Vector2, SurfacePoint2
|
2
|
+
from .geom3 import Point3, Vector3, SurfacePoint3
|
2
3
|
|
3
4
|
|
4
5
|
class Length2:
|
@@ -30,3 +31,34 @@ class Length2:
|
|
30
31
|
@property
|
31
32
|
def center(self) -> SurfacePoint2:
|
32
33
|
...
|
34
|
+
|
35
|
+
|
36
|
+
class Length3:
|
37
|
+
def __init__(self, a: Point3, b: Point3, direction: Vector3 | None = None):
|
38
|
+
"""
|
39
|
+
|
40
|
+
:param a:
|
41
|
+
:param b:
|
42
|
+
:param direction:
|
43
|
+
"""
|
44
|
+
...
|
45
|
+
|
46
|
+
@property
|
47
|
+
def a(self) -> Point3:
|
48
|
+
...
|
49
|
+
|
50
|
+
@property
|
51
|
+
def b(self) -> Point3:
|
52
|
+
...
|
53
|
+
|
54
|
+
@property
|
55
|
+
def direction(self) -> Vector3:
|
56
|
+
...
|
57
|
+
|
58
|
+
@property
|
59
|
+
def value(self) -> float:
|
60
|
+
...
|
61
|
+
|
62
|
+
@property
|
63
|
+
def center(self) -> SurfacePoint3:
|
64
|
+
...
|
engeom/pyvista.py
CHANGED
@@ -4,12 +4,16 @@ This module contains helper functions for working with PyVista.
|
|
4
4
|
|
5
5
|
from __future__ import annotations
|
6
6
|
|
7
|
-
from typing import List
|
7
|
+
from typing import List, Any, Dict, Union, Iterable, Tuple
|
8
8
|
|
9
9
|
import numpy
|
10
10
|
from pyvista import ColorLike
|
11
11
|
|
12
|
-
from .geom3 import Mesh, Curve3
|
12
|
+
from .geom3 import Mesh, Curve3, Vector3, Point3, Iso3
|
13
|
+
from .metrology import Length3
|
14
|
+
from .matplotlib import LabelPlace
|
15
|
+
|
16
|
+
PlotCoords = Union[Point3, Vector3, Iterable[float]]
|
13
17
|
|
14
18
|
try:
|
15
19
|
import pyvista
|
@@ -22,12 +26,12 @@ else:
|
|
22
26
|
self.plotter = plotter
|
23
27
|
|
24
28
|
def add_curves(
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
self,
|
30
|
+
*curves: Curve3,
|
31
|
+
color: ColorLike = "w",
|
32
|
+
width: float = 5.0,
|
33
|
+
label: str | None = None,
|
34
|
+
name: str | None = None,
|
31
35
|
) -> List[pyvista.vtkActor]:
|
32
36
|
"""
|
33
37
|
|
@@ -58,7 +62,117 @@ else:
|
|
58
62
|
:param mesh:
|
59
63
|
:return:
|
60
64
|
"""
|
61
|
-
|
62
|
-
|
63
|
-
|
65
|
+
if "cmap" in kwargs:
|
66
|
+
cmap_extremes = _cmap_extremes(kwargs["cmap"])
|
67
|
+
kwargs.update(cmap_extremes)
|
68
|
+
|
69
|
+
prefix = numpy.ones((mesh.faces.shape[0], 1), dtype=mesh.faces.dtype)
|
70
|
+
faces = numpy.hstack((prefix * 3, mesh.faces))
|
71
|
+
data = pyvista.PolyData(mesh.vertices, faces)
|
64
72
|
return self.plotter.add_mesh(data, **kwargs)
|
73
|
+
|
74
|
+
def dimension(
|
75
|
+
self,
|
76
|
+
length: Length3,
|
77
|
+
template: str = "{value:.3f}",
|
78
|
+
label_place: LabelPlace = LabelPlace.Outside,
|
79
|
+
label_offset: float | None = None,
|
80
|
+
text_size: int = 16,
|
81
|
+
scale_value: float = 1.0,
|
82
|
+
):
|
83
|
+
label_offset = label_offset or max(abs(length.value), 1.0) * 3
|
84
|
+
|
85
|
+
t_a = length.center.scalar_projection(length.a)
|
86
|
+
t_b = length.center.scalar_projection(length.b)
|
87
|
+
|
88
|
+
outside = length.center.at_distance(max(t_a, t_b))
|
89
|
+
inside = length.center.at_distance(min(t_a, t_b))
|
90
|
+
|
91
|
+
circles = []
|
92
|
+
builder = LineBuilder()
|
93
|
+
|
94
|
+
builder.add(inside - length.direction * label_offset * 0.25)
|
95
|
+
builder.add(inside)
|
96
|
+
circles.append(inside)
|
97
|
+
builder.skip()
|
98
|
+
|
99
|
+
circles.append(outside)
|
100
|
+
builder.add(outside)
|
101
|
+
builder.add(outside + length.direction * label_offset)
|
102
|
+
|
103
|
+
points = numpy.array([_tuplefy(p) for p in circles], dtype=numpy.float64)
|
104
|
+
self.plotter.add_points(points, color="black", point_size=4, render_points_as_spheres=True)
|
105
|
+
|
106
|
+
lines = builder.build()
|
107
|
+
self.plotter.add_lines(lines, color="black", width=1.5)
|
108
|
+
|
109
|
+
value = length.value * scale_value
|
110
|
+
label = pyvista.Label(text=template.format(value=value), position=lines[-1], size=text_size)
|
111
|
+
self.plotter.add_actor(label)
|
112
|
+
|
113
|
+
def coordinate_frame(self, iso: Iso3, size: float = 1.0):
|
114
|
+
points = numpy.array([[0, 0, 0], [size, 0, 0], [0, size, 0], [0, 0, size]], dtype=numpy.float64)
|
115
|
+
points = iso.transform_points(points)
|
116
|
+
|
117
|
+
self.plotter.add_lines(points[[0, 1]], color="red", width=5.0)
|
118
|
+
self.plotter.add_lines(points[[0, 2]], color="green", width=5.0)
|
119
|
+
self.plotter.add_lines(points[[0, 3]], color="blue", width=5.0)
|
120
|
+
|
121
|
+
def label(self, point: PlotCoords, text: str, **kwargs):
|
122
|
+
label = pyvista.Label(text=text, position=_tuplefy(point), **kwargs)
|
123
|
+
self.plotter.add_actor(label)
|
124
|
+
|
125
|
+
def arrow(self, start: PlotCoords, direction: PlotCoords,
|
126
|
+
tip_length: float = 0.25,
|
127
|
+
tip_radius: float = 0.1,
|
128
|
+
shaft_radius: float = 0.05,
|
129
|
+
**kwargs):
|
130
|
+
pd = pyvista.Arrow(_tuplefy(start), _tuplefy(direction), tip_length=tip_length, tip_radius=tip_radius,
|
131
|
+
shaft_radius=shaft_radius)
|
132
|
+
self.plotter.add_mesh(pd, **kwargs, color="black")
|
133
|
+
|
134
|
+
|
135
|
+
def _cmap_extremes(item: Any) -> Dict[str, ColorLike]:
|
136
|
+
working = {}
|
137
|
+
try:
|
138
|
+
from matplotlib.colors import Colormap
|
139
|
+
except ImportError:
|
140
|
+
return working
|
141
|
+
else:
|
142
|
+
if isinstance(item, Colormap):
|
143
|
+
over = getattr(item, "_rgba_over", None)
|
144
|
+
under = getattr(item, "_rgba_under", None)
|
145
|
+
if over is not None:
|
146
|
+
working["above_color"] = over
|
147
|
+
if under is not None:
|
148
|
+
working["below_color"] = under
|
149
|
+
return working
|
150
|
+
|
151
|
+
|
152
|
+
class LineBuilder:
|
153
|
+
def __init__(self):
|
154
|
+
self.vertices = []
|
155
|
+
self._skip = 1
|
156
|
+
|
157
|
+
def add(self, points: PlotCoords):
|
158
|
+
if self.vertices:
|
159
|
+
if self._skip > 0:
|
160
|
+
self._skip -= 1
|
161
|
+
else:
|
162
|
+
self.vertices.append(self.vertices[-1])
|
163
|
+
|
164
|
+
self.vertices.append(_tuplefy(points))
|
165
|
+
|
166
|
+
def skip(self):
|
167
|
+
self._skip = 2
|
168
|
+
|
169
|
+
def build(self) -> numpy.ndarray:
|
170
|
+
return numpy.array(self.vertices, dtype=numpy.float64)
|
171
|
+
|
172
|
+
|
173
|
+
def _tuplefy(item: PlotCoords) -> Tuple[float, float, float]:
|
174
|
+
if isinstance(item, (Point3, Vector3)):
|
175
|
+
return item.x, item.y, item.z
|
176
|
+
else:
|
177
|
+
x, y, z, *_ = item
|
178
|
+
return x, y, z
|
@@ -1,18 +1,18 @@
|
|
1
|
-
engeom-0.2.
|
2
|
-
engeom-0.2.
|
1
|
+
engeom-0.2.4.dist-info/METADATA,sha256=ihtRy0gEfQ1gc10xbpncuk_Uc_-ocxIR2HyZEejVXto,339
|
2
|
+
engeom-0.2.4.dist-info/WHEEL,sha256=_g1M2QM3kt1Ssm_sHOg_3TUY7GxNE2Ueyslb9ZDtPwk,94
|
3
3
|
engeom/airfoil/__init__.py,sha256=G6m7JEvHVk3sM2JooJPOg8JNA3VuEp0EIqczSEbC_PY,180
|
4
4
|
engeom/airfoil.pyi,sha256=0TVpXkolFUXbBqJp93FenA_XqvU7FD1DnbncAF0ubow,14654
|
5
5
|
engeom/align/__init__.py,sha256=SEeMqeqLKqJC73Mg8GwPwd9NwWnl-dcCqJ4rPdh8yyc,196
|
6
6
|
engeom/align.pyi,sha256=QCSKrTLkCoaIubcrPU9J-wDZe1lRP0GbPgWZmonXjo0,997
|
7
|
-
engeom/engeom.pyi,sha256=
|
7
|
+
engeom/engeom.pyi,sha256=Jia11rU8ZnMKdlXgfGeBPlSmsYPEfALM-_ufNwR0ibQ,254
|
8
8
|
engeom/geom2/__init__.py,sha256=mRu8Zh6DE-EQyhxScoxszPqDjGVzGWVJEQO6RIAtS4A,174
|
9
9
|
engeom/geom2.pyi,sha256=Jh0ES-Gvkl7sFQV7VG6KdwgDbCPhmiETG_YOObYazhU,22741
|
10
10
|
engeom/geom3/__init__.py,sha256=DG5jt2xgS9WRNb58ZkkrcKQQO6bIG-irg-uV_BkHEj4,174
|
11
|
-
engeom/geom3.pyi,sha256=
|
12
|
-
engeom/matplotlib.py,sha256=
|
11
|
+
engeom/geom3.pyi,sha256=UUUg3OHobF_MfSDJZXD0PEZGSak2XQpiGAkne3lY7fI,34163
|
12
|
+
engeom/matplotlib.py,sha256=A0gdQshzE3G7joNHna4viYnioQtA8LVXfSuZ_X6AHeo,9001
|
13
13
|
engeom/metrology/__init__.py,sha256=cpsB0-hJGitzW79Coxwf7r_mpNaeI6yG3myDEVdBJgk,186
|
14
|
-
engeom/metrology.pyi,sha256=
|
15
|
-
engeom/pyvista.py,sha256=
|
14
|
+
engeom/metrology.pyi,sha256=P_2pkoLUAOB0-RKppj0FN01XGY0jx1lGw9H1eKXrW8s,1144
|
15
|
+
engeom/pyvista.py,sha256=WVjaMG1hhd6hkknfxgkgCH8rZRXaM2AweG39T0UQkGc,6044
|
16
16
|
engeom/__init__.py,sha256=kYgFq3jq1quDfV013wEYQMlUBz4QNSpP6u8lFiuTHvc,115
|
17
|
-
engeom/engeom.pyd,sha256=
|
18
|
-
engeom-0.2.
|
17
|
+
engeom/engeom.pyd,sha256=7G9m_LBBIJkK7bVNkiV0Z6rQ0VA1R-KuVkZcB_w4rPE,2317312
|
18
|
+
engeom-0.2.4.dist-info/RECORD,,
|