engeom 0.2.8__tar.gz → 0.2.9__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.8 → engeom-0.2.9}/Cargo.lock +1 -1
- {engeom-0.2.8 → engeom-0.2.9}/Cargo.toml +1 -1
- {engeom-0.2.8 → engeom-0.2.9}/PKG-INFO +1 -1
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil/camber.rs +6 -3
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/edges.rs +3 -3
- engeom-0.2.9/engeom/src/geom3/mesh/outline.rs +281 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/patches.rs +5 -5
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh.rs +47 -7
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology.rs +2 -3
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/raster3.rs +58 -60
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/_plot/matplotlib.py +50 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/geom3.pyi +19 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/plot.py +1 -1
- {engeom-0.2.8 → engeom-0.2.9}/src/conversions.rs +10 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/mesh.rs +77 -1
- {engeom-0.2.8 → engeom-0.2.9}/.github/workflows/CI.yml +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/.gitignore +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/.gitmodules +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/README.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/airfoils/intro.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/api/airfoil.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/api/engeom.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/api/geom2.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/api/geom3.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/api/metrology.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/api/plot.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/bounding_volumes.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/curves.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/images/surface_point_meas.svg +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/index.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/isometries.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/meshes.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/metrology.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/numpy.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/planes_circles_lines.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/points_vectors.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/surf_points.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/docs/svd_basis.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/.gitignore +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/Cargo.lock +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/Cargo.toml +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/README.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/airfoils/camber.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/airfoils/overview.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/common/angles.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/common/core_space.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/common/discrete_domain.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/common/images/surface_point_meas.svg +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/common/svd_basis.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/geom2/alignment.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/geom2/curve.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/geom2/point_collections.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/geom2/shapes.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/index.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/javascripts/mathjax.js +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/python_rust.md +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/mkdocs.yml +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil/edges.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil/helpers.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil/inscribed_circle.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil/orientation.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/align.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/angles.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/convert_2d_3d.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/discrete_domain.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/indices.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/interval.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/kd_tree.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/points.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/poisson_disk.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/surface_point.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/svd_basis.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/vec_f64.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/errors.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/func1/common_functions.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/func1/polynomial.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/func1/series1.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/func1.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/aabb2.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/align2/jacobian.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/align2/points_to_curve.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/align2/rc_params2.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/align2.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/angles2.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/circle2.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/curve2.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/hull.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/line2.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/polyline2.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2.rs +1 -1
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/align3/jacobian.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/align3/multi_param.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/align3/points_to_mesh.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/align3/rotations.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/align3.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/curve3.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/conformal.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/faces.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/filtering.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/measurement.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/queries.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/sampling.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/uv_mapping.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/plane3.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/point_cloud.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/io.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/lib.rs +1 -1
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology/dimension.rs +1 -1
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology/line_profiles.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology/surface_deviation.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology/tolerance.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology/tolerance_map.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/stats.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/engeom/src/utility.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/mkdocs.yml +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/pyproject.toml +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/__init__.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/_plot/__init__.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/_plot/common.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/_plot/pyvista.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/airfoil/__init__.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/airfoil.pyi +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/align/__init__.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/align.pyi +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/engeom.pyi +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/geom2/__init__.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/geom2.pyi +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/geom3/__init__.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/metrology/__init__.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/metrology.pyi +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/raster3/__init__.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/engeom/raster3.pyi +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/tests/test_all.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/tests/test_geom2_simple.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/python/tests/test_geom3_simple.py +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/airfoil.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/alignments.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/bounding.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/common.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/geom2.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/geom3.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/lib.rs +1 -1
- {engeom-0.2.8 → engeom-0.2.9}/src/metrology.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/raster.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/ray_casting.rs +0 -0
- {engeom-0.2.8 → engeom-0.2.9}/src/svd_basis.rs +0 -0
@@ -10,8 +10,8 @@ use crate::geom2::hull::farthest_pair_indices;
|
|
10
10
|
use crate::geom2::polyline2::SpanningRay;
|
11
11
|
use crate::geom2::{rot90, Line2, Segment2};
|
12
12
|
use crate::AngleDir::{Ccw, Cw};
|
13
|
-
use crate::{Curve2, UnitVec2};
|
14
13
|
use crate::Result;
|
14
|
+
use crate::{Curve2, UnitVec2};
|
15
15
|
use parry2d_f64::query::Ray;
|
16
16
|
use parry2d_f64::shape::ConvexPolygon;
|
17
17
|
|
@@ -23,8 +23,11 @@ use parry2d_f64::shape::ConvexPolygon;
|
|
23
23
|
///
|
24
24
|
/// returns: Result<Unit<Matrix<f64, Const<2>, Const<1>, ArrayStorage<f64, 2, 1>>>, Box<dyn Error, Global>>
|
25
25
|
pub fn camber_detect_upper_dir(camber_line: &Curve2) -> Result<UnitVec2> {
|
26
|
-
let check = Segment2::try_new(
|
27
|
-
.
|
26
|
+
let check = Segment2::try_new(
|
27
|
+
camber_line.at_front().point(),
|
28
|
+
camber_line.at_back().point(),
|
29
|
+
)
|
30
|
+
.map_err(|_| "Failed to create segment from camber line while detecting face orientation")?;
|
28
31
|
|
29
32
|
let resampled = camber_line.resample(ByCount(50))?;
|
30
33
|
|
@@ -100,7 +100,7 @@ impl<'a> MeshEdges<'a> {
|
|
100
100
|
|
101
101
|
/// Given an edge, return a key that can be used to identify the vertices that are connected by the
|
102
102
|
/// edge without regard to the order of the vertices.
|
103
|
-
fn edge_key(edge: &[u32; 2]) -> [u32; 2] {
|
103
|
+
pub fn edge_key(edge: &[u32; 2]) -> [u32; 2] {
|
104
104
|
let x = edge[0].min(edge[1]);
|
105
105
|
let y = edge[0].max(edge[1]);
|
106
106
|
[x, y]
|
@@ -110,7 +110,7 @@ fn edge_key(edge: &[u32; 2]) -> [u32; 2] {
|
|
110
110
|
/// the faces are presented. The edges are not deduplicated nor their direction normalized. The
|
111
111
|
/// resulting list will be 3x the length of the original faces. Elements 0-2 will be from face 0,
|
112
112
|
/// elements 3-5 will be from face 1, and so on.
|
113
|
-
fn naive_edges(faces: &[[u32; 3]]) -> Vec<[u32; 2]> {
|
113
|
+
pub fn naive_edges(faces: &[[u32; 3]]) -> Vec<[u32; 2]> {
|
114
114
|
let mut edges = Vec::new();
|
115
115
|
for face in faces {
|
116
116
|
edges.push([face[1], face[2]]);
|
@@ -122,7 +122,7 @@ fn naive_edges(faces: &[[u32; 3]]) -> Vec<[u32; 2]> {
|
|
122
122
|
|
123
123
|
/// Given a reference list of all edges, return a sorted list of unique edges and the number of
|
124
124
|
/// times each edge appeared in the original list.
|
125
|
-
fn unique_edges(all_edges: &[[u32; 2]]) -> Vec<([u32; 2], usize)> {
|
125
|
+
pub fn unique_edges(all_edges: &[[u32; 2]]) -> Vec<([u32; 2], usize)> {
|
126
126
|
let mut unique = HashMap::new();
|
127
127
|
for edge in all_edges {
|
128
128
|
let key = edge_key(edge);
|
@@ -0,0 +1,281 @@
|
|
1
|
+
//! This module exists to help generate a visual outline of a mesh
|
2
|
+
|
3
|
+
use super::Mesh;
|
4
|
+
use crate::common::points::{fill_gaps, mid_point};
|
5
|
+
use crate::geom3::mesh::edges::{edge_key, naive_edges, unique_edges};
|
6
|
+
use crate::{Point3, UnitVec3};
|
7
|
+
use parry3d_f64::query::{Ray, RayCast};
|
8
|
+
use std::collections::{HashMap, HashSet};
|
9
|
+
use std::f64::consts::PI;
|
10
|
+
|
11
|
+
// struct KeyChainer {
|
12
|
+
// forward: HashMap<u32, HashSet<u32>>,
|
13
|
+
// reverse: HashMap<u32, HashSet<u32>>,
|
14
|
+
// }
|
15
|
+
//
|
16
|
+
// fn first_single(map: &mut HashMap<u32, HashSet<u32>>) -> Option<(u32, u32)> {
|
17
|
+
// for (k, v) in map.iter_mut() {
|
18
|
+
// if v.len() == 1 {
|
19
|
+
// let k0 = *k;
|
20
|
+
// let k1 = *v.iter().next().unwrap();
|
21
|
+
// return Some((k0, k1));
|
22
|
+
// }
|
23
|
+
// }
|
24
|
+
// None
|
25
|
+
// }
|
26
|
+
//
|
27
|
+
// impl KeyChainer {
|
28
|
+
// pub fn new() -> Self {
|
29
|
+
// KeyChainer {
|
30
|
+
// forward: HashMap::new(),
|
31
|
+
// reverse: HashMap::new(),
|
32
|
+
// }
|
33
|
+
// }
|
34
|
+
//
|
35
|
+
// pub fn push(&mut self, key: &[u32; 2]) {
|
36
|
+
// let k_min = key[0].min(key[1]);
|
37
|
+
// let k_max = key[0].max(key[1]);
|
38
|
+
// self.forward.entry(k_min).or_default().insert(k_max);
|
39
|
+
// self.reverse.entry(k_max).or_default().insert(k_min);
|
40
|
+
// }
|
41
|
+
//
|
42
|
+
// pub fn remove_pair(&mut self, k0: u32, k1: u32) {
|
43
|
+
// let k_min = k0.min(k1);
|
44
|
+
// let k_max = k0.max(k1);
|
45
|
+
//
|
46
|
+
// if let Some(v) = self.forward.get_mut(&k_min) {
|
47
|
+
// v.remove(&k_max);
|
48
|
+
// if v.is_empty() {
|
49
|
+
// self.forward.remove(&k_min);
|
50
|
+
// }
|
51
|
+
// }
|
52
|
+
//
|
53
|
+
// if let Some(v) = self.reverse.get_mut(&k_max) {
|
54
|
+
// v.remove(&k_min);
|
55
|
+
// if v.is_empty() {
|
56
|
+
// self.reverse.remove(&k_max);
|
57
|
+
// }
|
58
|
+
// }
|
59
|
+
// }
|
60
|
+
//
|
61
|
+
// pub fn pop_new(&mut self) -> Option<(u32, u32)> {
|
62
|
+
// if let Some((k0, k1)) = first_single(&mut self.forward) {
|
63
|
+
// self.remove_pair(k0, k1);
|
64
|
+
// Some((k0, k1))
|
65
|
+
// } else if let Some((k0, k1)) = first_single(&mut self.reverse) {
|
66
|
+
// self.remove_pair(k0, k1);
|
67
|
+
// Some((k0, k1))
|
68
|
+
// } else {
|
69
|
+
// // Find the lowest count from forward
|
70
|
+
// if let Some(n) = self
|
71
|
+
// .forward
|
72
|
+
// .iter()
|
73
|
+
// .min_by(|a, b| a.1.len().cmp(&b.1.len()))
|
74
|
+
// .clone()
|
75
|
+
// {
|
76
|
+
// let k0 = *n.0;
|
77
|
+
// let k1 = *n.1.iter().next().unwrap();
|
78
|
+
// self.remove_pair(k0, k1);
|
79
|
+
// Some((k0, k1))
|
80
|
+
// } else {
|
81
|
+
// None
|
82
|
+
// }
|
83
|
+
// }
|
84
|
+
// }
|
85
|
+
//
|
86
|
+
// pub fn find_next(&mut self, k0: u32) -> Option<u32> {
|
87
|
+
// if let Some(v) = self.forward.get_mut(&k0) {
|
88
|
+
// if v.len() == 1 {
|
89
|
+
// let k1 = *v.iter().next().unwrap();
|
90
|
+
// self.remove_pair(k0, k1);
|
91
|
+
// return Some(k1);
|
92
|
+
// }
|
93
|
+
// }
|
94
|
+
// if let Some(v) = self.reverse.get_mut(&k0) {
|
95
|
+
// if v.len() == 1 {
|
96
|
+
// let k1 = *v.iter().next().unwrap();
|
97
|
+
// self.remove_pair(k0, k1);
|
98
|
+
// return Some(k1);
|
99
|
+
// }
|
100
|
+
// }
|
101
|
+
// None
|
102
|
+
// }
|
103
|
+
//
|
104
|
+
// pub fn sequences(&mut self) -> Vec<Vec<u32>> {
|
105
|
+
// let mut chains = Vec::new();
|
106
|
+
// let mut working = Vec::new();
|
107
|
+
//
|
108
|
+
// // Find first
|
109
|
+
// while !self.forward.is_empty() {
|
110
|
+
// if working.is_empty() {
|
111
|
+
// if let Some((k0, k1)) = self.pop_new() {
|
112
|
+
// working.push(k0);
|
113
|
+
// working.push(k1);
|
114
|
+
// } else {
|
115
|
+
// for (a, b) in self.forward.iter() {
|
116
|
+
// println!("{} -> {:?}", a, b);
|
117
|
+
// }
|
118
|
+
// for (a, b) in self.reverse.iter() {
|
119
|
+
// println!("{} <- {:?}", a, b);
|
120
|
+
// }
|
121
|
+
// panic!("This should not happen");
|
122
|
+
// }
|
123
|
+
// }
|
124
|
+
//
|
125
|
+
// // Now we have a working chain
|
126
|
+
// let last = *working.last().unwrap();
|
127
|
+
// if let Some(next) = self.find_next(last) {
|
128
|
+
// self.remove_pair(last, next);
|
129
|
+
// working.push(next);
|
130
|
+
// } else {
|
131
|
+
// // We are done with this chain
|
132
|
+
// chains.push(working.clone());
|
133
|
+
// working.clear();
|
134
|
+
// }
|
135
|
+
// }
|
136
|
+
//
|
137
|
+
// chains
|
138
|
+
// }
|
139
|
+
// }
|
140
|
+
|
141
|
+
impl Mesh {
|
142
|
+
pub fn visual_outline(
|
143
|
+
&self,
|
144
|
+
facing: UnitVec3,
|
145
|
+
max_edge_length: f64,
|
146
|
+
corner_angle: Option<f64>,
|
147
|
+
) -> Vec<(Point3, Point3, u8)> {
|
148
|
+
let corner_angle = corner_angle.unwrap_or(PI / 4.0 - 1e-2);
|
149
|
+
let (boundaries, mut corners) = self.classified_edge_types();
|
150
|
+
// let mut working = KeyChainer::new();
|
151
|
+
let mut working = Vec::new();
|
152
|
+
|
153
|
+
for (i, indices) in self.shape.indices().iter().enumerate() {
|
154
|
+
for (i0, i1) in [(0, 1), (1, 2), (2, 0)] {
|
155
|
+
let k = edge_key(&[indices[i0], indices[i1]]);
|
156
|
+
|
157
|
+
if boundaries.contains(&k) {
|
158
|
+
working.push(k);
|
159
|
+
} else if let Some(corner) = corners.get_mut(&k) {
|
160
|
+
if corner[0] == u32::MAX {
|
161
|
+
corner[0] = i as u32;
|
162
|
+
} else {
|
163
|
+
corner[1] = i as u32;
|
164
|
+
}
|
165
|
+
}
|
166
|
+
}
|
167
|
+
}
|
168
|
+
|
169
|
+
// At this point, working contains boundary edges and corners contains corner face pairs
|
170
|
+
// Now we need to process the corners
|
171
|
+
for (key, corner) in corners.iter() {
|
172
|
+
if corner[0] == u32::MAX || corner[1] == u32::MAX {
|
173
|
+
continue;
|
174
|
+
}
|
175
|
+
|
176
|
+
let n0u = self.shape.triangle(corner[0]).normal();
|
177
|
+
let n1u = self.shape.triangle(corner[1]).normal();
|
178
|
+
|
179
|
+
if let (Some(n0), Some(n1)) = (n0u, n1u) {
|
180
|
+
if n0.angle(&n1) > corner_angle {
|
181
|
+
// Is this a corner?
|
182
|
+
working.push(*key);
|
183
|
+
} else {
|
184
|
+
let f0 = facing.dot(&n0);
|
185
|
+
let f1 = facing.dot(&n1);
|
186
|
+
let f_max = f0.max(f1);
|
187
|
+
let f_min = f0.min(f1);
|
188
|
+
|
189
|
+
if f_max >= 0.0 && f_min < 0.0 {
|
190
|
+
// Is this a silhouette?
|
191
|
+
working.push(*key);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
let vert_normals = self.get_vertex_normals();
|
198
|
+
let mut edges = Vec::new();
|
199
|
+
for k in working {
|
200
|
+
let k0 = k[0];
|
201
|
+
let k1 = k[1];
|
202
|
+
|
203
|
+
let p0: Point3 =
|
204
|
+
self.shape.vertices()[k0 as usize].clone() + vert_normals[k0 as usize] * 1e-2;
|
205
|
+
let p1: Point3 =
|
206
|
+
self.shape.vertices()[k1 as usize].clone() + vert_normals[k1 as usize] * 1e-2;
|
207
|
+
|
208
|
+
let points = fill_gaps(&[p0, p1], max_edge_length);
|
209
|
+
|
210
|
+
for (p0, p1) in points.iter().zip(points.iter().skip(1)) {
|
211
|
+
let p = mid_point(p0, p1) + facing.into_inner() * 1e-2;
|
212
|
+
|
213
|
+
let ray = Ray::new(p, facing.into_inner());
|
214
|
+
|
215
|
+
if self.shape.intersects_local_ray(&ray, f64::MAX) {
|
216
|
+
edges.push((*p0, *p1, 1))
|
217
|
+
} else {
|
218
|
+
edges.push((*p0, *p1, 0))
|
219
|
+
}
|
220
|
+
}
|
221
|
+
}
|
222
|
+
|
223
|
+
// // We want to chain vertices together
|
224
|
+
// let sequences = working
|
225
|
+
// .sequences()
|
226
|
+
// .into_iter()
|
227
|
+
// .map(|indices| {
|
228
|
+
// indices
|
229
|
+
// .iter()
|
230
|
+
// .map(|i| self.shape.vertices()[*i as usize])
|
231
|
+
// .collect::<Vec<_>>()
|
232
|
+
// })
|
233
|
+
// .collect::<Vec<_>>();
|
234
|
+
//
|
235
|
+
// let mut edges = Vec::new();
|
236
|
+
// let shift = -facing.into_inner();
|
237
|
+
// for c in sequences.iter() {
|
238
|
+
// if let Ok(chain) = Curve3::from_points(c, 1e-3) {
|
239
|
+
// if chain.count() < 3 || chain.length() < max_edge_length * 2.0 {
|
240
|
+
// continue;
|
241
|
+
// }
|
242
|
+
//
|
243
|
+
// // println!("before l={} n={}", chain.length(), chain.count());
|
244
|
+
// let chain = chain.resample(Resample::ByMaxSpacing(max_edge_length));
|
245
|
+
// // println!("after");
|
246
|
+
//
|
247
|
+
// for (p0, p1) in chain.points().iter().zip(chain.points().iter().skip(1)) {
|
248
|
+
// let p = mid_point(p0, p1) + shift * 1e-2;
|
249
|
+
//
|
250
|
+
// let ray = Ray::new(p, shift);
|
251
|
+
//
|
252
|
+
// if self.shape.intersects_local_ray(&ray, f64::MAX) {
|
253
|
+
// edges.push((*p0, *p1, 1))
|
254
|
+
// } else {
|
255
|
+
// edges.push((*p0, *p1, 0))
|
256
|
+
// }
|
257
|
+
// }
|
258
|
+
// }
|
259
|
+
// }
|
260
|
+
|
261
|
+
edges
|
262
|
+
}
|
263
|
+
|
264
|
+
fn classified_edge_types(&self) -> (HashSet<[u32; 2]>, HashMap<[u32; 2], [u32; 2]>) {
|
265
|
+
let naive = naive_edges(&self.shape.indices());
|
266
|
+
let unique = unique_edges(&naive);
|
267
|
+
|
268
|
+
let mut boundaries = HashSet::new();
|
269
|
+
let mut corners = HashMap::new();
|
270
|
+
|
271
|
+
for (key, count) in unique {
|
272
|
+
if count == 1 {
|
273
|
+
boundaries.insert(key);
|
274
|
+
} else if count == 2 {
|
275
|
+
corners.insert(key, [u32::MAX, u32::MAX]);
|
276
|
+
}
|
277
|
+
}
|
278
|
+
|
279
|
+
(boundaries, corners)
|
280
|
+
}
|
281
|
+
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
use crate::geom3::Mesh;
|
2
|
-
use crate::Point3;
|
2
|
+
use crate::{Point3, Result};
|
3
3
|
use std::collections::{HashMap, HashSet};
|
4
4
|
|
5
5
|
fn edge_key(i: usize, f: &[u32; 3]) -> (u32, u32) {
|
@@ -50,7 +50,7 @@ fn take_one_boundary(order: &mut HashMap<u32, u32>) -> Option<Vec<u32>> {
|
|
50
50
|
Some(sequence)
|
51
51
|
}
|
52
52
|
|
53
|
-
pub fn compute_boundary_points(mesh: &Mesh, patch: &[usize]) -> Vec<Vec<Point3
|
53
|
+
pub fn compute_boundary_points(mesh: &Mesh, patch: &[usize]) -> Result<Vec<Vec<Point3>>> {
|
54
54
|
let mut order = HashMap::new();
|
55
55
|
let mut remaining = HashSet::new();
|
56
56
|
let edges = compute_boundary_edges(mesh, patch);
|
@@ -58,7 +58,7 @@ pub fn compute_boundary_points(mesh: &Mesh, patch: &[usize]) -> Vec<Vec<Point3>>
|
|
58
58
|
if let std::collections::hash_map::Entry::Vacant(e) = order.entry(v0) {
|
59
59
|
e.insert(v1);
|
60
60
|
} else {
|
61
|
-
|
61
|
+
return Err("Duplicate boundary point found!".into());
|
62
62
|
}
|
63
63
|
remaining.insert(v0);
|
64
64
|
}
|
@@ -68,14 +68,14 @@ pub fn compute_boundary_points(mesh: &Mesh, patch: &[usize]) -> Vec<Vec<Point3>>
|
|
68
68
|
sequences.push(sequence);
|
69
69
|
}
|
70
70
|
|
71
|
-
sequences
|
71
|
+
Ok(sequences
|
72
72
|
.into_iter()
|
73
73
|
.map(|seq| {
|
74
74
|
seq.into_iter()
|
75
75
|
.map(|v| mesh.vertices()[v as usize])
|
76
76
|
.collect()
|
77
77
|
})
|
78
|
-
.collect()
|
78
|
+
.collect())
|
79
79
|
}
|
80
80
|
|
81
81
|
/// Uses the adjacency information of the mesh to compute a list of patches of connected faces. This
|
@@ -7,6 +7,7 @@ mod edges;
|
|
7
7
|
mod faces;
|
8
8
|
pub mod filtering;
|
9
9
|
mod measurement;
|
10
|
+
mod outline;
|
10
11
|
mod patches;
|
11
12
|
mod queries;
|
12
13
|
mod sampling;
|
@@ -14,7 +15,7 @@ mod uv_mapping;
|
|
14
15
|
|
15
16
|
pub use self::uv_mapping::UvMapping;
|
16
17
|
use crate::geom3::Aabb3;
|
17
|
-
use crate::{Iso3, Point2, Point3, Result, SurfacePoint3};
|
18
|
+
use crate::{Iso3, Point2, Point3, Result, SurfacePoint3, UnitVec3, Vector3};
|
18
19
|
pub use edges::MeshEdges;
|
19
20
|
use parry3d_f64::shape::{TriMesh, TriMeshFlags};
|
20
21
|
|
@@ -207,13 +208,52 @@ impl Mesh {
|
|
207
208
|
/// lists of points, where each list of points is the boundary of a patch. Note that this
|
208
209
|
/// function will not work on non-manifold meshes.
|
209
210
|
///
|
210
|
-
/// returns: Vec<Vec<usize, Global>, Global
|
211
|
-
pub fn get_patch_boundary_points(&self) -> Vec<Vec<Point3
|
211
|
+
/// returns: Result<Vec<Vec<usize, Global>, Global>>
|
212
|
+
pub fn get_patch_boundary_points(&self) -> Result<Vec<Vec<Point3>>> {
|
212
213
|
let patches = self.get_patches();
|
213
|
-
|
214
|
-
|
215
|
-
.
|
216
|
-
|
214
|
+
let mut result = Vec::new();
|
215
|
+
for patch in patches.iter() {
|
216
|
+
result.extend(patches::compute_boundary_points(self, patch)?);
|
217
|
+
}
|
218
|
+
|
219
|
+
Ok(result)
|
220
|
+
}
|
221
|
+
|
222
|
+
pub fn get_face_normals(&self) -> Result<Vec<UnitVec3>> {
|
223
|
+
let mut result = Vec::new();
|
224
|
+
for t in self.shape.triangles() {
|
225
|
+
if let Some(n) = t.normal() {
|
226
|
+
result.push(n.clone());
|
227
|
+
} else {
|
228
|
+
return Err("Failed to get normal".into());
|
229
|
+
}
|
230
|
+
}
|
231
|
+
|
232
|
+
Ok(result)
|
233
|
+
}
|
234
|
+
|
235
|
+
pub fn get_vertex_normals(&self) -> Vec<Vector3> {
|
236
|
+
let mut sums: Vec<Vector3> = vec![Vector3::new(0.0, 0.0, 0.0); self.shape.vertices().len()];
|
237
|
+
let mut counts = vec![0; self.shape.vertices().len()];
|
238
|
+
|
239
|
+
for (indices, tri) in self.shape.indices().iter().zip(self.shape.triangles()) {
|
240
|
+
if let Some(n) = tri.normal() {
|
241
|
+
for i in indices {
|
242
|
+
sums[*i as usize] += n.into_inner();
|
243
|
+
counts[*i as usize] += 1;
|
244
|
+
}
|
245
|
+
}
|
246
|
+
}
|
247
|
+
|
248
|
+
// Normalize the normals
|
249
|
+
for i in 0..sums.len() {
|
250
|
+
if counts[i] > 0 {
|
251
|
+
let v = sums[i] / counts[i] as f64;
|
252
|
+
sums[i] = v.normalize();
|
253
|
+
}
|
254
|
+
}
|
255
|
+
|
256
|
+
sums
|
217
257
|
}
|
218
258
|
}
|
219
259
|
|
@@ -7,8 +7,8 @@ mod tolerance_map;
|
|
7
7
|
pub use tolerance::Tolerance;
|
8
8
|
pub use tolerance_map::{ConstantTolMap, DiscreteDomainTolMap, ToleranceMap};
|
9
9
|
|
10
|
-
pub use dimension::Measurement;
|
11
10
|
use crate::{Iso3, To2D, To3D};
|
11
|
+
pub use dimension::Measurement;
|
12
12
|
|
13
13
|
pub type SurfaceDeviation2 = surface_deviation::SurfaceDeviation<2>;
|
14
14
|
pub type SurfaceDeviationSet2 = surface_deviation::SurfaceDeviationSet<2>;
|
@@ -18,7 +18,6 @@ pub type SurfaceDeviationSet3 = surface_deviation::SurfaceDeviationSet<3>;
|
|
18
18
|
pub type Distance2 = dimension::Distance<2>;
|
19
19
|
pub type Distance3 = dimension::Distance<3>;
|
20
20
|
|
21
|
-
|
22
21
|
// Conversions between 2D and 3D distances
|
23
22
|
impl Distance2 {
|
24
23
|
/// Convert a 2D distance to a 3D distance using an isometry transformation. The 2D distance
|
@@ -55,4 +54,4 @@ impl Distance3 {
|
|
55
54
|
let direction = (iso * self.direction).to_2d();
|
56
55
|
Distance2::new(a, b, Some(direction))
|
57
56
|
}
|
58
|
-
}
|
57
|
+
}
|
@@ -1,60 +1,58 @@
|
|
1
|
-
//! This module contains tools for working with 3D voxel grids.
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
///
|
8
|
-
///
|
9
|
-
///
|
10
|
-
///
|
11
|
-
///
|
12
|
-
///
|
13
|
-
///
|
14
|
-
///
|
15
|
-
///
|
16
|
-
///
|
17
|
-
///
|
18
|
-
/// ```
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
to_visit.
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
result
|
60
|
-
}
|
1
|
+
//! This module contains tools for working with 3D voxel grids.
|
2
|
+
|
3
|
+
use std::collections::HashSet;
|
4
|
+
|
5
|
+
/// This function takes a set of coordinates in a 3D grid and returns a list of clusters of
|
6
|
+
/// connected voxel coordinates.
|
7
|
+
///
|
8
|
+
/// # Arguments
|
9
|
+
///
|
10
|
+
/// * `indices`:
|
11
|
+
///
|
12
|
+
/// returns: Vec<Vec<(u32, u32, u32), Global>, Global>
|
13
|
+
///
|
14
|
+
/// # Examples
|
15
|
+
///
|
16
|
+
/// ```
|
17
|
+
///
|
18
|
+
/// ```
|
19
|
+
pub fn clusters_from_sparse(mut indices: HashSet<(i32, i32, i32)>) -> Vec<Vec<(i32, i32, i32)>> {
|
20
|
+
let mut results = Vec::new();
|
21
|
+
|
22
|
+
while !indices.is_empty() {
|
23
|
+
let mut working = Vec::new();
|
24
|
+
let mut to_visit = Vec::new();
|
25
|
+
|
26
|
+
to_visit.push(pop_index(&mut indices));
|
27
|
+
|
28
|
+
while !to_visit.is_empty() {
|
29
|
+
let current = to_visit.pop().unwrap();
|
30
|
+
working.push(current);
|
31
|
+
|
32
|
+
for x in -1..=1 {
|
33
|
+
for y in -1..=1 {
|
34
|
+
for z in -1..=1 {
|
35
|
+
if x == 0 && y == 0 && z == 0 {
|
36
|
+
continue;
|
37
|
+
}
|
38
|
+
|
39
|
+
let neighbor = (current.0 + x, current.1 + y, current.2 + z);
|
40
|
+
if indices.remove(&neighbor) {
|
41
|
+
to_visit.push(neighbor);
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
results.push(working.into_iter().collect());
|
49
|
+
}
|
50
|
+
|
51
|
+
results
|
52
|
+
}
|
53
|
+
|
54
|
+
fn pop_index(indices: &mut HashSet<(i32, i32, i32)>) -> (i32, i32, i32) {
|
55
|
+
let result = indices.iter().next().unwrap().clone();
|
56
|
+
indices.remove(&result);
|
57
|
+
result
|
58
|
+
}
|