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.
Files changed (149) hide show
  1. {engeom-0.2.8 → engeom-0.2.9}/Cargo.lock +1 -1
  2. {engeom-0.2.8 → engeom-0.2.9}/Cargo.toml +1 -1
  3. {engeom-0.2.8 → engeom-0.2.9}/PKG-INFO +1 -1
  4. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil/camber.rs +6 -3
  5. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/edges.rs +3 -3
  6. engeom-0.2.9/engeom/src/geom3/mesh/outline.rs +281 -0
  7. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/patches.rs +5 -5
  8. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh.rs +47 -7
  9. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology.rs +2 -3
  10. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/raster3.rs +58 -60
  11. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/_plot/matplotlib.py +50 -0
  12. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/geom3.pyi +19 -0
  13. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/plot.py +1 -1
  14. {engeom-0.2.8 → engeom-0.2.9}/src/conversions.rs +10 -0
  15. {engeom-0.2.8 → engeom-0.2.9}/src/mesh.rs +77 -1
  16. {engeom-0.2.8 → engeom-0.2.9}/.github/workflows/CI.yml +0 -0
  17. {engeom-0.2.8 → engeom-0.2.9}/.gitignore +0 -0
  18. {engeom-0.2.8 → engeom-0.2.9}/.gitmodules +0 -0
  19. {engeom-0.2.8 → engeom-0.2.9}/README.md +0 -0
  20. {engeom-0.2.8 → engeom-0.2.9}/docs/airfoils/intro.md +0 -0
  21. {engeom-0.2.8 → engeom-0.2.9}/docs/api/airfoil.md +0 -0
  22. {engeom-0.2.8 → engeom-0.2.9}/docs/api/engeom.md +0 -0
  23. {engeom-0.2.8 → engeom-0.2.9}/docs/api/geom2.md +0 -0
  24. {engeom-0.2.8 → engeom-0.2.9}/docs/api/geom3.md +0 -0
  25. {engeom-0.2.8 → engeom-0.2.9}/docs/api/metrology.md +0 -0
  26. {engeom-0.2.8 → engeom-0.2.9}/docs/api/plot.md +0 -0
  27. {engeom-0.2.8 → engeom-0.2.9}/docs/bounding_volumes.md +0 -0
  28. {engeom-0.2.8 → engeom-0.2.9}/docs/curves.md +0 -0
  29. {engeom-0.2.8 → engeom-0.2.9}/docs/images/surface_point_meas.svg +0 -0
  30. {engeom-0.2.8 → engeom-0.2.9}/docs/index.md +0 -0
  31. {engeom-0.2.8 → engeom-0.2.9}/docs/isometries.md +0 -0
  32. {engeom-0.2.8 → engeom-0.2.9}/docs/meshes.md +0 -0
  33. {engeom-0.2.8 → engeom-0.2.9}/docs/metrology.md +0 -0
  34. {engeom-0.2.8 → engeom-0.2.9}/docs/numpy.md +0 -0
  35. {engeom-0.2.8 → engeom-0.2.9}/docs/planes_circles_lines.md +0 -0
  36. {engeom-0.2.8 → engeom-0.2.9}/docs/points_vectors.md +0 -0
  37. {engeom-0.2.8 → engeom-0.2.9}/docs/surf_points.md +0 -0
  38. {engeom-0.2.8 → engeom-0.2.9}/docs/svd_basis.md +0 -0
  39. {engeom-0.2.8 → engeom-0.2.9}/engeom/.gitignore +0 -0
  40. {engeom-0.2.8 → engeom-0.2.9}/engeom/Cargo.lock +0 -0
  41. {engeom-0.2.8 → engeom-0.2.9}/engeom/Cargo.toml +0 -0
  42. {engeom-0.2.8 → engeom-0.2.9}/engeom/README.md +0 -0
  43. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/airfoils/camber.md +0 -0
  44. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/airfoils/overview.md +0 -0
  45. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/common/angles.md +0 -0
  46. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/common/core_space.md +0 -0
  47. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/common/discrete_domain.md +0 -0
  48. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/common/images/surface_point_meas.svg +0 -0
  49. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/common/svd_basis.md +0 -0
  50. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/geom2/alignment.md +0 -0
  51. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/geom2/curve.md +0 -0
  52. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/geom2/point_collections.md +0 -0
  53. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/geom2/shapes.md +0 -0
  54. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/index.md +0 -0
  55. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/javascripts/mathjax.js +0 -0
  56. {engeom-0.2.8 → engeom-0.2.9}/engeom/docs/python_rust.md +0 -0
  57. {engeom-0.2.8 → engeom-0.2.9}/engeom/mkdocs.yml +0 -0
  58. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil/edges.rs +0 -0
  59. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil/helpers.rs +0 -0
  60. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil/inscribed_circle.rs +0 -0
  61. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil/orientation.rs +0 -0
  62. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/airfoil.rs +0 -0
  63. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/align.rs +0 -0
  64. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/angles.rs +0 -0
  65. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/convert_2d_3d.rs +0 -0
  66. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/discrete_domain.rs +0 -0
  67. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/indices.rs +0 -0
  68. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/interval.rs +0 -0
  69. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/kd_tree.rs +0 -0
  70. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/points.rs +0 -0
  71. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/poisson_disk.rs +0 -0
  72. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/surface_point.rs +0 -0
  73. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/svd_basis.rs +0 -0
  74. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common/vec_f64.rs +0 -0
  75. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/common.rs +0 -0
  76. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/errors.rs +0 -0
  77. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/func1/common_functions.rs +0 -0
  78. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/func1/polynomial.rs +0 -0
  79. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/func1/series1.rs +0 -0
  80. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/func1.rs +0 -0
  81. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/aabb2.rs +0 -0
  82. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/align2/jacobian.rs +0 -0
  83. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/align2/points_to_curve.rs +0 -0
  84. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/align2/rc_params2.rs +0 -0
  85. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/align2.rs +0 -0
  86. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/angles2.rs +0 -0
  87. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/circle2.rs +0 -0
  88. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/curve2.rs +0 -0
  89. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/hull.rs +0 -0
  90. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/line2.rs +0 -0
  91. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2/polyline2.rs +0 -0
  92. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom2.rs +1 -1
  93. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/align3/jacobian.rs +0 -0
  94. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/align3/multi_param.rs +0 -0
  95. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/align3/points_to_mesh.rs +0 -0
  96. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/align3/rotations.rs +0 -0
  97. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/align3.rs +0 -0
  98. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/curve3.rs +0 -0
  99. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/conformal.rs +0 -0
  100. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/faces.rs +0 -0
  101. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/filtering.rs +0 -0
  102. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/measurement.rs +0 -0
  103. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/queries.rs +0 -0
  104. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/sampling.rs +0 -0
  105. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/mesh/uv_mapping.rs +0 -0
  106. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/plane3.rs +0 -0
  107. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3/point_cloud.rs +0 -0
  108. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/geom3.rs +0 -0
  109. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/io.rs +0 -0
  110. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/lib.rs +1 -1
  111. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology/dimension.rs +1 -1
  112. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology/line_profiles.rs +0 -0
  113. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology/surface_deviation.rs +0 -0
  114. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology/tolerance.rs +0 -0
  115. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/metrology/tolerance_map.rs +0 -0
  116. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/stats.rs +0 -0
  117. {engeom-0.2.8 → engeom-0.2.9}/engeom/src/utility.rs +0 -0
  118. {engeom-0.2.8 → engeom-0.2.9}/mkdocs.yml +0 -0
  119. {engeom-0.2.8 → engeom-0.2.9}/pyproject.toml +0 -0
  120. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/__init__.py +0 -0
  121. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/_plot/__init__.py +0 -0
  122. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/_plot/common.py +0 -0
  123. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/_plot/pyvista.py +0 -0
  124. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/airfoil/__init__.py +0 -0
  125. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/airfoil.pyi +0 -0
  126. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/align/__init__.py +0 -0
  127. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/align.pyi +0 -0
  128. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/engeom.pyi +0 -0
  129. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/geom2/__init__.py +0 -0
  130. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/geom2.pyi +0 -0
  131. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/geom3/__init__.py +0 -0
  132. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/metrology/__init__.py +0 -0
  133. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/metrology.pyi +0 -0
  134. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/raster3/__init__.py +0 -0
  135. {engeom-0.2.8 → engeom-0.2.9}/python/engeom/raster3.pyi +0 -0
  136. {engeom-0.2.8 → engeom-0.2.9}/python/tests/test_all.py +0 -0
  137. {engeom-0.2.8 → engeom-0.2.9}/python/tests/test_geom2_simple.py +0 -0
  138. {engeom-0.2.8 → engeom-0.2.9}/python/tests/test_geom3_simple.py +0 -0
  139. {engeom-0.2.8 → engeom-0.2.9}/src/airfoil.rs +0 -0
  140. {engeom-0.2.8 → engeom-0.2.9}/src/alignments.rs +0 -0
  141. {engeom-0.2.8 → engeom-0.2.9}/src/bounding.rs +0 -0
  142. {engeom-0.2.8 → engeom-0.2.9}/src/common.rs +0 -0
  143. {engeom-0.2.8 → engeom-0.2.9}/src/geom2.rs +0 -0
  144. {engeom-0.2.8 → engeom-0.2.9}/src/geom3.rs +0 -0
  145. {engeom-0.2.8 → engeom-0.2.9}/src/lib.rs +1 -1
  146. {engeom-0.2.8 → engeom-0.2.9}/src/metrology.rs +0 -0
  147. {engeom-0.2.8 → engeom-0.2.9}/src/raster.rs +0 -0
  148. {engeom-0.2.8 → engeom-0.2.9}/src/ray_casting.rs +0 -0
  149. {engeom-0.2.8 → engeom-0.2.9}/src/svd_basis.rs +0 -0
