From 4d46e39eb244d1f1d039d4874a0713d67a162ffc Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 31 Jul 2024 19:26:25 +0200 Subject: [PATCH 01/13] Document `PathApproxParams` --- crates/fj-core/src/algorithms/approx/circle.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/fj-core/src/algorithms/approx/circle.rs b/crates/fj-core/src/algorithms/approx/circle.rs index e1cb7b93d..cdcac2915 100644 --- a/crates/fj-core/src/algorithms/approx/circle.rs +++ b/crates/fj-core/src/algorithms/approx/circle.rs @@ -59,11 +59,13 @@ pub fn approx_circle( points } +/// Path approximation parameters for a circle struct PathApproxParams { increment: Scalar, } impl PathApproxParams { + /// Compute path approximation parameters for the given circle and tolerance pub fn for_circle( circle: &Circle, tolerance: impl Into, @@ -82,10 +84,12 @@ impl PathApproxParams { Self { increment } } + /// Return the increment pub fn increment(&self) -> Scalar { self.increment } + /// Generate points to approximate the circle within the boundary pub fn points( &self, boundary: impl Into>>, From e75225a0de40a6a74ddcf4d0b16185a712d4a5e4 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 31 Jul 2024 19:26:38 +0200 Subject: [PATCH 02/13] Make `PathApproxParams` public I need to use it from the `geometry` module. I expect that long-term, `approx` and `geometry` will get merged, and this code will turn into an internal implementation detail again. But for now, I don't want to rearchitect the whole module structure, before I have a better grasp on what the new geometry system is going to look like. So just making it public is the more pragmatic choice. --- crates/fj-core/src/algorithms/approx/circle.rs | 2 +- crates/fj-core/src/algorithms/approx/mod.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/circle.rs b/crates/fj-core/src/algorithms/approx/circle.rs index cdcac2915..900e6a367 100644 --- a/crates/fj-core/src/algorithms/approx/circle.rs +++ b/crates/fj-core/src/algorithms/approx/circle.rs @@ -60,7 +60,7 @@ pub fn approx_circle( } /// Path approximation parameters for a circle -struct PathApproxParams { +pub struct PathApproxParams { increment: Scalar, } diff --git a/crates/fj-core/src/algorithms/approx/mod.rs b/crates/fj-core/src/algorithms/approx/mod.rs index 16e25ec50..25e4243f3 100644 --- a/crates/fj-core/src/algorithms/approx/mod.rs +++ b/crates/fj-core/src/algorithms/approx/mod.rs @@ -25,7 +25,10 @@ use vertex::VertexApproxCache; use crate::geometry::Geometry; -pub use self::tolerance::{InvalidTolerance, Tolerance}; +pub use self::{ + circle::PathApproxParams, + tolerance::{InvalidTolerance, Tolerance}, +}; /// Approximate an object pub trait Approx: Sized { From ee755ac5e86b4948a0f6f4b00341f6b7210ae8e2 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 30 Jul 2024 19:43:55 +0200 Subject: [PATCH 03/13] Add `SurfaceGeom::triangle_at` --- crates/fj-core/src/geometry/surface.rs | 90 +++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 1 deletion(-) diff --git a/crates/fj-core/src/geometry/surface.rs b/crates/fj-core/src/geometry/surface.rs index f0a942ed7..9d049efe8 100644 --- a/crates/fj-core/src/geometry/surface.rs +++ b/crates/fj-core/src/geometry/surface.rs @@ -1,6 +1,8 @@ //! The geometry that defines a surface -use fj_math::{Line, Point, Transform, Vector}; +use fj_math::{Line, Point, Scalar, Transform, Triangle, Vector}; + +use crate::algorithms::approx::{PathApproxParams, Tolerance}; use super::GlobalPath; @@ -26,6 +28,92 @@ pub enum SurfaceGeom { } impl SurfaceGeom { + /// # Return the triangle at the provided point on the surface + /// + /// Select a triangle of the surface's triangle mesh representation, the one + /// at the provided surface point. Return that triangle, as well as the + /// barycentric coordinates of the provided point on the triangle. + /// + /// ## Triangle Size and Validity + /// + /// If a surface is curved along both axes, the triangle's size is chosen + /// such, that it approximates the surface, with the maximum allowed + /// deviation of the actual surface defined by the provided tolerance + /// argument. + /// + /// Otherwise, the size of the returned triangle is at least partially + /// arbitrary. Take the extreme case of a plane: Since it is not curved at + /// all, the returned triangle can be arbitrarily large. + /// + /// However, since surfaces are infinite, and we can't represent infinite + /// triangles, there is no sensible upper bound for the size. Instead, to + /// prevent an arbitrary choice for the size of triangles, which would imply + /// properties of the surface that are not true, and might therefore be + /// confusing, the triangles returned by this function have a length of zero + /// along axes that do not require approximation. + /// + /// The most extreme case would be a plane, for which the returned triangle + /// is collapsed to a point. For a cylinder, the triangle would have the + /// appropriate width to approximate the curved axis given the provided + /// tolerance, while having zero height. + /// + /// ## Implementation Note + /// + /// At the time this was written, there was no dedicated type to represent + /// barycentric coordinates. Nor any other code that used them, I think. + /// + /// If this changes, and a special type for barycentric coordinates is + /// added, it would make sense to return that here. + pub fn triangle_at( + &self, + point_surface: impl Into>, + tolerance: impl Into, + ) -> (Triangle<3>, [Scalar; 3]) { + let point_surface = point_surface.into(); + + let Self::Basic { u, v } = self; + match u { + GlobalPath::Circle(circle) => { + let params = PathApproxParams::for_circle(circle, tolerance); + + let a = point_surface.u - params.increment(); + let b = point_surface.u + params.increment(); + let c = a; // triangle is degenerate, as per function docs + + let triangle_points_in_circle_space = [a, b, c]; + let triangle_points_in_global_space = + triangle_points_in_circle_space + .map(|point_circle| { + circle.point_from_circle_coords([point_circle]) + }) + .map(|point_global| { + point_global + *v * point_surface.v + }); + + let triangle = Triangle::from(triangle_points_in_global_space); + let barycentric_coords = [0.5, 0.5, 0.0].map(Into::into); + + (triangle, barycentric_coords) + } + GlobalPath::Line(line) => { + let a = line.direction(); + let b = *v; + + let point_global = + line.origin() + a * point_surface.u + b * point_surface.v; + + // We don't need to approximate a plane, so our triangle can be + // arbitrarily large or small. Here we choose the smallest + // possible size (it is collapsed to a point), as per the + // documentation of this function. + let triangle = Triangle::from([point_global; 3]); + let barycentric_coords = [1. / 3.; 3].map(Into::into); + + (triangle, barycentric_coords) + } + } + } + /// Convert a point in surface coordinates to model coordinates pub fn point_from_surface_coords( &self, From 8215ff90a7dc567d54472e8918df4784ccfe6374 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 6 Aug 2024 19:40:28 +0200 Subject: [PATCH 04/13] Prepare for follow-on change --- crates/fj-core/src/algorithms/approx/curve.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 1808f139b..d772512e5 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -76,6 +76,8 @@ fn approx_circle_on_straight_surface( surface: &SurfaceGeom, tolerance: impl Into, ) -> Vec> { + let tolerance = tolerance.into(); + approx_circle(circle, boundary, tolerance) .into_iter() .map(|(point_curve, point_surface)| { @@ -105,6 +107,8 @@ fn approx_line_on_any_surface( surface: &SurfaceGeom, tolerance: impl Into, ) -> Vec> { + let tolerance = tolerance.into(); + let range_u = CurveBoundary::from( boundary .inner From 9858e41fe295a5d0bcb32ef8cd09f5954599a525 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 6 Aug 2024 19:42:51 +0200 Subject: [PATCH 05/13] Prepare for follow-on change --- crates/fj-core/src/algorithms/approx/cycle.rs | 1 + crates/fj-core/src/algorithms/approx/vertex.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/fj-core/src/algorithms/approx/cycle.rs b/crates/fj-core/src/algorithms/approx/cycle.rs index 981fcb8f0..fe7113a29 100644 --- a/crates/fj-core/src/algorithms/approx/cycle.rs +++ b/crates/fj-core/src/algorithms/approx/cycle.rs @@ -53,6 +53,7 @@ pub fn approx_cycle( half_edge.curve(), surface, start_position_curve, + tolerance, &mut cache.vertex, geometry, ); diff --git a/crates/fj-core/src/algorithms/approx/vertex.rs b/crates/fj-core/src/algorithms/approx/vertex.rs index 92530dfb0..60dcfcb8f 100644 --- a/crates/fj-core/src/algorithms/approx/vertex.rs +++ b/crates/fj-core/src/algorithms/approx/vertex.rs @@ -8,7 +8,7 @@ use crate::{ topology::{Curve, Surface, Vertex}, }; -use super::ApproxPoint; +use super::{ApproxPoint, Tolerance}; /// # Approximate a vertex position pub fn approx_vertex( @@ -16,6 +16,7 @@ pub fn approx_vertex( curve: &Handle, surface: &Handle, position_curve: Point<1>, + _: impl Into, cache: &mut VertexApproxCache, geometry: &Geometry, ) -> ApproxPoint<1> { From 7b54a176f6ab724733e51f90eb1cb0a8a6a9f7b5 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 7 Aug 2024 20:28:43 +0200 Subject: [PATCH 06/13] Group function arguments to increase clarity --- .../coincident_half_edges_are_not_siblings.rs | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs b/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs index 4c3b60505..d0e398bb2 100644 --- a/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs +++ b/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs @@ -117,24 +117,28 @@ impl ValidationCheck for CoincidentHalfEdgesAreNotSiblings { } let Some(points_and_distances) = distances( - half_edge_a.clone(), - object - .find_cycle_of_half_edge(half_edge_a) - .unwrap() - .half_edges() - .after(half_edge_a) - .unwrap() - .start_vertex(), - surface_a, - half_edge_b.clone(), - object - .find_cycle_of_half_edge(half_edge_b) - .unwrap() - .half_edges() - .after(half_edge_b) - .unwrap() - .start_vertex(), - surface_b, + ( + half_edge_a.clone(), + object + .find_cycle_of_half_edge(half_edge_a) + .unwrap() + .half_edges() + .after(half_edge_a) + .unwrap() + .start_vertex(), + surface_a, + ), + ( + half_edge_b.clone(), + object + .find_cycle_of_half_edge(half_edge_b) + .unwrap() + .half_edges() + .after(half_edge_b) + .unwrap() + .start_vertex(), + surface_b, + ), geometry, ) else { // The geometry to compute the distances is not available, @@ -179,12 +183,16 @@ impl ValidationCheck for CoincidentHalfEdgesAreNotSiblings { /// /// Returns an [`Iterator`] of the distance at each sample. fn distances( - half_edge_a: Handle, - end_vertex_a: &Handle, - surface_a: &Handle, - half_edge_b: Handle, - end_vertex_b: &Handle, - surface_b: &Handle, + (half_edge_a, end_vertex_a, surface_a): ( + Handle, + &Handle, + &Handle, + ), + (half_edge_b, end_vertex_b, surface_b): ( + Handle, + &Handle, + &Handle, + ), geometry: &Geometry, ) -> Option; 2], Scalar)>> { fn sample( From 26dfddb7af2dace6a1159b075fd97c09047dbd39 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 7 Aug 2024 20:29:27 +0200 Subject: [PATCH 07/13] Prepare for follow-on change --- .../checks/coincident_half_edges_are_not_siblings.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs b/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs index d0e398bb2..7e048664b 100644 --- a/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs +++ b/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs @@ -3,6 +3,7 @@ use std::fmt; use fj_math::{Point, Scalar}; use crate::{ + algorithms::approx::Tolerance, geometry::{CurveBoundary, Geometry}, queries::{ AllHalfEdgesWithSurface, BoundingVerticesOfHalfEdge, CycleOfHalfEdge, @@ -139,6 +140,7 @@ impl ValidationCheck for CoincidentHalfEdgesAreNotSiblings { .start_vertex(), surface_b, ), + config.tolerance, geometry, ) else { // The geometry to compute the distances is not available, @@ -193,6 +195,7 @@ fn distances( &Handle, &Handle, ), + _: Tolerance, geometry: &Geometry, ) -> Option; 2], Scalar)>> { fn sample( From 64f0a6d75732c92b08225048f093b03dc00c2eca Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 7 Aug 2024 20:30:25 +0200 Subject: [PATCH 08/13] Prepare for follow-on change --- .../coincident_half_edges_are_not_siblings.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs b/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs index 7e048664b..25b6a289e 100644 --- a/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs +++ b/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs @@ -195,7 +195,7 @@ fn distances( &Handle, &Handle, ), - _: Tolerance, + tolerance: Tolerance, geometry: &Geometry, ) -> Option; 2], Scalar)>> { fn sample( @@ -203,6 +203,7 @@ fn distances( half_edge: &Handle, end_vertex: &Handle, surface: &Handle, + _: Tolerance, geometry: &Geometry, ) -> Option> { let [start, end] = [ @@ -241,13 +242,20 @@ fn distances( let mut distances = Vec::new(); for i in 0..sample_count { let percent = i as f64 * step; - let sample1 = - sample(percent, &half_edge_a, end_vertex_a, surface_a, geometry)?; + let sample1 = sample( + percent, + &half_edge_a, + end_vertex_a, + surface_a, + tolerance, + geometry, + )?; let sample2 = sample( 1.0 - percent, &half_edge_b, end_vertex_b, surface_b, + tolerance, geometry, )?; distances.push(([sample1, sample2], sample1.distance_to(&sample2))) From 30f08d9f5bc691543caa9ba5fe4c08e9940af3d1 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 7 Aug 2024 20:31:34 +0200 Subject: [PATCH 09/13] Prepare for follow-on change --- crates/fj-core/src/algorithms/approx/curve.rs | 10 ++++--- .../fj-core/src/algorithms/approx/vertex.rs | 4 +-- .../src/algorithms/bounding_volume/face.rs | 29 +++++++++++++++---- .../fj-core/src/algorithms/triangulate/mod.rs | 22 +++++++------- crates/fj-core/src/geometry/surface.rs | 11 +++++-- crates/fj-core/src/operations/holes.rs | 5 +++- crates/fj-core/src/operations/sweep/path.rs | 8 +++-- crates/fj-core/src/validate/solid.rs | 1 + .../coincident_half_edges_are_not_siblings.rs | 4 +-- .../checks/curve_geometry_mismatch.rs | 14 ++++++--- 10 files changed, 75 insertions(+), 33 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index d772512e5..a9ccfa50f 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -95,7 +95,8 @@ fn approx_circle_on_straight_surface( // point available, so it needs to be computed later anyway, in // the general case. - let point_global = surface.point_from_surface_coords(point_surface); + let point_global = + surface.point_from_surface_coords(point_surface, tolerance); ApproxPoint::new(point_curve, point_global) }) .collect() @@ -125,7 +126,8 @@ fn approx_line_on_any_surface( for (u, _) in approx_u { let t = (u.t - line.origin().u) / line.direction().u; let point_surface = line.point_from_line_coords([t]); - let point_global = surface.point_from_surface_coords(point_surface); + let point_global = + surface.point_from_surface_coords(point_surface, tolerance); points.push(ApproxPoint::new(u, point_global)); } @@ -262,7 +264,7 @@ mod tests { .layers .geometry .of_surface(&surface) - .point_from_surface_coords(point_surface); + .point_from_surface_coords(point_surface, tolerance); ApproxPoint::new(point_local, point_global) }) .collect::>(); @@ -290,7 +292,7 @@ mod tests { .layers .geometry .of_surface(&surface) - .point_from_surface_coords(point_surface); + .point_from_surface_coords(point_surface, tolerance); ApproxPoint::new(point_local, point_global) }) .collect::>(); diff --git a/crates/fj-core/src/algorithms/approx/vertex.rs b/crates/fj-core/src/algorithms/approx/vertex.rs index 60dcfcb8f..813cef9fd 100644 --- a/crates/fj-core/src/algorithms/approx/vertex.rs +++ b/crates/fj-core/src/algorithms/approx/vertex.rs @@ -16,7 +16,7 @@ pub fn approx_vertex( curve: &Handle, surface: &Handle, position_curve: Point<1>, - _: impl Into, + tolerance: impl Into, cache: &mut VertexApproxCache, geometry: &Geometry, ) -> ApproxPoint<1> { @@ -33,7 +33,7 @@ pub fn approx_vertex( None => { let position_global = geometry .of_surface(surface) - .point_from_surface_coords(position_surface); + .point_from_surface_coords(position_surface, tolerance); cache.insert(vertex, position_global) } }; diff --git a/crates/fj-core/src/algorithms/bounding_volume/face.rs b/crates/fj-core/src/algorithms/bounding_volume/face.rs index 2de9af4d0..8520a87ba 100644 --- a/crates/fj-core/src/algorithms/bounding_volume/face.rs +++ b/crates/fj-core/src/algorithms/bounding_volume/face.rs @@ -1,8 +1,9 @@ use std::ops::Deref; -use fj_math::Aabb; +use fj_math::{Aabb, Vector}; use crate::{ + algorithms::approx::Tolerance, geometry::{Geometry, GlobalPath, SurfaceGeom}, topology::Face, }; @@ -29,10 +30,28 @@ impl super::BoundingVolume<3> for &Face { aabb_bottom.merged(&aabb_top) } - GlobalPath::Line(_) => Aabb { - min: surface.point_from_surface_coords(aabb2.min), - max: surface.point_from_surface_coords(aabb2.max), - }, + GlobalPath::Line(_) => { + // A bounding volume must include the body it bounds, + // but does not need to match it precisely. So it's + // okay, if it's a bit larger. + // + // Let's just choose a reasonable tolerance value here, + // then make sure we enlarge the AABB accordingly, to + // make sure it fits. + let tolerance_f64 = 0.001; + let tolerance = Tolerance::from_scalar(tolerance_f64) + .expect("Tolerance provided is larger than zero"); + let offset = Vector::from([tolerance_f64; 3]); + + Aabb { + min: surface.point_from_surface_coords( + aabb2.min, tolerance, + ) - offset, + max: surface.point_from_surface_coords( + aabb2.max, tolerance, + ) + offset, + } + } } }) } diff --git a/crates/fj-core/src/algorithms/triangulate/mod.rs b/crates/fj-core/src/algorithms/triangulate/mod.rs index 7ef1c391c..ad6d3606d 100644 --- a/crates/fj-core/src/algorithms/triangulate/mod.rs +++ b/crates/fj-core/src/algorithms/triangulate/mod.rs @@ -180,32 +180,32 @@ mod tests { .layers .geometry .of_surface(&surface) - .point_from_surface_coords(a); + .point_from_surface_coords(a, core.tolerance()); let b = core .layers .geometry .of_surface(&surface) - .point_from_surface_coords(b); + .point_from_surface_coords(b, core.tolerance()); let e = core .layers .geometry .of_surface(&surface) - .point_from_surface_coords(e); + .point_from_surface_coords(e, core.tolerance()); let f = core .layers .geometry .of_surface(&surface) - .point_from_surface_coords(f); + .point_from_surface_coords(f, core.tolerance()); let g = core .layers .geometry .of_surface(&surface) - .point_from_surface_coords(g); + .point_from_surface_coords(g, core.tolerance()); let h = core .layers .geometry .of_surface(&surface) - .point_from_surface_coords(h); + .point_from_surface_coords(h, core.tolerance()); // Let's test that some correct triangles are present. We don't need to // test them all. @@ -275,27 +275,27 @@ mod tests { .layers .geometry .of_surface(&surface) - .point_from_surface_coords(a); + .point_from_surface_coords(a, core.tolerance()); let b = core .layers .geometry .of_surface(&surface) - .point_from_surface_coords(b); + .point_from_surface_coords(b, core.tolerance()); let c = core .layers .geometry .of_surface(&surface) - .point_from_surface_coords(c); + .point_from_surface_coords(c, core.tolerance()); let d = core .layers .geometry .of_surface(&surface) - .point_from_surface_coords(d); + .point_from_surface_coords(d, core.tolerance()); let e = core .layers .geometry .of_surface(&surface) - .point_from_surface_coords(e); + .point_from_surface_coords(e, core.tolerance()); assert!(triangles.contains_triangle([a, b, d])); assert!(triangles.contains_triangle([a, d, e])); diff --git a/crates/fj-core/src/geometry/surface.rs b/crates/fj-core/src/geometry/surface.rs index 9d049efe8..81f91974b 100644 --- a/crates/fj-core/src/geometry/surface.rs +++ b/crates/fj-core/src/geometry/surface.rs @@ -118,6 +118,7 @@ impl SurfaceGeom { pub fn point_from_surface_coords( &self, point: impl Into>, + _: impl Into, ) -> Point<3> { let point = point.into(); let Self::Basic { u, .. } = self; @@ -157,7 +158,10 @@ mod tests { use fj_math::{Line, Point, Vector}; use pretty_assertions::assert_eq; - use crate::geometry::{GlobalPath, SurfaceGeom}; + use crate::{ + algorithms::approx::Tolerance, + geometry::{GlobalPath, SurfaceGeom}, + }; #[test] fn point_from_surface_coords() { @@ -169,8 +173,11 @@ mod tests { v: Vector::from([0., 0., 2.]), }; + // Value doesn't matter; we're dealing with a plane. + let tolerance = Tolerance::from_scalar(1.).unwrap(); + assert_eq!( - surface.point_from_surface_coords([2., 4.]), + surface.point_from_surface_coords([2., 4.], tolerance), Point::from([1., 5., 9.]), ); } diff --git a/crates/fj-core/src/operations/holes.rs b/crates/fj-core/src/operations/holes.rs index 706ceac4e..834720277 100644 --- a/crates/fj-core/src/operations/holes.rs +++ b/crates/fj-core/src/operations/holes.rs @@ -93,7 +93,10 @@ impl AddHole for Shell { core.layers .geometry .of_surface(location.face.surface()) - .point_from_surface_coords(location.position) + .point_from_surface_coords( + location.position, + core.tolerance(), + ) }; let entry_point = point(&entry_location); diff --git a/crates/fj-core/src/operations/sweep/path.rs b/crates/fj-core/src/operations/sweep/path.rs index 63d00dc8f..b5e51f37e 100644 --- a/crates/fj-core/src/operations/sweep/path.rs +++ b/crates/fj-core/src/operations/sweep/path.rs @@ -66,7 +66,10 @@ impl SweepSurfacePath for SurfacePath { let u = match self { SurfacePath::Circle(circle) => { - let center = surface.point_from_surface_coords(circle.center()); + let center = surface.point_from_surface_coords( + circle.center(), + core.tolerance(), + ); let a = surface.vector_from_surface_coords(circle.a()); let b = surface.vector_from_surface_coords(circle.b()); @@ -75,7 +78,8 @@ impl SweepSurfacePath for SurfacePath { GlobalPath::Circle(circle) } SurfacePath::Line(line) => { - let origin = surface.point_from_surface_coords(line.origin()); + let origin = surface + .point_from_surface_coords(line.origin(), core.tolerance()); let direction = surface.vector_from_surface_coords(line.direction()); diff --git a/crates/fj-core/src/validate/solid.rs b/crates/fj-core/src/validate/solid.rs index 64d0ebae9..4f400596f 100644 --- a/crates/fj-core/src/validate/solid.rs +++ b/crates/fj-core/src/validate/solid.rs @@ -125,6 +125,7 @@ impl SolidValidationError { .unwrap() .position, ), + config.tolerance, ), h.start_vertex().clone(), )) diff --git a/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs b/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs index 25b6a289e..404602260 100644 --- a/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs +++ b/crates/fj-core/src/validation/checks/coincident_half_edges_are_not_siblings.rs @@ -203,7 +203,7 @@ fn distances( half_edge: &Handle, end_vertex: &Handle, surface: &Handle, - _: Tolerance, + tolerance: Tolerance, geometry: &Geometry, ) -> Option> { let [start, end] = [ @@ -229,7 +229,7 @@ fn distances( Some( geometry .of_surface(surface) - .point_from_surface_coords(surface_coords), + .point_from_surface_coords(surface_coords, tolerance), ) } diff --git a/crates/fj-core/src/validation/checks/curve_geometry_mismatch.rs b/crates/fj-core/src/validation/checks/curve_geometry_mismatch.rs index f8f16ec8d..06b81322f 100644 --- a/crates/fj-core/src/validation/checks/curve_geometry_mismatch.rs +++ b/crates/fj-core/src/validation/checks/curve_geometry_mismatch.rs @@ -162,10 +162,16 @@ impl ValidationCheck for CurveGeometryMismatch { .path .point_from_path_coords(point_curve); - let a_global = - surface_geom_a.point_from_surface_coords(a_surface); - let b_global = - surface_geom_b.point_from_surface_coords(b_surface); + let a_global = surface_geom_a + .point_from_surface_coords( + a_surface, + config.tolerance, + ); + let b_global = surface_geom_b + .point_from_surface_coords( + b_surface, + config.tolerance, + ); let distance = (a_global - b_global).magnitude(); From de6f8fb157844f5fa768bb82624bbb34c693afbc Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 7 Aug 2024 20:32:25 +0200 Subject: [PATCH 10/13] Rewrite conversion function on top of `triangle_at` --- crates/fj-core/src/geometry/surface.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/fj-core/src/geometry/surface.rs b/crates/fj-core/src/geometry/surface.rs index 81f91974b..240c94d29 100644 --- a/crates/fj-core/src/geometry/surface.rs +++ b/crates/fj-core/src/geometry/surface.rs @@ -118,12 +118,10 @@ impl SurfaceGeom { pub fn point_from_surface_coords( &self, point: impl Into>, - _: impl Into, + tolerance: impl Into, ) -> Point<3> { - let point = point.into(); - let Self::Basic { u, .. } = self; - u.point_from_path_coords([point.u]) - + self.path_to_line().vector_from_line_coords([point.v]) + let (triangle, barycentric_coords) = self.triangle_at(point, tolerance); + triangle.point_from_barycentric_coords(barycentric_coords) } /// Convert a vector in surface coordinates to model coordinates From dc499ab859096c8dbbc6c8d128a150ef87d377ff Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 13 Aug 2024 18:49:30 +0200 Subject: [PATCH 11/13] Prepare for follow-on change --- crates/fj-core/src/geometry/surface.rs | 6 +++++- crates/fj-core/src/operations/sweep/path.rs | 12 ++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/fj-core/src/geometry/surface.rs b/crates/fj-core/src/geometry/surface.rs index 240c94d29..6d00e29f1 100644 --- a/crates/fj-core/src/geometry/surface.rs +++ b/crates/fj-core/src/geometry/surface.rs @@ -128,6 +128,7 @@ impl SurfaceGeom { pub fn vector_from_surface_coords( &self, vector: impl Into>, + _: impl Into, ) -> Vector<3> { let vector = vector.into(); let Self::Basic { u, .. } = self; @@ -190,8 +191,11 @@ mod tests { v: Vector::from([0., 0., 2.]), }; + // Value doesn't matter; we're dealing with a plane. + let tolerance = Tolerance::from_scalar(1.).unwrap(); + assert_eq!( - surface.vector_from_surface_coords([2., 4.]), + surface.vector_from_surface_coords([2., 4.], tolerance), Vector::from([0., 4., 8.]), ); } diff --git a/crates/fj-core/src/operations/sweep/path.rs b/crates/fj-core/src/operations/sweep/path.rs index b5e51f37e..f5bdc65f3 100644 --- a/crates/fj-core/src/operations/sweep/path.rs +++ b/crates/fj-core/src/operations/sweep/path.rs @@ -70,8 +70,10 @@ impl SweepSurfacePath for SurfacePath { circle.center(), core.tolerance(), ); - let a = surface.vector_from_surface_coords(circle.a()); - let b = surface.vector_from_surface_coords(circle.b()); + let a = surface + .vector_from_surface_coords(circle.a(), core.tolerance()); + let b = surface + .vector_from_surface_coords(circle.b(), core.tolerance()); let circle = Circle::new(center, a, b); @@ -80,8 +82,10 @@ impl SweepSurfacePath for SurfacePath { SurfacePath::Line(line) => { let origin = surface .point_from_surface_coords(line.origin(), core.tolerance()); - let direction = - surface.vector_from_surface_coords(line.direction()); + let direction = surface.vector_from_surface_coords( + line.direction(), + core.tolerance(), + ); let line = Line::from_origin_and_direction(origin, direction); From bea80db4460870e02227f794db6a9ca4665c0b31 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 13 Aug 2024 18:52:07 +0200 Subject: [PATCH 12/13] Add `SurfaceGeom::origin` --- crates/fj-core/src/geometry/surface.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/fj-core/src/geometry/surface.rs b/crates/fj-core/src/geometry/surface.rs index 6d00e29f1..c1aa94c44 100644 --- a/crates/fj-core/src/geometry/surface.rs +++ b/crates/fj-core/src/geometry/surface.rs @@ -28,6 +28,15 @@ pub enum SurfaceGeom { } impl SurfaceGeom { + /// # Access the origin of the surface + pub fn origin(&self) -> Point<3> { + let Self::Basic { u, .. } = self; + match u { + GlobalPath::Circle(circle) => circle.center(), + GlobalPath::Line(line) => line.origin(), + } + } + /// # Return the triangle at the provided point on the surface /// /// Select a triangle of the surface's triangle mesh representation, the one From 884c28416c038dbe293a5f02ba39c203ca1a65c4 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Tue, 13 Aug 2024 18:58:04 +0200 Subject: [PATCH 13/13] Rewrite conversion function on top of `triangle_at` --- crates/fj-core/src/geometry/surface.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/crates/fj-core/src/geometry/surface.rs b/crates/fj-core/src/geometry/surface.rs index c1aa94c44..8a5348a6f 100644 --- a/crates/fj-core/src/geometry/surface.rs +++ b/crates/fj-core/src/geometry/surface.rs @@ -1,6 +1,6 @@ //! The geometry that defines a surface -use fj_math::{Line, Point, Scalar, Transform, Triangle, Vector}; +use fj_math::{Point, Scalar, Transform, Triangle, Vector}; use crate::algorithms::approx::{PathApproxParams, Tolerance}; @@ -137,17 +137,12 @@ impl SurfaceGeom { pub fn vector_from_surface_coords( &self, vector: impl Into>, - _: impl Into, + tolerance: impl Into, ) -> Vector<3> { let vector = vector.into(); - let Self::Basic { u, .. } = self; - u.vector_from_path_coords([vector.u]) - + self.path_to_line().vector_from_line_coords([vector.v]) - } - - fn path_to_line(&self) -> Line<3> { - let Self::Basic { u, v } = self; - Line::from_origin_and_direction(u.origin(), *v) + let point = + self.point_from_surface_coords(Point { coords: vector }, tolerance); + point - self.origin() } /// Transform the surface geometry