engeom 0.2.4__tar.gz → 0.2.5__tar.gz
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-0.2.4 → engeom-0.2.5}/Cargo.lock +1 -1
- {engeom-0.2.4 → engeom-0.2.5}/Cargo.toml +1 -1
- {engeom-0.2.4 → engeom-0.2.5}/PKG-INFO +7 -1
- engeom-0.2.5/README.md +3 -0
- engeom-0.2.5/docs/airfoils/intro.md +13 -0
- engeom-0.2.5/docs/bounding_volumes.md +112 -0
- engeom-0.2.5/docs/curves.md +111 -0
- engeom-0.2.5/docs/index.md +27 -0
- engeom-0.2.5/docs/isometries.md +146 -0
- engeom-0.2.5/docs/meshes.md +319 -0
- engeom-0.2.5/docs/metrology.md +12 -0
- engeom-0.2.5/docs/numpy.md +13 -0
- engeom-0.2.5/docs/planes_circles_lines.md +7 -0
- engeom-0.2.5/docs/points_vectors.md +175 -0
- engeom-0.2.5/docs/surf_points.md +159 -0
- engeom-0.2.5/docs/svd_basis.md +103 -0
- engeom-0.2.5/engeom/docs/common/images/surface_point_meas.svg +553 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2.rs +52 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/mesh.rs +0 -1
- engeom-0.2.5/mkdocs.yml +39 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/geom2.pyi +153 -15
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/geom3.pyi +137 -44
- {engeom-0.2.4 → engeom-0.2.5}/python/tests/test_geom2_simple.py +66 -0
- engeom-0.2.5/python/tests/test_geom3_simple.py +171 -0
- {engeom-0.2.4 → engeom-0.2.5}/src/bounding.rs +98 -7
- {engeom-0.2.4 → engeom-0.2.5}/src/geom2.rs +60 -0
- {engeom-0.2.4 → engeom-0.2.5}/src/geom3.rs +56 -0
- {engeom-0.2.4 → engeom-0.2.5}/src/svd_basis.rs +61 -19
- engeom-0.2.4/engeom/src/geom3/mesh/serialization.rs +0 -106
- {engeom-0.2.4 → engeom-0.2.5}/.github/workflows/CI.yml +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/.gitignore +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/.gitmodules +0 -0
- {engeom-0.2.4/engeom/docs/common → engeom-0.2.5/docs}/images/surface_point_meas.svg +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/.gitignore +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/Cargo.lock +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/Cargo.toml +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/README.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/airfoils/camber.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/airfoils/overview.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/common/angles.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/common/core_space.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/common/discrete_domain.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/common/svd_basis.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/geom2/alignment.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/geom2/curve.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/geom2/point_collections.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/geom2/shapes.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/index.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/javascripts/mathjax.js +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/docs/python_rust.md +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/mkdocs.yml +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/airfoil/camber.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/airfoil/edges.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/airfoil/helpers.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/airfoil/inscribed_circle.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/airfoil/orientation.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/airfoil.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/align.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/angles.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/convert_2d_3d.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/discrete_domain.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/indices.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/interval.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/kd_tree.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/points.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/poisson_disk.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/surface_point.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/svd_basis.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common/vec_f64.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/common.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/errors.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/func1/common_functions.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/func1/polynomial.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/func1/series1.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/func1.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/aabb2.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/align2/jacobian.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/align2/points_to_curve.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/align2/rc_params2.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/align2.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/angles2.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/circle2.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/curve2.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/hull.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/line2.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom2/polyline2.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/align3/jacobian.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/align3/multi_param.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/align3/points_to_mesh.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/align3/rotations.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/align3.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/curve3.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/mesh/conformal.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/mesh/edges.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/mesh/faces.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/mesh/filtering.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/mesh/measurement.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/mesh/patches.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/mesh/queries.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/mesh/sampling.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/mesh/uv_mapping.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/plane3.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3/point_cloud.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/geom3.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/io.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/lib.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/metrology/dimension.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/metrology/line_profiles.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/metrology/surface_deviation.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/metrology/tolerance.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/metrology/tolerance_map.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/metrology.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/stats.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/engeom/src/utility.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/pyproject.toml +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/__init__.py +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/airfoil/__init__.py +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/airfoil.pyi +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/align/__init__.py +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/align.pyi +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/engeom.pyi +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/geom2/__init__.py +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/geom3/__init__.py +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/matplotlib.py +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/metrology/__init__.py +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/metrology.pyi +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/engeom/pyvista.py +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/python/tests/test_all.py +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/src/airfoil.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/src/alignments.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/src/common.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/src/conversions.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/src/lib.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/src/mesh.rs +0 -0
- {engeom-0.2.4 → engeom-0.2.5}/src/metrology.rs +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: engeom
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.5
|
4
4
|
Classifier: Programming Language :: Rust
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
6
6
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
@@ -8,3 +8,9 @@ Requires-Dist: numpy
|
|
8
8
|
Requires-Dist: pytest ; extra == 'tests'
|
9
9
|
Provides-Extra: tests
|
10
10
|
Requires-Python: >=3.8
|
11
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
12
|
+
|
13
|
+
# Engeom Python Bindings
|
14
|
+
|
15
|
+
Full documentation at https://mattj23.github.io/py-engeom/
|
16
|
+
|
engeom-0.2.5/README.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Airfoil Introduction
|
2
|
+
|
3
|
+
An airfoil is a body designed to produce asymmetrical forces when moving through a fluid. Airfoils are a common subject
|
4
|
+
of metrology and engineering analysis in the aerospace and aerospace propulsion industries.
|
5
|
+
|
6
|
+
The `engeom` library has tools for airfoil analysis centered around the 2D cross-sections of airfoil bodies. This is a
|
7
|
+
function of the aerospace industry's historical use of 2D drawings to specify airfoil dimensional requirements and
|
8
|
+
performance characteristics.
|
9
|
+
|
10
|
+
Airfoil analysis is a complicated subject with a lot of specialized tools and terminology based on convention and
|
11
|
+
historical precedent. There aren't typically one-size-fits-all algorithms and methods because of the extreme variation
|
12
|
+
in airfoil shapes and requirements. Different institutions and organizations even within the same industry will often
|
13
|
+
have their own preferred methods, tools, and sometimes even terminology.
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# Bounding Volumes
|
2
|
+
|
3
|
+
There are currently two types of bounding volumes available in the `engeom` library. Both are axis-aligned bounding
|
4
|
+
boxes (AABBs), but one is for 2D (`Aabb2`) and the other is for 3D (`Aabb3`).
|
5
|
+
|
6
|
+
These bounding volumes are primarily used within the Rust language library as an internal mechanism to support data
|
7
|
+
structures which accelerate distance queries and intersection checks. However, they also have a number of useful
|
8
|
+
features and so are exposed to the Python API.
|
9
|
+
|
10
|
+
## Creating Bounding Volumes
|
11
|
+
|
12
|
+
Most commonly, bounding volumes are created internally by the library for geometries that are part of acceleration
|
13
|
+
structures or have clear spatial bounds, and accessed by retrieving them from those entities. However, through the
|
14
|
+
Python API, they can also be created directly.
|
15
|
+
|
16
|
+
```python
|
17
|
+
from engeom.geom2 import Aabb2
|
18
|
+
from engeom.geom3 import Aabb3
|
19
|
+
|
20
|
+
# Create a 2D AABB. The arguments are x_min, y_min, x_max, y_max.
|
21
|
+
box2 = Aabb2(-1, -2, 3, 4)
|
22
|
+
|
23
|
+
# Create a 3D AABB. The arguments are x_min, y_min, z_min, x_max, y_max, z_max.
|
24
|
+
box3 = Aabb3(-1, -2, -3, 3, 4, 5)
|
25
|
+
```
|
26
|
+
|
27
|
+
Two other convenient methods for creating bounding volumes are `from_points` and `at_point`.
|
28
|
+
|
29
|
+
The `from_points` function creates a bounding volume that contains all the points in the input list. Pass it a list of
|
30
|
+
points as a numpy array, and it will determine the bounds.
|
31
|
+
|
32
|
+
```python
|
33
|
+
import numpy
|
34
|
+
from engeom.geom2 import Aabb2
|
35
|
+
from engeom.geom3 import Aabb3
|
36
|
+
|
37
|
+
points = numpy.array([[0, 0, 0],
|
38
|
+
[1, 1, 1],
|
39
|
+
[2, 2, 2],
|
40
|
+
[3, 3, 3]]).astype(numpy.float64)
|
41
|
+
|
42
|
+
# Create boxes that contain all the points
|
43
|
+
box2 = Aabb2.from_points(points[:, :2])
|
44
|
+
box3 = Aabb3.from_points(points)
|
45
|
+
```
|
46
|
+
|
47
|
+
The `at_point` function creates a bounding volume centered at the specified point with the specified dimensions. The
|
48
|
+
arguments are the center point and the dimensions of the box. The 2D version takes 4 arguments (x, y, width, height),
|
49
|
+
and the 3D version takes 6 arguments (x, y, z, width, height, depth).
|
50
|
+
|
51
|
+
```python
|
52
|
+
from engeom.geom2 import Aabb2
|
53
|
+
from engeom.geom3 import Aabb3
|
54
|
+
|
55
|
+
# Create a 2D AABB centered at the point (1, 2) that is 3 units wide (x)
|
56
|
+
# and 4 units tall (y).
|
57
|
+
box2 = Aabb2.at_point(1, 2, 3, 4)
|
58
|
+
|
59
|
+
# Create a 3D AABB centered at the point (1, 2, 3) that is 4 units wide (x),
|
60
|
+
# 5 units tall (y), and 6 units deep (z).
|
61
|
+
box3 = Aabb3.at_point(1, 2, 3, 4, 5, 6)
|
62
|
+
```
|
63
|
+
|
64
|
+
## Bounding Volume Properties
|
65
|
+
|
66
|
+
There are four properties of bounding volumes, both 2D and 3D, that can be accessed and yield point and vector values
|
67
|
+
related to the geometry of the volume.
|
68
|
+
|
69
|
+
| Property | Type | Description |
|
70
|
+
|-----------|---------------------|------------------------------------------------------------------|
|
71
|
+
| `.center` | `Point2`/`Point3` | The center point of the bounding volume |
|
72
|
+
| `.min` | `Point2`/`Point3` | The minimum corner point of the bounding volume |
|
73
|
+
| `.max` | `Point2`/`Point3` | The maximum corner point of the bounding volume |
|
74
|
+
| `.extent` | `Vector2`/`Vector3` | The extents of the bounding volume (equivalent to `.max - .min`) |
|
75
|
+
|
76
|
+
```python
|
77
|
+
from engeom.geom3 import Aabb3
|
78
|
+
|
79
|
+
box = Aabb3(-1, -2, -3, 1, 2, 3)
|
80
|
+
|
81
|
+
# Get the center point of the box
|
82
|
+
print(box.center) # Point3(0, 0, 0)
|
83
|
+
print(box.min) # Point3(-1, -2, -3)
|
84
|
+
print(box.max) # Point3(1, 2, 3)
|
85
|
+
print(box.extent) # Vector3(2, 4, 6)
|
86
|
+
```
|
87
|
+
|
88
|
+
## Expand and Shrink
|
89
|
+
|
90
|
+
Bounding volumes can be expanded or shrunk by a specified amount, yielding a new bounding volume. The `expand` and
|
91
|
+
`shrink` methods take a single argument, which is the amount to expand or shrink the perimeter of the bounding volume.
|
92
|
+
|
93
|
+
The overall change in the size of the extents will be twice the amount specified. For example, if you expand a 2D box
|
94
|
+
by 1 unit, the width and height will each increase by 2 units.
|
95
|
+
|
96
|
+
```python
|
97
|
+
from engeom.geom2 import Aabb2
|
98
|
+
box = Aabb2(-1, -2, 1, 2)
|
99
|
+
|
100
|
+
print(box.extent) # Vector2(2, 4)
|
101
|
+
|
102
|
+
expanded = box.expand(0.5)
|
103
|
+
print(expanded.extent) # Vector2(3, 5)
|
104
|
+
|
105
|
+
shrunk = box.shrink(0.5)
|
106
|
+
print(shrunk.extent) # Vector2(1, 3)
|
107
|
+
```
|
108
|
+
|
109
|
+
## Intersection and Containment
|
110
|
+
|
111
|
+
!!! warning
|
112
|
+
Intersection and containment options are not yet bound to the Python API. This will be added in a future release.
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# Curves
|
2
|
+
|
3
|
+
## Introduction
|
4
|
+
|
5
|
+
The `engeom` library has both a 2D and 3D curve type. This type represents a 1-dimensional manifold that consists of
|
6
|
+
a sequence of vertices in $\mathbb{R}^n$ space that are connected by line-segment edges, sometimes referred to as a
|
7
|
+
polyline.
|
8
|
+
|
9
|
+
Each curve entity consists of a single contiguous sequence of vertices with a clear start and end point. In the case of
|
10
|
+
the 2D `Curve2` type, the curve can also be "closed", meaning that the end point is connected to the start point to
|
11
|
+
form a closed loop and so operations on the manifold that cross the end point will wrap around to the start point,
|
12
|
+
and vice versa.
|
13
|
+
|
14
|
+
While the fundamental operations on a curve, such as distance queries and manifold traversal are the same or similar
|
15
|
+
for the 2D and 3D curve types, the 2D curve type has many more features that are made possible by the 2D plane.
|
16
|
+
|
17
|
+
## 2D Curves
|
18
|
+
|
19
|
+
The 2D curve type, `Curve2`, is a more feature-rich type than the 3D curve type, because the nature of the 2D plane
|
20
|
+
means that a 2D polyline is more conceptually related to a 3D `Mesh` object than it is to a 3D polyline. A `Curve2`
|
21
|
+
object can represent a boundary/surface with a clear sense of "inside" and "outside", and if it forms a closed loop
|
22
|
+
it can model a partitioning of the $\mathbb{R}^2$ plane into an interior and exterior region.
|
23
|
+
|
24
|
+
In 2D, a curve has concept of a surface normal direction, which is built from the concept inside/outside defined through
|
25
|
+
the winding order of the vertices. By convention, the segment from vertex $i$ to vertex $i+1$ defines the space to
|
26
|
+
its "right" as being outside the curve, and the space to its "left" as being inside the curve, resulting in a
|
27
|
+
counter-clockwise winding order defining a positive convex shape.
|
28
|
+
|
29
|
+
### Creation
|
30
|
+
|
31
|
+
Creation of a `Curve2` object is done by passing in a list of ordered vertices that define the curve. The vertices
|
32
|
+
will be interpreted in sequential order, so that each vertex will be connected to the next vertex in the list.
|
33
|
+
|
34
|
+
Adjacent vertices that are within a distance tolerance from each other will be de-duplicated, to prevent the curve from
|
35
|
+
having zero-length segments. Additionally, if the first and last point are within the distance tolerance, the curve
|
36
|
+
will be considered "closed" and algorithms which do manifold traversal will wrap between the first and last points.
|
37
|
+
|
38
|
+
However, because of the importance of winding order on 2D curves, the `Curve2` type must *also* be constructed with the
|
39
|
+
vertices in the specific order that matches the intended definition of "inside" vs "outside" for the curve.
|
40
|
+
|
41
|
+
While the space occupied by the curve might be exactly the same whether the vertices are constructed in forward or
|
42
|
+
reverse order, the inside vs outside will be opposite.
|
43
|
+
|
44
|
+
There are three ways that winding order can be specified during construction of a `Curve2` object:
|
45
|
+
|
46
|
+
1. You can prepare the list of vertices so that they are in the correct order and then pass them into the constructor.
|
47
|
+
Counter-clockwise order of vertices for positive convex shapes/regions, and clockwise order for negative convex
|
48
|
+
shapes/regions.
|
49
|
+
|
50
|
+
2. If you know that the curve is meant to represent a positive (convex) shape overall, you can set the `hull_ccw=True`
|
51
|
+
flag. The constructor will build the convex hull of the vertices you provide and reverse their order if the hull
|
52
|
+
sequence does not match the input sequence.
|
53
|
+
|
54
|
+
3. You may provide the constructor with an array of surface normals that correspond with the vertices. There must be one
|
55
|
+
normal per vertex in the input list, and it must be pointing in the direction that you intend to be the "outside" of
|
56
|
+
the surface. The constructor will reverse the input order if the majority of normals are not pointing in the same
|
57
|
+
direction as the winding order would imply.
|
58
|
+
|
59
|
+
The input arguments for the constructor are:
|
60
|
+
|
61
|
+
| Argument | Type | Description |
|
62
|
+
|----------------|------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
63
|
+
| `vertices` | `numpy.ndarray` | A 2D array of shape `(n, 2)` where `n` is the number of vertices in the curve. The columns are the x and y components of the vertices |
|
64
|
+
| `normals` | `numpy.ndarray` **OPTIONAL** | A 2D array of shape `(n, 2)` where `n` is the number of vertices in the curve. The columns are the x and y components of the normals at each vertex. Default is `None`. |
|
65
|
+
| `tol` | `float` **OPTIONAL** | A tolerance distance, below which points are considered to be the same and de-duplicated. Default is `1e-6` |
|
66
|
+
| `force_closed` | `bool` **OPTIONAL** | If `True`, the curve will be guaranteed to be closed. If the first and last point are more than `tol` distance apart, an additional vertex will be added to the end that overlaps with the beginning. Default is `False` |
|
67
|
+
| `hull_ccw` | `bool` **OPTIONAL** | If `True`, the vertices will be re-ordered to match the convex hull of the vertices. Default is `False`. This will be ignored if `normals` is not `None` |
|
68
|
+
|
69
|
+
```python
|
70
|
+
import numpy
|
71
|
+
from engeom.geom2 import Curve2
|
72
|
+
|
73
|
+
# These are the corners of an open unit square
|
74
|
+
vertices = numpy.array([[0.0, 0], [1, 0], [1, 1], [0, 1]])
|
75
|
+
|
76
|
+
# Create a curve with these vertices and nothing else
|
77
|
+
c1 = Curve2(vertices)
|
78
|
+
print(c1) # <Curve2 n=4, l=3 (open)>
|
79
|
+
|
80
|
+
# Force the curve to be closed
|
81
|
+
c2 = Curve2(vertices, force_closed=True)
|
82
|
+
print(c2) # <Curve2 n=5, l=4 (closed)>
|
83
|
+
```
|
84
|
+
|
85
|
+
### Stations
|
86
|
+
|
87
|
+
Because a `Curve2` object is a 1D manifold, every unique position along the curve can be represented by a single scalar
|
88
|
+
value, which is the length from the start of the curve. Each unique position along a 2D curve has several geometric
|
89
|
+
properties which are useful. These properties are bundled in a `CurveStation2` object, which is a lightweight data
|
90
|
+
object that represents a single position on the manifold.
|
91
|
+
|
92
|
+
`CurveStation2` objects are not created directly, but are retrieved from a `Curve2` object through one of several different possible queries on the manifold.
|
93
|
+
|
94
|
+
The `CurveStation2` object has the following properties:
|
95
|
+
|
96
|
+
| Property | Type | Description |
|
97
|
+
|--------------------|-----------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
|
98
|
+
| `.point` | `Point2` | The 2D position in space that corresponds with the station on the manifold. |
|
99
|
+
| `.direction` | `Vector2` | The vector pointing in the direction of positive distance along the curve. Typically this is the vector from the last vertex to the next vertex. |
|
100
|
+
| `.normal` | `Vector2` | The vector pointing in the direction of the surface normal at the station. This is the `direction` vector rotated by $-90°$. |
|
101
|
+
| `.direction_point` | `SurfacePoint2` | A convenience surface point that combines the `point` position and `direction` vector. |
|
102
|
+
| `.surface_point` | `SurfacePoint2` | A convenience surface point that combines the `point` position and `normal` vector. |
|
103
|
+
| `.index` | `int` | The index of the previous vertex on the curve, at or before the station. |
|
104
|
+
| `.length_along` | `float` | The distance along the curve from the start to the station. This is the manifold domain. |
|
105
|
+
|
106
|
+
|
107
|
+
### Querying
|
108
|
+
|
109
|
+
|
110
|
+
|
111
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Engeom (Python Bindings)
|
2
|
+
|
3
|
+
This site has the documentation for the Python bindings of the `engeom` library.
|
4
|
+
|
5
|
+
The [`engeom` library](https://github.com/mattj23/engeom) is a Rust library for 2D and 3D engineering geometry, with a specific focus on metrology applications. The Python bindings provide a way to use some of the library's functionality in Python, with a reasonably similar interface to the Rust library.
|
6
|
+
|
7
|
+
The bindings are built using `pyo3` and `maturin` and `engeom` is compiled as a self-contained extension library, so the python module is the only thing which needs to be installed on an interpreter for everything to work. It is available for Python versions `>=3.8`, for Windows/Linux/macOS, and on most common 64-bit architectures.
|
8
|
+
|
9
|
+
## Quick Overview
|
10
|
+
|
11
|
+
The `engeom` Python library has a few general feature sets:
|
12
|
+
|
13
|
+
- 2D and 3D geometric primitives (points, vectors, planes, circles/arcs, line segments, transformation matrices, etc) which can be used to represent and perform basic geometric operations through a simple and consistent interface.
|
14
|
+
- More complicated entities like 3D triangle meshes, 2D and 3D polylines, and 3D point clouds, with common operations like intersections, projections, distance queries, transformations, etc.
|
15
|
+
- Some basic fitting and alignment algorithms for 2D and 3D data
|
16
|
+
- Helper functions for plotting and visualization to assist in use of the `matplotlib` and `pyvista` libraries.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
The Python bindings can be installed using `pip`:
|
21
|
+
|
22
|
+
```bash
|
23
|
+
pip install engeom
|
24
|
+
```
|
25
|
+
|
26
|
+
|
27
|
+
|
@@ -0,0 +1,146 @@
|
|
1
|
+
# Isometries (Rigid-body Transformations)
|
2
|
+
|
3
|
+
Isometries are a class of transformations that preserve distances between points. They are also known as rigid-body
|
4
|
+
transformations, as they preserve the shape of the object being transformed.
|
5
|
+
|
6
|
+
In the underlying Rust `engeom` library, isometries are aliases for a native `nalgebra` struct. In two dimensions this
|
7
|
+
consists of a 2D translation and a unit complex for rotation, and in three dimensions it consists of a 3D translation
|
8
|
+
and a unit quaternion for rotation.
|
9
|
+
|
10
|
+
Isometries can be thought of as equivalent to transformation matrices, but with the limitation that certain matrices
|
11
|
+
are not valid isometries because they do not preserve distances. They can be composed together by multiplication
|
12
|
+
according to the rules of matrix multiplication, inverted according to the rules of matrix inversion, and they can be
|
13
|
+
multiplied against points or vectors to transform them.
|
14
|
+
|
15
|
+
## Creating Isometries
|
16
|
+
|
17
|
+
Two-dimensional isometries can be created directly from three scalar values: the x and y components of the translation,
|
18
|
+
and the angle of rotation in radians. Three-dimensional isometries are more complicated, and are easiest to create
|
19
|
+
through composition.
|
20
|
+
|
21
|
+
### Two-dimensional Isometries
|
22
|
+
|
23
|
+
```python
|
24
|
+
from math import pi
|
25
|
+
from engeom.geom2 import Iso2
|
26
|
+
|
27
|
+
# Shortcut for the identity transform
|
28
|
+
i0 = Iso2.identity()
|
29
|
+
|
30
|
+
# Create an isometry that translates by (1, 2) and rotates by pi/4 radians
|
31
|
+
i1 = Iso2(1, 2, pi / 4)
|
32
|
+
```
|
33
|
+
|
34
|
+
### Three-dimensional Isometries
|
35
|
+
|
36
|
+
```python
|
37
|
+
import numpy
|
38
|
+
from math import pi
|
39
|
+
from engeom.geom3 import Iso3
|
40
|
+
|
41
|
+
# Shortcut for the identity transform
|
42
|
+
i0 = Iso3.identity()
|
43
|
+
|
44
|
+
# Try to create an isometry directly from a 4x4 transformation matrix. Will throw
|
45
|
+
# an exception if the matrix is not a valid isometry.
|
46
|
+
m = numpy.array([[1, 0, 0, 1],
|
47
|
+
[0, 1, 0, 2],
|
48
|
+
[0, 0, 1, 3],
|
49
|
+
[0, 0, 0, 1]])
|
50
|
+
i1 = Iso3(m)
|
51
|
+
|
52
|
+
# Create an isometry that only translates by specifying the translation vector
|
53
|
+
i2 = Iso3.from_translation(1, 2, 3)
|
54
|
+
|
55
|
+
# Create an isometry that rotates by pi/4 radians around the x-axis. See the documentation
|
56
|
+
# for `from_rotation` for more information on the arguments.
|
57
|
+
i3 = Iso3.from_rotation(pi / 4, 1, 0, 0)
|
58
|
+
```
|
59
|
+
|
60
|
+
## Inverting Isometries
|
61
|
+
|
62
|
+
Isometries can, by definition, be inverted. The inverse of an isometry is an isometry that, when applied to the result
|
63
|
+
of the original isometry, returns the original input. An isometry multiplied by its inverse is the identity isometry.
|
64
|
+
|
65
|
+
```python
|
66
|
+
from math import pi
|
67
|
+
from engeom.geom3 import Iso3
|
68
|
+
|
69
|
+
i = Iso3.from_rotation(pi / 4, 1, 0, 0)
|
70
|
+
|
71
|
+
# Invert the isometry
|
72
|
+
i_inv = i.inverse()
|
73
|
+
```
|
74
|
+
|
75
|
+
|
76
|
+
## Composition
|
77
|
+
|
78
|
+
Isometries can be composed together by multiplying them together. The order of multiplication is important, as
|
79
|
+
isometries do not commute. The result of multiplying two isometries together is a new isometry that is equivalent to
|
80
|
+
apply the right hand isometry first, and then the left hand isometry.
|
81
|
+
|
82
|
+
The operator for isometry multiplication is the same as the matrix multiplication operator, `@`.
|
83
|
+
|
84
|
+
```python
|
85
|
+
from math import pi
|
86
|
+
from engeom.geom3 import Iso3
|
87
|
+
|
88
|
+
i1 = Iso3.from_rotation(pi / 4, 1, 0, 0)
|
89
|
+
i2 = Iso3.from_translation(1, 2, 3)
|
90
|
+
|
91
|
+
# Apply the rotation first, then the translation
|
92
|
+
|
93
|
+
i3 = i2 @ i1
|
94
|
+
```
|
95
|
+
|
96
|
+
## Transforming Primitives
|
97
|
+
|
98
|
+
### Vectors, Points, Surface Points, etc
|
99
|
+
|
100
|
+
Isometries can be applied to points, vectors, and other geometric primitives by multiplying them by the isometry using
|
101
|
+
the `@` operator.
|
102
|
+
|
103
|
+
* A point transformed by an isometry is a new point that is at a new position in space.
|
104
|
+
* A vector transformed by an isometry has been rotated, but its magnitude has not changed.
|
105
|
+
* A surface point transformed by an isometry is the result of transforming the point and the normal vector
|
106
|
+
independently, and then re-constituting them into a new surface point. The position has been moved and the normal
|
107
|
+
vector rotated but remains of unit magnitude.
|
108
|
+
|
109
|
+
```python
|
110
|
+
from math import pi
|
111
|
+
from engeom.geom2 import Iso2, Vector2, Point2, SurfacePoint2
|
112
|
+
|
113
|
+
p = Point2(1, 2)
|
114
|
+
v = Vector2(1, 2)
|
115
|
+
sp = SurfacePoint2(1, 2, 1, 0)
|
116
|
+
|
117
|
+
i = Iso2(1, 2, pi / 4)
|
118
|
+
|
119
|
+
p2 = i @ p
|
120
|
+
v2 = i @ v
|
121
|
+
sp2 = i @ sp
|
122
|
+
```
|
123
|
+
|
124
|
+
### Numpy Arrays
|
125
|
+
|
126
|
+
For efficient transformation of large numbers of points or vectors, both 2D and 3D isometries can be applied to `numpy`
|
127
|
+
arrays representing points or vectors according to the rules defined in the previous section.
|
128
|
+
|
129
|
+
```python
|
130
|
+
import numpy
|
131
|
+
from math import pi
|
132
|
+
from engeom.geom2 import Iso2
|
133
|
+
|
134
|
+
values = numpy.array([[1, 2],
|
135
|
+
[3, 4],
|
136
|
+
[5, 6],
|
137
|
+
[7, 8]])
|
138
|
+
|
139
|
+
i = Iso2(1, 2, pi / 4)
|
140
|
+
|
141
|
+
# Apply the isometry to the values as if they were points
|
142
|
+
new_points = i.transform_points(values)
|
143
|
+
|
144
|
+
# Apply the isometry to the values as if they were vectors
|
145
|
+
new_vectors = i.transform_vectors(values)
|
146
|
+
```
|