@@ -1025,7 +1025,7 @@ dependencies = [
1025
1025
 
1026
1026
  [[package]]
1027
1027
  name = "py-engeom"
1028
- version = "0.2.8"
1028
+ version = "0.2.9"
1029
1029
  dependencies = [
1030
1030
  "engeom",
1031
1031
  "numpy",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "py-engeom"
3
- version = "0.2.8"
3
+ version = "0.2.9"
4
4
  edition = "2021"
5
5
 
6
6
  # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: engeom
3
- Version: 0.2.8
3
+ Version: 0.2.9
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -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(camber_line.at_front().point(), camber_line.at_back().point())
27
- .map_err(|_| "Failed to create segment from camber line while detecting face orientation")?;
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
- panic!("Duplicate boundary point found!");
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
- patches
214
- .iter()
215
- .flat_map(|patch| patches::compute_boundary_points(self, patch))
216
- .collect()
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
- use std::collections::HashSet;
5
-
6
-
7
- /// This function takes a set of coordinates in a 3D grid and returns a list of clusters of
8
- /// connected voxel coordinates.
9
- ///
10
- /// # Arguments
11
- ///
12
- /// * `indices`:
13
- ///
14
- /// returns: Vec<Vec<(u32, u32, u32), Global>, Global>
15
- ///
16
- /// # Examples
17
- ///
18
- /// ```
19
- ///
20
- /// ```
21
- pub fn clusters_from_sparse(mut indices: HashSet<(i32, i32, i32)>) -> Vec<Vec<(i32, i32, i32)>> {
22
- let mut results = Vec::new();
23
-
24
- while !indices.is_empty() {
25
- let mut working = Vec::new();
26
- let mut to_visit = Vec::new();
27
-
28
- to_visit.push(pop_index(&mut indices));
29
-
30
- while !to_visit.is_empty() {
31
- let current = to_visit.pop().unwrap();
32
- working.push(current);
33
-
34
- for x in -1..=1 {
35
- for y in -1..=1 {
36
- for z in -1..=1 {
37
- if x == 0 && y == 0 && z == 0 {
38
- continue;
39
- }
40
-
41
- let neighbor = (current.0 + x, current.1 + y, current.2 + z);
42
- if indices.remove(&neighbor) {
43
- to_visit.push(neighbor);
44
- }
45
- }
46
- }
47
- }
48
- }
49
-
50
- results.push(working.into_iter().collect());
51
- }
52
-
53
- results
54
- }
55
-
56
- fn pop_index(indices: &mut HashSet<(i32, i32, i32)>) -> (i32, i32, i32) {
57
- let result = indices.iter().next().unwrap().clone();
58
- indices.remove(&result);
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
+ }