From 5c3be8d63af81cdd3243dd657dc9eaf279a2842f Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:01:25 +0200 Subject: [PATCH 01/50] Update order of arguments to increase clarity The surface is very closely related to the curve, especially its geometry, while the half-edge only provide supplemental information about the approximation. --- crates/fj-core/src/algorithms/approx/curve.rs | 12 ++++++------ crates/fj-core/src/algorithms/approx/edge.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 82bff3dc3..ef07717aa 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -15,7 +15,7 @@ use crate::{ use super::{Approx, ApproxPoint, Tolerance}; -impl Approx for (&Handle, &HalfEdgeGeom, &Handle) { +impl Approx for (&Handle, &Handle, &HalfEdgeGeom) { type Approximation = CurveApprox; type Cache = CurveApproxCache; @@ -25,7 +25,7 @@ impl Approx for (&Handle, &HalfEdgeGeom, &Handle) { cache: &mut Self::Cache, geometry: &Geometry, ) -> Self::Approximation { - let (curve, half_edge, surface) = self; + let (curve, surface, half_edge) = self; match cache.get(curve, half_edge.boundary) { Some(approx) => approx, @@ -206,7 +206,7 @@ mod tests { let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &half_edge, &surface) + let approx = (&curve, &surface, &half_edge) .approx(tolerance, &core.layers.geometry); assert_eq!(approx.points, vec![]); @@ -229,7 +229,7 @@ mod tests { let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &half_edge, &surface) + let approx = (&curve, &surface, &half_edge) .approx(tolerance, &core.layers.geometry); assert_eq!(approx.points, vec![]); @@ -251,7 +251,7 @@ mod tests { let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &half_edge, &surface) + let approx = (&curve, &surface, &half_edge) .approx(tolerance, &core.layers.geometry); let expected_approx = (global_path, boundary) @@ -282,7 +282,7 @@ mod tests { let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &half_edge, &surface) + let approx = (&curve, &surface, &half_edge) .approx(tolerance, &core.layers.geometry); let expected_approx = (&path, boundary) diff --git a/crates/fj-core/src/algorithms/approx/edge.rs b/crates/fj-core/src/algorithms/approx/edge.rs index bc9ea427b..ad3b47958 100644 --- a/crates/fj-core/src/algorithms/approx/edge.rs +++ b/crates/fj-core/src/algorithms/approx/edge.rs @@ -56,7 +56,7 @@ impl Approx for (&Handle, &Handle) { let rest = { let approx = - (half_edge.curve(), geometry.of_half_edge(half_edge), surface) + (half_edge.curve(), surface, geometry.of_half_edge(half_edge)) .approx_with_cache(tolerance, &mut cache.curve, geometry); approx.points.into_iter().map(|point| { From a6ccd52b81a91a44ad68979e723d6d288abe0aae Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:06:53 +0200 Subject: [PATCH 02/50] Simplify argument for curve approximation --- crates/fj-core/src/algorithms/approx/curve.rs | 23 ++++++++----------- crates/fj-core/src/algorithms/approx/edge.rs | 13 ++++++++--- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index ef07717aa..d873f1474 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -5,17 +5,14 @@ use std::collections::BTreeMap; use fj_math::Point; use crate::{ - geometry::{ - CurveBoundary, Geometry, GlobalPath, HalfEdgeGeom, SurfaceGeom, - SurfacePath, - }, + geometry::{CurveBoundary, Geometry, GlobalPath, SurfaceGeom, SurfacePath}, storage::Handle, topology::{Curve, Surface}, }; use super::{Approx, ApproxPoint, Tolerance}; -impl Approx for (&Handle, &Handle, &HalfEdgeGeom) { +impl Approx for (&Handle, &Handle, CurveBoundary>) { type Approximation = CurveApprox; type Cache = CurveApproxCache; @@ -25,9 +22,9 @@ impl Approx for (&Handle, &Handle, &HalfEdgeGeom) { cache: &mut Self::Cache, geometry: &Geometry, ) -> Self::Approximation { - let (curve, surface, half_edge) = self; + let (curve, surface, boundary) = self; - match cache.get(curve, half_edge.boundary) { + match cache.get(curve, boundary) { Some(approx) => approx, None => { let approx = approx_curve( @@ -38,12 +35,12 @@ impl Approx for (&Handle, &Handle, &HalfEdgeGeom) { .unwrap() .path, geometry.of_surface(surface), - half_edge.boundary, + boundary, tolerance, geometry, ); - cache.insert(curve.clone(), half_edge.boundary, approx) + cache.insert(curve.clone(), boundary, approx) } } } @@ -206,7 +203,7 @@ mod tests { let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &surface, &half_edge) + let approx = (&curve, &surface, half_edge.boundary) .approx(tolerance, &core.layers.geometry); assert_eq!(approx.points, vec![]); @@ -229,7 +226,7 @@ mod tests { let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &surface, &half_edge) + let approx = (&curve, &surface, half_edge.boundary) .approx(tolerance, &core.layers.geometry); assert_eq!(approx.points, vec![]); @@ -251,7 +248,7 @@ mod tests { let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &surface, &half_edge) + let approx = (&curve, &surface, half_edge.boundary) .approx(tolerance, &core.layers.geometry); let expected_approx = (global_path, boundary) @@ -282,7 +279,7 @@ mod tests { let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &surface, &half_edge) + let approx = (&curve, &surface, half_edge.boundary) .approx(tolerance, &core.layers.geometry); let expected_approx = (&path, boundary) diff --git a/crates/fj-core/src/algorithms/approx/edge.rs b/crates/fj-core/src/algorithms/approx/edge.rs index ad3b47958..725e36e44 100644 --- a/crates/fj-core/src/algorithms/approx/edge.rs +++ b/crates/fj-core/src/algorithms/approx/edge.rs @@ -55,9 +55,16 @@ impl Approx for (&Handle, &Handle) { let first = ApproxPoint::new(start_position_surface, start_position); let rest = { - let approx = - (half_edge.curve(), surface, geometry.of_half_edge(half_edge)) - .approx_with_cache(tolerance, &mut cache.curve, geometry); + let approx = ( + half_edge.curve(), + surface, + geometry.of_half_edge(half_edge).boundary, + ) + .approx_with_cache( + tolerance, + &mut cache.curve, + geometry, + ); approx.points.into_iter().map(|point| { let point_surface = geometry From 1e44da2c4906509a0101161e538df2d1d7a99634 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:08:07 +0200 Subject: [PATCH 03/50] Remove redundant variables --- crates/fj-core/src/algorithms/approx/curve.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index d873f1474..697694233 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -184,7 +184,7 @@ mod tests { use crate::{ algorithms::approx::{Approx, ApproxPoint}, - geometry::{CurveBoundary, GlobalPath, HalfEdgeGeom, SurfacePath}, + geometry::{CurveBoundary, GlobalPath, SurfacePath}, operations::build::{BuildCurve, BuildSurface}, topology::{Curve, Surface}, Core, @@ -200,10 +200,9 @@ mod tests { let curve = Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from(boundary); - let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &surface, half_edge.boundary) + let approx = (&curve, &surface, boundary) .approx(tolerance, &core.layers.geometry); assert_eq!(approx.points, vec![]); @@ -223,10 +222,9 @@ mod tests { let curve = Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from(boundary); - let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &surface, half_edge.boundary) + let approx = (&curve, &surface, boundary) .approx(tolerance, &core.layers.geometry); assert_eq!(approx.points, vec![]); @@ -245,10 +243,9 @@ mod tests { let curve = Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from([[0.], [TAU]]); - let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &surface, half_edge.boundary) + let approx = (&curve, &surface, boundary) .approx(tolerance, &core.layers.geometry); let expected_approx = (global_path, boundary) @@ -276,10 +273,9 @@ mod tests { let curve = Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from([[0.], [TAU]]); - let half_edge = HalfEdgeGeom { boundary }; let tolerance = 1.; - let approx = (&curve, &surface, half_edge.boundary) + let approx = (&curve, &surface, boundary) .approx(tolerance, &core.layers.geometry); let expected_approx = (&path, boundary) From 0030b7cc76e69bfc1c44a7cb2a08b4dd3aaf2cc8 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:08:32 +0200 Subject: [PATCH 04/50] Extract variable to simplify --- crates/fj-core/src/algorithms/approx/edge.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/edge.rs b/crates/fj-core/src/algorithms/approx/edge.rs index 725e36e44..a78ba3f36 100644 --- a/crates/fj-core/src/algorithms/approx/edge.rs +++ b/crates/fj-core/src/algorithms/approx/edge.rs @@ -55,16 +55,9 @@ impl Approx for (&Handle, &Handle) { let first = ApproxPoint::new(start_position_surface, start_position); let rest = { - let approx = ( - half_edge.curve(), - surface, - geometry.of_half_edge(half_edge).boundary, - ) - .approx_with_cache( - tolerance, - &mut cache.curve, - geometry, - ); + let boundary = geometry.of_half_edge(half_edge).boundary; + let approx = (half_edge.curve(), surface, boundary) + .approx_with_cache(tolerance, &mut cache.curve, geometry); approx.points.into_iter().map(|point| { let point_surface = geometry From 56216e2255511206f095e3f9418ff93e8c674461 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:20:00 +0200 Subject: [PATCH 05/50] Update module name --- crates/fj-core/src/algorithms/approx/cycle.rs | 2 +- crates/fj-core/src/algorithms/approx/face.rs | 2 +- .../fj-core/src/algorithms/approx/{edge.rs => half_edge.rs} | 0 crates/fj-core/src/algorithms/approx/mod.rs | 2 +- crates/fj-core/src/algorithms/approx/shell.rs | 4 +++- crates/fj-core/src/algorithms/approx/sketch.rs | 4 +++- crates/fj-core/src/algorithms/approx/solid.rs | 4 +++- 7 files changed, 12 insertions(+), 6 deletions(-) rename crates/fj-core/src/algorithms/approx/{edge.rs => half_edge.rs} (100%) diff --git a/crates/fj-core/src/algorithms/approx/cycle.rs b/crates/fj-core/src/algorithms/approx/cycle.rs index 23f356b9f..256487eee 100644 --- a/crates/fj-core/src/algorithms/approx/cycle.rs +++ b/crates/fj-core/src/algorithms/approx/cycle.rs @@ -11,7 +11,7 @@ use crate::{ }; use super::{ - edge::{HalfEdgeApprox, HalfEdgeApproxCache}, + half_edge::{HalfEdgeApprox, HalfEdgeApproxCache}, Approx, ApproxPoint, Tolerance, }; diff --git a/crates/fj-core/src/algorithms/approx/face.rs b/crates/fj-core/src/algorithms/approx/face.rs index 1f939888a..aa0b809fa 100644 --- a/crates/fj-core/src/algorithms/approx/face.rs +++ b/crates/fj-core/src/algorithms/approx/face.rs @@ -12,7 +12,7 @@ use crate::{ }; use super::{ - cycle::CycleApprox, edge::HalfEdgeApproxCache, Approx, ApproxPoint, + cycle::CycleApprox, half_edge::HalfEdgeApproxCache, Approx, ApproxPoint, Tolerance, }; diff --git a/crates/fj-core/src/algorithms/approx/edge.rs b/crates/fj-core/src/algorithms/approx/half_edge.rs similarity index 100% rename from crates/fj-core/src/algorithms/approx/edge.rs rename to crates/fj-core/src/algorithms/approx/half_edge.rs diff --git a/crates/fj-core/src/algorithms/approx/mod.rs b/crates/fj-core/src/algorithms/approx/mod.rs index 46b9b6001..9e9d403a4 100644 --- a/crates/fj-core/src/algorithms/approx/mod.rs +++ b/crates/fj-core/src/algorithms/approx/mod.rs @@ -2,8 +2,8 @@ pub mod curve; pub mod cycle; -pub mod edge; pub mod face; +pub mod half_edge; pub mod path; pub mod shell; pub mod sketch; diff --git a/crates/fj-core/src/algorithms/approx/shell.rs b/crates/fj-core/src/algorithms/approx/shell.rs index ed3fc77ad..2bdde91b6 100644 --- a/crates/fj-core/src/algorithms/approx/shell.rs +++ b/crates/fj-core/src/algorithms/approx/shell.rs @@ -4,7 +4,9 @@ use std::collections::BTreeSet; use crate::{geometry::Geometry, topology::Shell}; -use super::{edge::HalfEdgeApproxCache, face::FaceApprox, Approx, Tolerance}; +use super::{ + face::FaceApprox, half_edge::HalfEdgeApproxCache, Approx, Tolerance, +}; impl Approx for &Shell { type Approximation = BTreeSet; diff --git a/crates/fj-core/src/algorithms/approx/sketch.rs b/crates/fj-core/src/algorithms/approx/sketch.rs index 9c8804df3..192d5ac56 100644 --- a/crates/fj-core/src/algorithms/approx/sketch.rs +++ b/crates/fj-core/src/algorithms/approx/sketch.rs @@ -4,7 +4,9 @@ use std::collections::BTreeSet; use crate::{geometry::Geometry, topology::Sketch}; -use super::{edge::HalfEdgeApproxCache, face::FaceApprox, Approx, Tolerance}; +use super::{ + face::FaceApprox, half_edge::HalfEdgeApproxCache, Approx, Tolerance, +}; impl Approx for &Sketch { type Approximation = BTreeSet; diff --git a/crates/fj-core/src/algorithms/approx/solid.rs b/crates/fj-core/src/algorithms/approx/solid.rs index 8f3cc326c..3592100c2 100644 --- a/crates/fj-core/src/algorithms/approx/solid.rs +++ b/crates/fj-core/src/algorithms/approx/solid.rs @@ -4,7 +4,9 @@ use std::collections::BTreeSet; use crate::{geometry::Geometry, topology::Solid}; -use super::{edge::HalfEdgeApproxCache, face::FaceApprox, Approx, Tolerance}; +use super::{ + face::FaceApprox, half_edge::HalfEdgeApproxCache, Approx, Tolerance, +}; impl Approx for &Solid { type Approximation = BTreeSet; From 0e9e5c83e489e3d53bf5d8e5d4fe05d8051f7ab3 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:21:31 +0200 Subject: [PATCH 06/50] Update documentation of half-edge approximation --- crates/fj-core/src/algorithms/approx/half_edge.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/half_edge.rs b/crates/fj-core/src/algorithms/approx/half_edge.rs index a78ba3f36..b186cda49 100644 --- a/crates/fj-core/src/algorithms/approx/half_edge.rs +++ b/crates/fj-core/src/algorithms/approx/half_edge.rs @@ -1,9 +1,6 @@ -//! Edge approximation +//! Half-edge approximation //! -//! The approximation of a curve is its first vertex, combined with the -//! approximation of its curve. The second vertex is left out, as edge -//! approximations are usually used to build cycle approximations, and this way, -//! the caller doesn't have to deal with duplicate vertices. +//! See [`HalfEdgeApprox`]. use crate::{ geometry::Geometry, @@ -80,6 +77,11 @@ impl Approx for (&Handle, &Handle) { } /// An approximation of a [`HalfEdge`] +/// +/// The approximation of a half-edge is its first vertex, combined with the +/// approximation of its curve. The second vertex is left out, as half-edge +/// approximations are usually used to build cycle approximations, and this way, +/// the caller doesn't have to deal with duplicate vertices. #[derive(Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct HalfEdgeApprox { /// The points that approximate the half-edge From 5f629ca597e814ab93f76a3fedae37e19cf08278 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:27:42 +0200 Subject: [PATCH 07/50] Improve documentation of curve approximation --- crates/fj-core/src/algorithms/approx/curve.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 697694233..c79d46641 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -126,7 +126,11 @@ fn approx_curve( CurveApprox { points } } -/// Approximation of [`Curve`], within a specific boundary +/// Approximation of a [`Curve`], within a specific boundary +/// +/// The approximation of the curve only includes points _within_ the boundary, +/// not those _on_ the boundary. Those boundary points are part of half-edge +/// approximation, which uses and includes curve approximation. #[derive(Clone)] pub struct CurveApprox { /// The points that approximate the curve within the boundary From af971c78019d2c735c7448644baae7aa152e7a26 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:32:26 +0200 Subject: [PATCH 08/50] Construct correct data type right away --- crates/fj-core/src/algorithms/approx/curve.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index c79d46641..2d7ed4fe8 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -88,7 +88,7 @@ fn approx_curve( let point_global = surface.point_from_surface_coords(point_surface); - (point_curve, point_global) + ApproxPoint::new(point_curve, point_global) }) .collect() } @@ -110,19 +110,14 @@ fn approx_curve( let point_surface = path.point_from_path_coords([t]); let point_global = surface.point_from_surface_coords(point_surface); - points.push((u, point_global)); + points.push(ApproxPoint::new(u, point_global)); } points } }; - let points = points - .into_iter() - .map(|(point_curve, point_global)| { - ApproxPoint::new(point_curve, point_global) - }) - .collect(); + let points = points.into_iter().collect(); CurveApprox { points } } From 8bc97b2352bb6448bdb171c045f4c7317a4d4fe1 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:33:24 +0200 Subject: [PATCH 09/50] Remove redundant variable --- crates/fj-core/src/algorithms/approx/curve.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 2d7ed4fe8..a9b0b207b 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -117,7 +117,6 @@ fn approx_curve( } }; - let points = points.into_iter().collect(); CurveApprox { points } } From 633bf5edcc442f17d451dd76d3f4896d1019b39b Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:40:27 +0200 Subject: [PATCH 10/50] Improve documentation for line approximation --- crates/fj-core/src/algorithms/approx/path.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index 827a00ff8..8838d73b5 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -30,7 +30,7 @@ use std::iter; -use fj_math::{Circle, Point, Scalar, Sign}; +use fj_math::{Circle, Line, Point, Scalar, Sign}; use crate::geometry::{CurveBoundary, Geometry, GlobalPath, SurfacePath}; @@ -52,7 +52,7 @@ impl Approx for (&SurfacePath, CurveBoundary>) { SurfacePath::Circle(circle) => { approx_circle(circle, range, tolerance.into()) } - SurfacePath::Line(_) => vec![], + SurfacePath::Line(line) => approx_line(line), } } } @@ -73,7 +73,7 @@ impl Approx for (GlobalPath, CurveBoundary>) { GlobalPath::Circle(circle) => { approx_circle(&circle, range, tolerance.into()) } - GlobalPath::Line(_) => vec![], + GlobalPath::Line(line) => approx_line(&line), } } } @@ -100,6 +100,20 @@ fn approx_circle( points } +/// Approximate a line +/// +/// Since path approximation don't include the end points of the approximation +/// boundary, and a line does not require any other points to be fully defined, +/// this method always returns no points. +/// +/// The method still exists, to make the code that approximates lines easy to +/// find for anyone reading this module, as well as to provide a place to +/// document this fact about line approximations. +fn approx_line(line: &Line) -> Vec<(Point<1>, Point)> { + let _ = line; + Vec::new() +} + struct PathApproxParams { increment: Scalar, } From ad5ec4d562f726138978584eda595341d77e886a Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:18:22 +0200 Subject: [PATCH 11/50] Improve documentation of line approximation --- crates/fj-core/src/algorithms/approx/path.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index 8838d73b5..2be49f01f 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -102,13 +102,12 @@ fn approx_circle( /// Approximate a line /// -/// Since path approximation don't include the end points of the approximation -/// boundary, and a line does not require any other points to be fully defined, -/// this method always returns no points. +/// Since curve approximations don't include the approximation boundary itself, +/// and a line does not require any other points to be fully defined, this +/// method always returns no points. /// -/// The method still exists, to make the code that approximates lines easy to -/// find for anyone reading this module, as well as to provide a place to -/// document this fact about line approximations. +/// The method still exists, to make the code that approximates lines, and thus +/// this piece of documentation, easy to find for anyone who's looking. fn approx_line(line: &Line) -> Vec<(Point<1>, Point)> { let _ = line; Vec::new() From a23fe37e4b31b7fa55e50ca24c2844ea1f794097 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:44:17 +0200 Subject: [PATCH 12/50] Prepare for follow-on change --- crates/fj-core/src/algorithms/approx/path.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index 2be49f01f..3c3e2b379 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -82,7 +82,7 @@ impl Approx for (GlobalPath, CurveBoundary>) { /// /// `tolerance` specifies how much the approximation is allowed to deviate /// from the circle. -fn approx_circle( +pub fn approx_circle( circle: &Circle, boundary: impl Into>>, tolerance: Tolerance, @@ -108,7 +108,9 @@ fn approx_circle( /// /// The method still exists, to make the code that approximates lines, and thus /// this piece of documentation, easy to find for anyone who's looking. -fn approx_line(line: &Line) -> Vec<(Point<1>, Point)> { +pub fn approx_line( + line: &Line, +) -> Vec<(Point<1>, Point)> { let _ = line; Vec::new() } From d6c17fe3f423d8b39d84601c3ede8427c1d94e1f Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:46:38 +0200 Subject: [PATCH 13/50] Make `approx_circle` more convenient to call --- crates/fj-core/src/algorithms/approx/path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index 3c3e2b379..1cfb3f37a 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -85,7 +85,7 @@ impl Approx for (GlobalPath, CurveBoundary>) { pub fn approx_circle( circle: &Circle, boundary: impl Into>>, - tolerance: Tolerance, + tolerance: impl Into, ) -> Vec<(Point<1>, Point)> { let boundary = boundary.into(); From fdb92762ab5a8042286fe9c01153da583b10a69d Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:49:05 +0200 Subject: [PATCH 14/50] Circumvent redundant code in circle approximation --- crates/fj-core/src/algorithms/approx/curve.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index a9b0b207b..564ecf70a 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -10,7 +10,7 @@ use crate::{ topology::{Curve, Surface}, }; -use super::{Approx, ApproxPoint, Tolerance}; +use super::{path::approx_circle, Approx, ApproxPoint, Tolerance}; impl Approx for (&Handle, &Handle, CurveBoundary>) { type Approximation = CurveApprox; @@ -65,9 +65,8 @@ fn approx_curve( "Approximating a circle on a curved surface not supported yet." ) } - (SurfacePath::Circle(_), GlobalPath::Line(_)) => { - (path, boundary) - .approx_with_cache(tolerance, &mut (), geometry) + (SurfacePath::Circle(circle), GlobalPath::Line(_)) => { + approx_circle(circle, boundary, tolerance) .into_iter() .map(|(point_curve, point_surface)| { // We're throwing away `point_surface` here, which is a From 50dd44201ec6450a044568f91bc537c27a943058 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:57:21 +0200 Subject: [PATCH 15/50] Prepare for follow-on change --- crates/fj-core/src/algorithms/approx/curve.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 564ecf70a..dc778b36d 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -177,6 +177,7 @@ impl CurveApproxCache { mod tests { use std::f64::consts::TAU; + use fj_math::Circle; use pretty_assertions::assert_eq; use crate::{ @@ -266,7 +267,8 @@ mod tests { let mut core = Core::new(); let surface = core.layers.topology.surfaces.xz_plane(); - let path = SurfacePath::circle_from_center_and_radius([0., 0.], 1.); + let circle = Circle::from_center_and_radius([0., 0.], 1.); + let path = SurfacePath::Circle(circle); let curve = Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from([[0.], [TAU]]); From e5a4070b6cad28dde02a54cc757487b2609cdd69 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:52:30 +0200 Subject: [PATCH 16/50] Circumvent redundant abstraction layer in test --- crates/fj-core/src/algorithms/approx/curve.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index dc778b36d..13a592682 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -181,7 +181,7 @@ mod tests { use pretty_assertions::assert_eq; use crate::{ - algorithms::approx::{Approx, ApproxPoint}, + algorithms::approx::{path::approx_circle, Approx, ApproxPoint}, geometry::{CurveBoundary, GlobalPath, SurfacePath}, operations::build::{BuildCurve, BuildSurface}, topology::{Curve, Surface}, @@ -277,8 +277,7 @@ mod tests { let approx = (&curve, &surface, boundary) .approx(tolerance, &core.layers.geometry); - let expected_approx = (&path, boundary) - .approx(tolerance, &core.layers.geometry) + let expected_approx = approx_circle(&circle, boundary, tolerance) .into_iter() .map(|(point_local, _)| { let point_surface = path.point_from_path_coords(point_local); From c17d5ef9f8ff807b808d99300059269bb244c384 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:53:04 +0200 Subject: [PATCH 17/50] Remove unused code --- crates/fj-core/src/algorithms/approx/path.rs | 23 +------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index 1cfb3f37a..61f423c98 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -32,31 +32,10 @@ use std::iter; use fj_math::{Circle, Line, Point, Scalar, Sign}; -use crate::geometry::{CurveBoundary, Geometry, GlobalPath, SurfacePath}; +use crate::geometry::{CurveBoundary, Geometry, GlobalPath}; use super::{Approx, Tolerance}; -impl Approx for (&SurfacePath, CurveBoundary>) { - type Approximation = Vec<(Point<1>, Point<2>)>; - type Cache = (); - - fn approx_with_cache( - self, - tolerance: impl Into, - (): &mut Self::Cache, - _: &Geometry, - ) -> Self::Approximation { - let (path, range) = self; - - match path { - SurfacePath::Circle(circle) => { - approx_circle(circle, range, tolerance.into()) - } - SurfacePath::Line(line) => approx_line(line), - } - } -} - impl Approx for (GlobalPath, CurveBoundary>) { type Approximation = Vec<(Point<1>, Point<3>)>; type Cache = (); From 714bc35f4df469e62bd3371cc4a33b30dafb998e Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:58:53 +0200 Subject: [PATCH 18/50] Prepare for follow-on change --- crates/fj-core/src/algorithms/approx/curve.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 13a592682..f743b6d0b 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -177,7 +177,7 @@ impl CurveApproxCache { mod tests { use std::f64::consts::TAU; - use fj_math::Circle; + use fj_math::{Circle, Point}; use pretty_assertions::assert_eq; use crate::{ @@ -232,7 +232,8 @@ mod tests { fn approx_line_on_curved_surface_along_curve() { let mut core = Core::new(); - let global_path = GlobalPath::circle_from_radius(1.); + let circle = Circle::from_center_and_radius(Point::origin(), 1.); + let global_path = GlobalPath::Circle(circle); let surface = Surface::from_uv(global_path, [0., 0., 1.], &mut core); let path = SurfacePath::line_from_points_with_coords([ ([0.], [0., 1.]), From fc8db4353f3ab2b366bb6af45e1f48b6f1c09b66 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 19:59:51 +0200 Subject: [PATCH 19/50] Circumvent redundant abstraction layer --- crates/fj-core/src/algorithms/approx/curve.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index f743b6d0b..05f2f4cb6 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -247,8 +247,7 @@ mod tests { let approx = (&curve, &surface, boundary) .approx(tolerance, &core.layers.geometry); - let expected_approx = (global_path, boundary) - .approx(tolerance, &core.layers.geometry) + let expected_approx = approx_circle(&circle, boundary, tolerance) .into_iter() .map(|(point_local, _)| { let point_surface = path.point_from_path_coords(point_local); From e0262026c57ac13b3b552a7c4dd9c7925a50ada1 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:02:40 +0200 Subject: [PATCH 20/50] Circumvent redundant abstraction layer --- crates/fj-core/src/algorithms/approx/curve.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 05f2f4cb6..14c3bd402 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -10,7 +10,10 @@ use crate::{ topology::{Curve, Surface}, }; -use super::{path::approx_circle, Approx, ApproxPoint, Tolerance}; +use super::{ + path::{approx_circle, approx_line}, + Approx, ApproxPoint, Tolerance, +}; impl Approx for (&Handle, &Handle, CurveBoundary>) { type Approximation = CurveApprox; @@ -51,7 +54,7 @@ fn approx_curve( surface: &SurfaceGeom, boundary: CurveBoundary>, tolerance: impl Into, - geometry: &Geometry, + _: &Geometry, ) -> CurveApprox { // There are different cases of varying complexity. Circles are the hard // part here, as they need to be approximated, while lines don't need to be. @@ -97,11 +100,12 @@ fn approx_curve( [path.point_from_path_coords(point_curve).u] })); - let approx_u = (surface.u, range_u).approx_with_cache( - tolerance, - &mut (), - geometry, - ); + let approx_u = match surface.u { + GlobalPath::Circle(circle) => { + approx_circle(&circle, range_u, tolerance) + } + GlobalPath::Line(line) => approx_line(&line), + }; let mut points = Vec::new(); for (u, _) in approx_u { From ba2a0f425f1c812f7f99499aae35f88829fecada Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:04:09 +0200 Subject: [PATCH 21/50] Remove unused argument --- crates/fj-core/src/algorithms/approx/curve.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 14c3bd402..c17c4c21c 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -40,7 +40,6 @@ impl Approx for (&Handle, &Handle, CurveBoundary>) { geometry.of_surface(surface), boundary, tolerance, - geometry, ); cache.insert(curve.clone(), boundary, approx) @@ -54,7 +53,6 @@ fn approx_curve( surface: &SurfaceGeom, boundary: CurveBoundary>, tolerance: impl Into, - _: &Geometry, ) -> CurveApprox { // There are different cases of varying complexity. Circles are the hard // part here, as they need to be approximated, while lines don't need to be. From 7a10405b90f37a545473e1e11a7b4873909de249 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:04:29 +0200 Subject: [PATCH 22/50] Remove unused code --- crates/fj-core/src/algorithms/approx/path.rs | 25 ++------------------ 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index 61f423c98..b5fe20293 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -32,30 +32,9 @@ use std::iter; use fj_math::{Circle, Line, Point, Scalar, Sign}; -use crate::geometry::{CurveBoundary, Geometry, GlobalPath}; +use crate::geometry::CurveBoundary; -use super::{Approx, Tolerance}; - -impl Approx for (GlobalPath, CurveBoundary>) { - type Approximation = Vec<(Point<1>, Point<3>)>; - type Cache = (); - - fn approx_with_cache( - self, - tolerance: impl Into, - (): &mut Self::Cache, - _: &Geometry, - ) -> Self::Approximation { - let (path, range) = self; - - match path { - GlobalPath::Circle(circle) => { - approx_circle(&circle, range, tolerance.into()) - } - GlobalPath::Line(line) => approx_line(&line), - } - } -} +use super::Tolerance; /// Approximate a circle /// From 8e7d311ef92269a91b104e056fefb3c8f4d75594 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:09:01 +0200 Subject: [PATCH 23/50] Consolidate circle approx in dedicated module --- .../fj-core/src/algorithms/approx/circle.rs | 174 +++++++++++++++++ crates/fj-core/src/algorithms/approx/curve.rs | 5 +- crates/fj-core/src/algorithms/approx/mod.rs | 2 + crates/fj-core/src/algorithms/approx/path.rs | 175 +----------------- 4 files changed, 179 insertions(+), 177 deletions(-) create mode 100644 crates/fj-core/src/algorithms/approx/circle.rs diff --git a/crates/fj-core/src/algorithms/approx/circle.rs b/crates/fj-core/src/algorithms/approx/circle.rs new file mode 100644 index 000000000..0a4403f95 --- /dev/null +++ b/crates/fj-core/src/algorithms/approx/circle.rs @@ -0,0 +1,174 @@ +use std::iter; + +use fj_math::{Circle, Point, Scalar, Sign}; + +use crate::geometry::CurveBoundary; + +use super::Tolerance; + +/// Approximate a circle +/// +/// `tolerance` specifies how much the approximation is allowed to deviate +/// from the circle. +pub fn approx_circle( + circle: &Circle, + boundary: impl Into>>, + tolerance: impl Into, +) -> Vec<(Point<1>, Point)> { + let boundary = boundary.into(); + + let params = PathApproxParams::for_circle(circle, tolerance); + let mut points = Vec::new(); + + for point_curve in params.points(boundary) { + let point_global = circle.point_from_circle_coords(point_curve); + points.push((point_curve, point_global)); + } + + points +} + +struct PathApproxParams { + increment: Scalar, +} + +impl PathApproxParams { + pub fn for_circle( + circle: &Circle, + tolerance: impl Into, + ) -> Self { + let radius = circle.a().magnitude(); + + let num_vertices_to_approx_full_circle = Scalar::max( + Scalar::PI + / (Scalar::ONE - (tolerance.into().inner() / radius)).acos(), + 3., + ) + .ceil(); + + let increment = Scalar::TAU / num_vertices_to_approx_full_circle; + + Self { increment } + } + + pub fn increment(&self) -> Scalar { + self.increment + } + + pub fn points( + &self, + boundary: impl Into>>, + ) -> impl Iterator> + '_ { + let boundary = boundary.into(); + + let [a, b] = boundary.inner.map(|point| point.t / self.increment()); + let direction = (b - a).sign(); + let [min, max] = if a < b { [a, b] } else { [b, a] }; + + // We can't generate a point exactly at the boundaries of the range as + // part of the approximation. Make sure we stay inside the range. + let min = min.floor() + 1.; + let max = max.ceil() - 1.; + + let [start, end] = match direction { + Sign::Negative => [max, min], + Sign::Positive | Sign::Zero => [min, max], + }; + + let mut i = start; + iter::from_fn(move || { + let is_finished = match direction { + Sign::Negative => i < end, + Sign::Positive | Sign::Zero => i > end, + }; + + if is_finished { + return None; + } + + let t = self.increment() * i; + i += direction.to_scalar(); + + Some(Point::from([t])) + }) + } +} + +#[cfg(test)] +mod tests { + use std::f64::consts::TAU; + + use fj_math::{Circle, Point, Scalar}; + + use crate::{algorithms::approx::Tolerance, geometry::CurveBoundary}; + + use super::PathApproxParams; + + #[test] + fn increment_for_circle() { + test_increment(1., 0.5, 3.); + test_increment(1., 0.1, 7.); + test_increment(1., 0.01, 23.); + + fn test_increment( + radius: impl Into, + tolerance: impl Into, + expected_num_vertices: impl Into, + ) { + let circle = Circle::from_center_and_radius([0., 0.], radius); + let params = PathApproxParams::for_circle(&circle, tolerance); + + let expected_increment = Scalar::TAU / expected_num_vertices; + assert_eq!(params.increment(), expected_increment); + } + } + + #[test] + fn points_for_circle() { + // At the chosen values for radius and tolerance (see below), the + // increment is `PI / 4`, so ~1.57. + + // Empty range + let empty: [Scalar; 0] = []; + test_path([[0.], [0.]], empty); + + // Ranges contain all generated points. Start is before the first + // increment and after the last one in each case. + test_path([[0.], [TAU]], [1., 2., 3.]); + test_path([[1.], [TAU]], [1., 2., 3.]); + test_path([[0.], [TAU - 1.]], [1., 2., 3.]); + + // Here the range is restricted to cut of the first or last increment. + test_path([[2.], [TAU]], [2., 3.]); + test_path([[0.], [TAU - 2.]], [1., 2.]); + + // And everything again, but in reverse. + test_path([[TAU], [0.]], [3., 2., 1.]); + test_path([[TAU], [1.]], [3., 2., 1.]); + test_path([[TAU - 1.], [0.]], [3., 2., 1.]); + test_path([[TAU], [2.]], [3., 2.]); + test_path([[TAU - 2.], [0.]], [2., 1.]); + + fn test_path( + boundary: impl Into>>, + expected_coords: impl IntoIterator>, + ) { + // Choose radius and tolerance such, that we need 4 vertices to + // approximate a full circle. This is the lowest number that we can + // still cover all the edge cases with + let radius = 1.; + let tolerance = 0.375; + + let circle = Circle::from_center_and_radius([0., 0.], radius); + let params = PathApproxParams::for_circle(&circle, tolerance); + + let points = params.points(boundary).collect::>(); + + let expected_points = expected_coords + .into_iter() + .map(|i| Point::from([params.increment() * i])) + .collect::>(); + assert_eq!(points, expected_points); + } + } +} diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index c17c4c21c..b705b21f7 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -11,8 +11,7 @@ use crate::{ }; use super::{ - path::{approx_circle, approx_line}, - Approx, ApproxPoint, Tolerance, + circle::approx_circle, path::approx_line, Approx, ApproxPoint, Tolerance, }; impl Approx for (&Handle, &Handle, CurveBoundary>) { @@ -183,7 +182,7 @@ mod tests { use pretty_assertions::assert_eq; use crate::{ - algorithms::approx::{path::approx_circle, Approx, ApproxPoint}, + algorithms::approx::{circle::approx_circle, Approx, ApproxPoint}, geometry::{CurveBoundary, GlobalPath, SurfacePath}, operations::build::{BuildCurve, BuildSurface}, topology::{Curve, Surface}, diff --git a/crates/fj-core/src/algorithms/approx/mod.rs b/crates/fj-core/src/algorithms/approx/mod.rs index 9e9d403a4..ea2ad43e1 100644 --- a/crates/fj-core/src/algorithms/approx/mod.rs +++ b/crates/fj-core/src/algorithms/approx/mod.rs @@ -11,6 +11,8 @@ pub mod solid; pub mod tolerance; pub mod vertex; +mod circle; + use std::{ cmp::Ordering, fmt::Debug, diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index b5fe20293..a47b69063 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -28,35 +28,7 @@ //! fit together in a valid mesh, no matter which ranges of a path are being //! approximated, and how many times. -use std::iter; - -use fj_math::{Circle, Line, Point, Scalar, Sign}; - -use crate::geometry::CurveBoundary; - -use super::Tolerance; - -/// Approximate a circle -/// -/// `tolerance` specifies how much the approximation is allowed to deviate -/// from the circle. -pub fn approx_circle( - circle: &Circle, - boundary: impl Into>>, - tolerance: impl Into, -) -> Vec<(Point<1>, Point)> { - let boundary = boundary.into(); - - let params = PathApproxParams::for_circle(circle, tolerance); - let mut points = Vec::new(); - - for point_curve in params.points(boundary) { - let point_global = circle.point_from_circle_coords(point_curve); - points.push((point_curve, point_global)); - } - - points -} +use fj_math::{Line, Point}; /// Approximate a line /// @@ -72,148 +44,3 @@ pub fn approx_line( let _ = line; Vec::new() } - -struct PathApproxParams { - increment: Scalar, -} - -impl PathApproxParams { - pub fn for_circle( - circle: &Circle, - tolerance: impl Into, - ) -> Self { - let radius = circle.a().magnitude(); - - let num_vertices_to_approx_full_circle = Scalar::max( - Scalar::PI - / (Scalar::ONE - (tolerance.into().inner() / radius)).acos(), - 3., - ) - .ceil(); - - let increment = Scalar::TAU / num_vertices_to_approx_full_circle; - - Self { increment } - } - - pub fn increment(&self) -> Scalar { - self.increment - } - - pub fn points( - &self, - boundary: impl Into>>, - ) -> impl Iterator> + '_ { - let boundary = boundary.into(); - - let [a, b] = boundary.inner.map(|point| point.t / self.increment()); - let direction = (b - a).sign(); - let [min, max] = if a < b { [a, b] } else { [b, a] }; - - // We can't generate a point exactly at the boundaries of the range as - // part of the approximation. Make sure we stay inside the range. - let min = min.floor() + 1.; - let max = max.ceil() - 1.; - - let [start, end] = match direction { - Sign::Negative => [max, min], - Sign::Positive | Sign::Zero => [min, max], - }; - - let mut i = start; - iter::from_fn(move || { - let is_finished = match direction { - Sign::Negative => i < end, - Sign::Positive | Sign::Zero => i > end, - }; - - if is_finished { - return None; - } - - let t = self.increment() * i; - i += direction.to_scalar(); - - Some(Point::from([t])) - }) - } -} - -#[cfg(test)] -mod tests { - use std::f64::consts::TAU; - - use fj_math::{Circle, Point, Scalar}; - - use crate::algorithms::approx::{path::CurveBoundary, Tolerance}; - - use super::PathApproxParams; - - #[test] - fn increment_for_circle() { - test_increment(1., 0.5, 3.); - test_increment(1., 0.1, 7.); - test_increment(1., 0.01, 23.); - - fn test_increment( - radius: impl Into, - tolerance: impl Into, - expected_num_vertices: impl Into, - ) { - let circle = Circle::from_center_and_radius([0., 0.], radius); - let params = PathApproxParams::for_circle(&circle, tolerance); - - let expected_increment = Scalar::TAU / expected_num_vertices; - assert_eq!(params.increment(), expected_increment); - } - } - - #[test] - fn points_for_circle() { - // At the chosen values for radius and tolerance (see below), the - // increment is `PI / 4`, so ~1.57. - - // Empty range - let empty: [Scalar; 0] = []; - test_path([[0.], [0.]], empty); - - // Ranges contain all generated points. Start is before the first - // increment and after the last one in each case. - test_path([[0.], [TAU]], [1., 2., 3.]); - test_path([[1.], [TAU]], [1., 2., 3.]); - test_path([[0.], [TAU - 1.]], [1., 2., 3.]); - - // Here the range is restricted to cut of the first or last increment. - test_path([[2.], [TAU]], [2., 3.]); - test_path([[0.], [TAU - 2.]], [1., 2.]); - - // And everything again, but in reverse. - test_path([[TAU], [0.]], [3., 2., 1.]); - test_path([[TAU], [1.]], [3., 2., 1.]); - test_path([[TAU - 1.], [0.]], [3., 2., 1.]); - test_path([[TAU], [2.]], [3., 2.]); - test_path([[TAU - 2.], [0.]], [2., 1.]); - - fn test_path( - boundary: impl Into>>, - expected_coords: impl IntoIterator>, - ) { - // Choose radius and tolerance such, that we need 4 vertices to - // approximate a full circle. This is the lowest number that we can - // still cover all the edge cases with - let radius = 1.; - let tolerance = 0.375; - - let circle = Circle::from_center_and_radius([0., 0.], radius); - let params = PathApproxParams::for_circle(&circle, tolerance); - - let points = params.points(boundary).collect::>(); - - let expected_points = expected_coords - .into_iter() - .map(|i| Point::from([params.increment() * i])) - .collect::>(); - assert_eq!(points, expected_points); - } - } -} From 2b7a8ebc81cfa652402361ba4b17c0ac2074f265 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:15:32 +0200 Subject: [PATCH 24/50] Consolidate documentation on circle approx --- .../fj-core/src/algorithms/approx/circle.rs | 37 +++++++++++++++++-- crates/fj-core/src/algorithms/approx/path.rs | 28 -------------- 2 files changed, 34 insertions(+), 31 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/circle.rs b/crates/fj-core/src/algorithms/approx/circle.rs index 0a4403f95..e1cb7b93d 100644 --- a/crates/fj-core/src/algorithms/approx/circle.rs +++ b/crates/fj-core/src/algorithms/approx/circle.rs @@ -6,10 +6,41 @@ use crate::geometry::CurveBoundary; use super::Tolerance; -/// Approximate a circle +/// # Approximate a circle /// -/// `tolerance` specifies how much the approximation is allowed to deviate -/// from the circle. +/// ## Arguments +/// +/// Besides a circle, this method takes two arguments: +/// +/// - The `boundary` within which the circle should be approximated. +/// - The `tolerance` that specifies how much the approximation is allowed to +/// deviate from the actual circle. +/// +/// ## Return Value +/// +/// The approximation returns points within the provided boundary. The boundary +/// points themselves are not included in the approximation. This gives the +/// caller (who knows the boundary anyway) more options for how to further +/// process the approximation. +/// +/// ## Determinism +/// +/// Circle approximation is carefully designed to produce a deterministic result +/// for the combination of a given circle and tolerance, regardless of the +/// boundary. This is done to prevent invalid meshes from being generated. +/// +/// In specific terms, this means there is an infinite set of points that +/// approximates a circle (infinite, since the circle's local coordinate space +/// is infinite). That set is deterministic for a given combination of circle +/// and tolerance. The boundary that defines where the circle is approximated +/// only influences the result in two ways: +/// +/// 1. It controls which points from the infinite set are actually computed. +/// 2. It defines the order in which the computed points are returned. +/// +/// As a result, circle approximation is guaranteed to generate points that can +/// fit together in a valid mesh, no matter which ranges of a path are being +/// approximated, and how many times. pub fn approx_circle( circle: &Circle, boundary: impl Into>>, diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index a47b69063..0b484b8c2 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -1,32 +1,4 @@ //! # Path approximation -//! -//! Since paths are infinite (even circles have an infinite coordinate space, -//! even though they connect to themselves in global coordinates), a range must -//! be provided to approximate them. The approximation then returns points -//! within that range. -//! -//! The boundaries of the range are not included in the approximation. This is -//! done, to give the caller (who knows the boundary anyway) more options on how -//! to further process the approximation. -//! -//! ## Determinism -//! -//! Path approximation is carefully designed to produce a deterministic result -//! for the combination of a given path and a given tolerance, regardless of -//! what the range is. This is done to prevent invalid meshes from being -//! generated. -//! -//! In specific terms, this means there is an infinite set of points that -//! approximates a path, and that set is deterministic for a given combination -//! of path and tolerance. The range that defines where the path is approximated -//! only influences the result in two ways: -//! -//! 1. It controls which points from the infinite set are actually computed. -//! 2. It defines the order in which the computed points are returned. -//! -//! As a result, path approximation is guaranteed to generate points that can -//! fit together in a valid mesh, no matter which ranges of a path are being -//! approximated, and how many times. use fj_math::{Line, Point}; From adf114abf14163ab6934e74f942cbffccf0cc38c Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:19:29 +0200 Subject: [PATCH 25/50] Remove unnecessary `pub` --- crates/fj-core/src/algorithms/approx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fj-core/src/algorithms/approx/mod.rs b/crates/fj-core/src/algorithms/approx/mod.rs index ea2ad43e1..e070b0fa3 100644 --- a/crates/fj-core/src/algorithms/approx/mod.rs +++ b/crates/fj-core/src/algorithms/approx/mod.rs @@ -4,7 +4,6 @@ pub mod curve; pub mod cycle; pub mod face; pub mod half_edge; -pub mod path; pub mod shell; pub mod sketch; pub mod solid; @@ -12,6 +11,7 @@ pub mod tolerance; pub mod vertex; mod circle; +mod path; use std::{ cmp::Ordering, From 7d48d20a57a695509c9d915e956bea6164e03d76 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:19:41 +0200 Subject: [PATCH 26/50] Remove outdated and redundant doc comment --- crates/fj-core/src/algorithms/approx/path.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/path.rs index 0b484b8c2..e0d2e1a68 100644 --- a/crates/fj-core/src/algorithms/approx/path.rs +++ b/crates/fj-core/src/algorithms/approx/path.rs @@ -1,5 +1,3 @@ -//! # Path approximation - use fj_math::{Line, Point}; /// Approximate a line From a6612aa55e8d0a1bc7a15762d0e821873ff441ee Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:19:59 +0200 Subject: [PATCH 27/50] Update module name --- crates/fj-core/src/algorithms/approx/curve.rs | 2 +- crates/fj-core/src/algorithms/approx/{path.rs => line.rs} | 0 crates/fj-core/src/algorithms/approx/mod.rs | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename crates/fj-core/src/algorithms/approx/{path.rs => line.rs} (100%) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index b705b21f7..708ae09c2 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -11,7 +11,7 @@ use crate::{ }; use super::{ - circle::approx_circle, path::approx_line, Approx, ApproxPoint, Tolerance, + circle::approx_circle, line::approx_line, Approx, ApproxPoint, Tolerance, }; impl Approx for (&Handle, &Handle, CurveBoundary>) { diff --git a/crates/fj-core/src/algorithms/approx/path.rs b/crates/fj-core/src/algorithms/approx/line.rs similarity index 100% rename from crates/fj-core/src/algorithms/approx/path.rs rename to crates/fj-core/src/algorithms/approx/line.rs diff --git a/crates/fj-core/src/algorithms/approx/mod.rs b/crates/fj-core/src/algorithms/approx/mod.rs index e070b0fa3..8607b025c 100644 --- a/crates/fj-core/src/algorithms/approx/mod.rs +++ b/crates/fj-core/src/algorithms/approx/mod.rs @@ -11,7 +11,7 @@ pub mod tolerance; pub mod vertex; mod circle; -mod path; +mod line; use std::{ cmp::Ordering, From 788aab543e54f746f6cef6aef24e97d24b140e4e Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:24:21 +0200 Subject: [PATCH 28/50] Circumvent redundant abstraction layer --- crates/fj-core/src/algorithms/approx/curve.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 708ae09c2..5fcab11ad 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -94,7 +94,7 @@ fn approx_curve( (SurfacePath::Line(line), _) => { let range_u = CurveBoundary::from(boundary.inner.map(|point_curve| { - [path.point_from_path_coords(point_curve).u] + [line.point_from_line_coords(point_curve).u] })); let approx_u = match surface.u { @@ -107,7 +107,7 @@ fn approx_curve( let mut points = Vec::new(); for (u, _) in approx_u { let t = (u.t - line.origin().u) / line.direction().u; - let point_surface = path.point_from_path_coords([t]); + let point_surface = line.point_from_line_coords([t]); let point_global = surface.point_from_surface_coords(point_surface); points.push(ApproxPoint::new(u, point_global)); From 8c4ecf3218eeff8a1c257bf86b7a8793896e4bf3 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:26:26 +0200 Subject: [PATCH 29/50] Extract code into dedicated function --- crates/fj-core/src/algorithms/approx/curve.rs | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 5fcab11ad..6c321450e 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; -use fj_math::Point; +use fj_math::{Line, Point}; use crate::{ geometry::{CurveBoundary, Geometry, GlobalPath, SurfaceGeom, SurfacePath}, @@ -92,34 +92,43 @@ fn approx_curve( .collect() } (SurfacePath::Line(line), _) => { - let range_u = - CurveBoundary::from(boundary.inner.map(|point_curve| { - [line.point_from_line_coords(point_curve).u] - })); - - let approx_u = match surface.u { - GlobalPath::Circle(circle) => { - approx_circle(&circle, range_u, tolerance) - } - GlobalPath::Line(line) => approx_line(&line), - }; - - let mut points = Vec::new(); - 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); - points.push(ApproxPoint::new(u, point_global)); - } - - points + approx_line_on_any_surface(line, boundary, surface, tolerance) } }; CurveApprox { points } } +fn approx_line_on_any_surface( + line: &Line<2>, + boundary: CurveBoundary>, + surface: &SurfaceGeom, + tolerance: impl Into, +) -> Vec> { + let range_u = CurveBoundary::from( + boundary + .inner + .map(|point_curve| [line.point_from_line_coords(point_curve).u]), + ); + + let approx_u = match surface.u { + GlobalPath::Circle(circle) => { + approx_circle(&circle, range_u, tolerance) + } + GlobalPath::Line(line) => approx_line(&line), + }; + + let mut points = Vec::new(); + 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); + points.push(ApproxPoint::new(u, point_global)); + } + + points +} + /// Approximation of a [`Curve`], within a specific boundary /// /// The approximation of the curve only includes points _within_ the boundary, From 9391ede47d621e3db73453bb87fb870082035ad8 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:28:25 +0200 Subject: [PATCH 30/50] Extract code into dedicated function --- crates/fj-core/src/algorithms/approx/curve.rs | 58 +++++++++++-------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 6c321450e..1c6fa2878 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -2,7 +2,7 @@ use std::collections::BTreeMap; -use fj_math::{Line, Point}; +use fj_math::{Circle, Line, Point}; use crate::{ geometry::{CurveBoundary, Geometry, GlobalPath, SurfaceGeom, SurfacePath}, @@ -66,30 +66,9 @@ fn approx_curve( ) } (SurfacePath::Circle(circle), GlobalPath::Line(_)) => { - approx_circle(circle, boundary, tolerance) - .into_iter() - .map(|(point_curve, point_surface)| { - // We're throwing away `point_surface` here, which is a - // bit weird, as we're recomputing it later (outside of - // this function). - // - // It should be fine though: - // - // 1. We're throwing this version away, so there's no - // danger of inconsistency between this and the later - // version. - // 2. This version should have been computed using the - // same path and parameters and the later version - // will be, so they should be the same anyway. - // 3. Not all other cases handled in this function have - // a 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); - ApproxPoint::new(point_curve, point_global) - }) - .collect() + approx_circle_on_straight_surface( + circle, boundary, surface, tolerance, + ) } (SurfacePath::Line(line), _) => { approx_line_on_any_surface(line, boundary, surface, tolerance) @@ -99,6 +78,35 @@ fn approx_curve( CurveApprox { points } } +fn approx_circle_on_straight_surface( + circle: &Circle<2>, + boundary: CurveBoundary>, + surface: &SurfaceGeom, + tolerance: impl Into, +) -> Vec> { + approx_circle(circle, boundary, tolerance) + .into_iter() + .map(|(point_curve, point_surface)| { + // We're throwing away `point_surface` here, which is a bit weird, + // as we're recomputing it later (outside of this function). + // + // It should be fine though: + // + // 1. We're throwing this version away, so there's no danger of + // inconsistency between this and the later version. + // 2. This version should have been computed using the same path and + // parameters and the later version will be, so they should be + // the same anyway. + // 3. Not all other cases handled in this function have a 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); + ApproxPoint::new(point_curve, point_global) + }) + .collect() +} + fn approx_line_on_any_surface( line: &Line<2>, boundary: CurveBoundary>, From c2b87ca1ca4a37a38a0a9edea278d605166b1784 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:30:13 +0200 Subject: [PATCH 31/50] Extract code into dedicated function --- crates/fj-core/src/algorithms/approx/curve.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 1c6fa2878..22889bd9b 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -61,9 +61,7 @@ fn approx_curve( // in a more abstract way. let points = match (path, surface.u) { (SurfacePath::Circle(_), GlobalPath::Circle(_)) => { - todo!( - "Approximating a circle on a curved surface not supported yet." - ) + approx_circle_on_curved_surface() } (SurfacePath::Circle(circle), GlobalPath::Line(_)) => { approx_circle_on_straight_surface( @@ -78,6 +76,10 @@ fn approx_curve( CurveApprox { points } } +fn approx_circle_on_curved_surface() -> Vec> { + todo!("Approximating a circle on a curved surface not supported yet.") +} + fn approx_circle_on_straight_surface( circle: &Circle<2>, boundary: CurveBoundary>, From 6a6448070b3b32557131f5c25ac87907f19906da Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:30:35 +0200 Subject: [PATCH 32/50] Add missing word to panic message --- crates/fj-core/src/algorithms/approx/curve.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 22889bd9b..9b3882b5e 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -77,7 +77,7 @@ fn approx_curve( } fn approx_circle_on_curved_surface() -> Vec> { - todo!("Approximating a circle on a curved surface not supported yet.") + todo!("Approximating a circle on a curved surface is not supported yet.") } fn approx_circle_on_straight_surface( From 77183637a3c98173ea49b7820a60d1242cdb9035 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:30:51 +0200 Subject: [PATCH 33/50] Remove redundant comment --- crates/fj-core/src/algorithms/approx/curve.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 9b3882b5e..d31324985 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -53,12 +53,6 @@ fn approx_curve( boundary: CurveBoundary>, tolerance: impl Into, ) -> CurveApprox { - // There are different cases of varying complexity. Circles are the hard - // part here, as they need to be approximated, while lines don't need to be. - // - // This will probably all be unified eventually, as `SurfacePath` and - // `GlobalPath` grow APIs that are better suited to implementing this code - // in a more abstract way. let points = match (path, surface.u) { (SurfacePath::Circle(_), GlobalPath::Circle(_)) => { approx_circle_on_curved_surface() From 93be46487233bc0b0df9c00e1f2b8056cd5a54a8 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:40:42 +0200 Subject: [PATCH 34/50] Prepare for follow-on change --- crates/fj-core/src/algorithms/approx/curve.rs | 51 ++++++++++++------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index d31324985..186ffe9e9 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -26,23 +26,40 @@ impl Approx for (&Handle, &Handle, CurveBoundary>) { ) -> Self::Approximation { let (curve, surface, boundary) = self; - match cache.get(curve, boundary) { - Some(approx) => approx, - None => { - let approx = approx_curve( - &geometry - .of_curve(curve) - .unwrap() - .local_on(surface) - .unwrap() - .path, - geometry.of_surface(surface), - boundary, - tolerance, - ); - - cache.insert(curve.clone(), boundary, approx) - } + approx_curve_with_cache( + curve, surface, boundary, tolerance, cache, geometry, + ) + } +} + +/// Approximate the provided curve +/// +/// The approximation is cached, and cached approximations are used, where +/// possible. +pub fn approx_curve_with_cache( + curve: &Handle, + surface: &Handle, + boundary: CurveBoundary>, + tolerance: impl Into, + cache: &mut CurveApproxCache, + geometry: &Geometry, +) -> CurveApprox { + match cache.get(curve, boundary) { + Some(approx) => approx, + None => { + let approx = approx_curve( + &geometry + .of_curve(curve) + .unwrap() + .local_on(surface) + .unwrap() + .path, + geometry.of_surface(surface), + boundary, + tolerance, + ); + + cache.insert(curve.clone(), boundary, approx) } } } From 972a0b98b3a7a2bb170a10c69002d5f2b95372f4 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:41:57 +0200 Subject: [PATCH 35/50] Circumvent redundant abstraction layer --- crates/fj-core/src/algorithms/approx/half_edge.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/half_edge.rs b/crates/fj-core/src/algorithms/approx/half_edge.rs index b186cda49..13eac56b2 100644 --- a/crates/fj-core/src/algorithms/approx/half_edge.rs +++ b/crates/fj-core/src/algorithms/approx/half_edge.rs @@ -9,8 +9,9 @@ use crate::{ }; use super::{ - curve::CurveApproxCache, vertex::VertexApproxCache, Approx, ApproxPoint, - Tolerance, + curve::{approx_curve_with_cache, CurveApproxCache}, + vertex::VertexApproxCache, + Approx, ApproxPoint, Tolerance, }; impl Approx for (&Handle, &Handle) { @@ -53,8 +54,14 @@ impl Approx for (&Handle, &Handle) { let rest = { let boundary = geometry.of_half_edge(half_edge).boundary; - let approx = (half_edge.curve(), surface, boundary) - .approx_with_cache(tolerance, &mut cache.curve, geometry); + let approx = approx_curve_with_cache( + half_edge.curve(), + surface, + boundary, + tolerance, + &mut cache.curve, + geometry, + ); approx.points.into_iter().map(|point| { let point_surface = geometry From 219f0d080c1a0a55ae9765762e659d811ef25d7d Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:46:33 +0200 Subject: [PATCH 36/50] Simplify test --- crates/fj-core/src/algorithms/approx/curve.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 186ffe9e9..13bdfcfc5 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -212,7 +212,9 @@ mod tests { use pretty_assertions::assert_eq; use crate::{ - algorithms::approx::{circle::approx_circle, Approx, ApproxPoint}, + algorithms::approx::{ + circle::approx_circle, curve::approx_curve, Approx, ApproxPoint, + }, geometry::{CurveBoundary, GlobalPath, SurfacePath}, operations::build::{BuildCurve, BuildSurface}, topology::{Curve, Surface}, @@ -221,18 +223,15 @@ mod tests { #[test] fn approx_line_on_flat_surface() { - let mut core = Core::new(); + let core = Core::new(); - let surface = core.layers.topology.surfaces.xz_plane(); + let surface = core.layers.geometry.xz_plane(); let (path, boundary) = SurfacePath::line_from_points([[1., 1.], [2., 1.]]); - let curve = - Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from(boundary); let tolerance = 1.; - let approx = (&curve, &surface, boundary) - .approx(tolerance, &core.layers.geometry); + let approx = approx_curve(&path, surface, boundary, tolerance); assert_eq!(approx.points, vec![]); } From 567229816d817a321e0eca0b6aaa68736c0f034b Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:48:21 +0200 Subject: [PATCH 37/50] Simplify test --- crates/fj-core/src/algorithms/approx/curve.rs | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 13bdfcfc5..3b2989c77 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -208,14 +208,14 @@ impl CurveApproxCache { mod tests { use std::f64::consts::TAU; - use fj_math::{Circle, Point}; + use fj_math::{Circle, Point, Vector}; use pretty_assertions::assert_eq; use crate::{ algorithms::approx::{ circle::approx_circle, curve::approx_curve, Approx, ApproxPoint, }, - geometry::{CurveBoundary, GlobalPath, SurfacePath}, + geometry::{CurveBoundary, GlobalPath, SurfaceGeom, SurfacePath}, operations::build::{BuildCurve, BuildSurface}, topology::{Curve, Surface}, Core, @@ -238,22 +238,16 @@ mod tests { #[test] fn approx_line_on_curved_surface_but_not_along_curve() { - let mut core = Core::new(); - - let surface = Surface::from_uv( - GlobalPath::circle_from_radius(1.), - [0., 0., 1.], - &mut core, - ); + let surface = SurfaceGeom { + u: GlobalPath::circle_from_radius(1.), + v: Vector::from([0., 0., 1.]), + }; let (path, boundary) = SurfacePath::line_from_points([[1., 1.], [2., 1.]]); - let curve = - Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from(boundary); let tolerance = 1.; - let approx = (&curve, &surface, boundary) - .approx(tolerance, &core.layers.geometry); + let approx = approx_curve(&path, &surface, boundary, tolerance); assert_eq!(approx.points, vec![]); } From 3afd86aadf55719f7a05a8b0b4dbad150e6576fb Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:55:36 +0200 Subject: [PATCH 38/50] Simplify test --- crates/fj-core/src/algorithms/approx/curve.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index 3b2989c77..de7f4a85f 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -258,18 +258,19 @@ mod tests { let circle = Circle::from_center_and_radius(Point::origin(), 1.); let global_path = GlobalPath::Circle(circle); - let surface = Surface::from_uv(global_path, [0., 0., 1.], &mut core); + let surface_geom = SurfaceGeom { + u: global_path, + v: Vector::from([0., 0., 1.]), + }; + let surface = Surface::from_geometry(surface_geom, &mut core); let path = SurfacePath::line_from_points_with_coords([ ([0.], [0., 1.]), ([TAU], [TAU, 1.]), ]); - let curve = - Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from([[0.], [TAU]]); let tolerance = 1.; - let approx = (&curve, &surface, boundary) - .approx(tolerance, &core.layers.geometry); + let approx = approx_curve(&path, &surface_geom, boundary, tolerance); let expected_approx = approx_circle(&circle, boundary, tolerance) .into_iter() From e7b8278775fd9fc845417ef9fc739a29a295ea9c Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:57:40 +0200 Subject: [PATCH 39/50] Simplify test --- crates/fj-core/src/algorithms/approx/curve.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index de7f4a85f..a860a0e05 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -213,11 +213,11 @@ mod tests { use crate::{ algorithms::approx::{ - circle::approx_circle, curve::approx_curve, Approx, ApproxPoint, + circle::approx_circle, curve::approx_curve, ApproxPoint, }, geometry::{CurveBoundary, GlobalPath, SurfaceGeom, SurfacePath}, - operations::build::{BuildCurve, BuildSurface}, - topology::{Curve, Surface}, + operations::build::BuildSurface, + topology::Surface, Core, }; @@ -291,16 +291,14 @@ mod tests { fn approx_circle_on_flat_surface() { let mut core = Core::new(); - let surface = core.layers.topology.surfaces.xz_plane(); + let surface_geom = *core.layers.geometry.xz_plane(); + let surface = Surface::from_geometry(surface_geom, &mut core); let circle = Circle::from_center_and_radius([0., 0.], 1.); let path = SurfacePath::Circle(circle); - let curve = - Curve::from_path_and_surface(path, surface.clone(), &mut core); let boundary = CurveBoundary::from([[0.], [TAU]]); let tolerance = 1.; - let approx = (&curve, &surface, boundary) - .approx(tolerance, &core.layers.geometry); + let approx = approx_curve(&path, &surface_geom, boundary, tolerance); let expected_approx = approx_circle(&circle, boundary, tolerance) .into_iter() From 7dbb6d12608578e14d4dafd30589ee2cfcfaf58c Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:58:05 +0200 Subject: [PATCH 40/50] Remove unused code --- crates/fj-core/src/algorithms/approx/curve.rs | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index a860a0e05..a7a96e6fa 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -10,27 +10,7 @@ use crate::{ topology::{Curve, Surface}, }; -use super::{ - circle::approx_circle, line::approx_line, Approx, ApproxPoint, Tolerance, -}; - -impl Approx for (&Handle, &Handle, CurveBoundary>) { - type Approximation = CurveApprox; - type Cache = CurveApproxCache; - - fn approx_with_cache( - self, - tolerance: impl Into, - cache: &mut Self::Cache, - geometry: &Geometry, - ) -> Self::Approximation { - let (curve, surface, boundary) = self; - - approx_curve_with_cache( - curve, surface, boundary, tolerance, cache, geometry, - ) - } -} +use super::{circle::approx_circle, line::approx_line, ApproxPoint, Tolerance}; /// Approximate the provided curve /// From 6c7ce69b4fa3b5fdf056f8044fb4136f859053bb Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:58:22 +0200 Subject: [PATCH 41/50] Remove unnecessary `pub` --- crates/fj-core/src/algorithms/approx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fj-core/src/algorithms/approx/mod.rs b/crates/fj-core/src/algorithms/approx/mod.rs index 8607b025c..15025bb07 100644 --- a/crates/fj-core/src/algorithms/approx/mod.rs +++ b/crates/fj-core/src/algorithms/approx/mod.rs @@ -1,6 +1,5 @@ //! Approximation of objects -pub mod curve; pub mod cycle; pub mod face; pub mod half_edge; @@ -11,6 +10,7 @@ pub mod tolerance; pub mod vertex; mod circle; +mod curve; mod line; use std::{ From adce91db4a96ec0890bacb488288dd556bc6884d Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 20:58:33 +0200 Subject: [PATCH 42/50] Remove redundant doc comment --- crates/fj-core/src/algorithms/approx/curve.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/curve.rs b/crates/fj-core/src/algorithms/approx/curve.rs index a7a96e6fa..426fd161f 100644 --- a/crates/fj-core/src/algorithms/approx/curve.rs +++ b/crates/fj-core/src/algorithms/approx/curve.rs @@ -1,5 +1,3 @@ -//! Curve approximation - use std::collections::BTreeMap; use fj_math::{Circle, Line, Point}; From 1adf7a60d1a379cdd1bc786d1c68ed1e9eb922ad Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 21:01:37 +0200 Subject: [PATCH 43/50] Remove unnecessary `pub` --- crates/fj-core/src/algorithms/approx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fj-core/src/algorithms/approx/mod.rs b/crates/fj-core/src/algorithms/approx/mod.rs index 15025bb07..c88042866 100644 --- a/crates/fj-core/src/algorithms/approx/mod.rs +++ b/crates/fj-core/src/algorithms/approx/mod.rs @@ -7,11 +7,11 @@ pub mod shell; pub mod sketch; pub mod solid; pub mod tolerance; -pub mod vertex; mod circle; mod curve; mod line; +mod vertex; use std::{ cmp::Ordering, From 85e31174c27ed216d35e28a9e742b388aea465b8 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 21:01:52 +0200 Subject: [PATCH 44/50] Remove redundant doc comment --- crates/fj-core/src/algorithms/approx/vertex.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/vertex.rs b/crates/fj-core/src/algorithms/approx/vertex.rs index ca641eb4d..68d70076a 100644 --- a/crates/fj-core/src/algorithms/approx/vertex.rs +++ b/crates/fj-core/src/algorithms/approx/vertex.rs @@ -1,5 +1,3 @@ -//! Vertex approximation - use std::collections::BTreeMap; use fj_math::Point; From 0560022c2c68a475d70014ec91abd4bde4973803 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 21:14:31 +0200 Subject: [PATCH 45/50] Add `approx_vertex` --- .../src/algorithms/approx/half_edge.rs | 24 +++++---------- .../fj-core/src/algorithms/approx/vertex.rs | 29 ++++++++++++++++++- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/half_edge.rs b/crates/fj-core/src/algorithms/approx/half_edge.rs index 13eac56b2..338b244d1 100644 --- a/crates/fj-core/src/algorithms/approx/half_edge.rs +++ b/crates/fj-core/src/algorithms/approx/half_edge.rs @@ -10,7 +10,7 @@ use crate::{ use super::{ curve::{approx_curve_with_cache, CurveApproxCache}, - vertex::VertexApproxCache, + vertex::{approx_vertex, VertexApproxCache}, Approx, ApproxPoint, Tolerance, }; @@ -36,21 +36,13 @@ impl Approx for (&Handle, &Handle) { .unwrap() .path, ); - let start_position = - match cache.start_position.get(half_edge.start_vertex()) { - Some(position) => position, - None => { - let position_global = geometry - .of_surface(surface) - .point_from_surface_coords(start_position_surface); - cache.start_position.insert( - half_edge.start_vertex().clone(), - position_global, - ) - } - }; - - let first = ApproxPoint::new(start_position_surface, start_position); + let first = approx_vertex( + half_edge.start_vertex().clone(), + surface, + start_position_surface, + &mut cache.start_position, + geometry, + ); let rest = { let boundary = geometry.of_half_edge(half_edge).boundary; diff --git a/crates/fj-core/src/algorithms/approx/vertex.rs b/crates/fj-core/src/algorithms/approx/vertex.rs index 68d70076a..2f54e7250 100644 --- a/crates/fj-core/src/algorithms/approx/vertex.rs +++ b/crates/fj-core/src/algorithms/approx/vertex.rs @@ -2,7 +2,34 @@ use std::collections::BTreeMap; use fj_math::Point; -use crate::{storage::Handle, topology::Vertex}; +use crate::{ + geometry::Geometry, + storage::Handle, + topology::{Surface, Vertex}, +}; + +use super::ApproxPoint; + +/// # Approximate a vertex position +pub fn approx_vertex( + vertex: Handle, + surface: &Handle, + position_surface: Point<2>, + cache: &mut VertexApproxCache, + geometry: &Geometry, +) -> ApproxPoint<2> { + let position_global = match cache.get(&vertex) { + Some(position) => position, + None => { + let position_global = geometry + .of_surface(surface) + .point_from_surface_coords(position_surface); + cache.insert(vertex, position_global) + } + }; + + ApproxPoint::new(position_surface, position_global) +} /// Cache for vertex approximations #[derive(Default)] From 63aa5c59f25703cc26ff9a52a9c739614b188c8a Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 21:21:06 +0200 Subject: [PATCH 46/50] Prepare for follow-on change --- crates/fj-core/src/algorithms/approx/half_edge.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/fj-core/src/algorithms/approx/half_edge.rs b/crates/fj-core/src/algorithms/approx/half_edge.rs index 338b244d1..92253a941 100644 --- a/crates/fj-core/src/algorithms/approx/half_edge.rs +++ b/crates/fj-core/src/algorithms/approx/half_edge.rs @@ -27,6 +27,8 @@ impl Approx for (&Handle, &Handle) { let (half_edge, surface) = self; let tolerance = tolerance.into(); + let boundary = geometry.of_half_edge(half_edge).boundary; + let start_position_surface = geometry.of_half_edge(half_edge).start_position( &geometry @@ -45,7 +47,6 @@ impl Approx for (&Handle, &Handle) { ); let rest = { - let boundary = geometry.of_half_edge(half_edge).boundary; let approx = approx_curve_with_cache( half_edge.curve(), surface, From 19642f55be3c9ed28379aff0b3aed36cf6b8108d Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 21:26:35 +0200 Subject: [PATCH 47/50] Consolidate vertex approximation code --- crates/fj-core/src/algorithms/approx/half_edge.rs | 13 +++---------- crates/fj-core/src/algorithms/approx/vertex.rs | 13 +++++++++++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/half_edge.rs b/crates/fj-core/src/algorithms/approx/half_edge.rs index 92253a941..2f0467c9f 100644 --- a/crates/fj-core/src/algorithms/approx/half_edge.rs +++ b/crates/fj-core/src/algorithms/approx/half_edge.rs @@ -28,20 +28,13 @@ impl Approx for (&Handle, &Handle) { let tolerance = tolerance.into(); let boundary = geometry.of_half_edge(half_edge).boundary; + let [start_position_curve, _] = boundary.inner; - let start_position_surface = - geometry.of_half_edge(half_edge).start_position( - &geometry - .of_curve(half_edge.curve()) - .unwrap() - .local_on(surface) - .unwrap() - .path, - ); let first = approx_vertex( half_edge.start_vertex().clone(), + half_edge.curve(), surface, - start_position_surface, + start_position_curve, &mut cache.start_position, geometry, ); diff --git a/crates/fj-core/src/algorithms/approx/vertex.rs b/crates/fj-core/src/algorithms/approx/vertex.rs index 2f54e7250..756ee2e39 100644 --- a/crates/fj-core/src/algorithms/approx/vertex.rs +++ b/crates/fj-core/src/algorithms/approx/vertex.rs @@ -5,7 +5,7 @@ use fj_math::Point; use crate::{ geometry::Geometry, storage::Handle, - topology::{Surface, Vertex}, + topology::{Curve, Surface, Vertex}, }; use super::ApproxPoint; @@ -13,11 +13,20 @@ use super::ApproxPoint; /// # Approximate a vertex position pub fn approx_vertex( vertex: Handle, + curve: &Handle, surface: &Handle, - position_surface: Point<2>, + position_curve: Point<1>, cache: &mut VertexApproxCache, geometry: &Geometry, ) -> ApproxPoint<2> { + let position_surface = geometry + .of_curve(curve) + .unwrap() + .local_on(surface) + .unwrap() + .path + .point_from_path_coords(position_curve); + let position_global = match cache.get(&vertex) { Some(position) => position, None => { From 09ebcab2572a6fe927c308e3050d40989c803888 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 21:29:59 +0200 Subject: [PATCH 48/50] Return more appropriate type from `approx_vertex` --- .../src/algorithms/approx/half_edge.rs | 29 ++++++++++--------- .../fj-core/src/algorithms/approx/vertex.rs | 4 +-- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/half_edge.rs b/crates/fj-core/src/algorithms/approx/half_edge.rs index 2f0467c9f..116d517d2 100644 --- a/crates/fj-core/src/algorithms/approx/half_edge.rs +++ b/crates/fj-core/src/algorithms/approx/half_edge.rs @@ -39,17 +39,21 @@ impl Approx for (&Handle, &Handle) { geometry, ); - let rest = { - let approx = approx_curve_with_cache( - half_edge.curve(), - surface, - boundary, - tolerance, - &mut cache.curve, - geometry, - ); + let rest = approx_curve_with_cache( + half_edge.curve(), + surface, + boundary, + tolerance, + &mut cache.curve, + geometry, + ); - approx.points.into_iter().map(|point| { + let mut points = vec![first]; + points.extend(rest.points); + + let points = points + .into_iter() + .map(|point| { let point_surface = geometry .of_curve(half_edge.curve()) .unwrap() @@ -60,10 +64,7 @@ impl Approx for (&Handle, &Handle) { ApproxPoint::new(point_surface, point.global_form) }) - }; - - let mut points = vec![first]; - points.extend(rest); + .collect(); HalfEdgeApprox { points } } diff --git a/crates/fj-core/src/algorithms/approx/vertex.rs b/crates/fj-core/src/algorithms/approx/vertex.rs index 756ee2e39..92530dfb0 100644 --- a/crates/fj-core/src/algorithms/approx/vertex.rs +++ b/crates/fj-core/src/algorithms/approx/vertex.rs @@ -18,7 +18,7 @@ pub fn approx_vertex( position_curve: Point<1>, cache: &mut VertexApproxCache, geometry: &Geometry, -) -> ApproxPoint<2> { +) -> ApproxPoint<1> { let position_surface = geometry .of_curve(curve) .unwrap() @@ -37,7 +37,7 @@ pub fn approx_vertex( } }; - ApproxPoint::new(position_surface, position_global) + ApproxPoint::new(position_curve, position_global) } /// Cache for vertex approximations From 1da0f6aa17cc2f6d5a64ace4cdae9d0f6a78edf7 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 21:32:23 +0200 Subject: [PATCH 49/50] Update variable name --- crates/fj-core/src/algorithms/approx/half_edge.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/half_edge.rs b/crates/fj-core/src/algorithms/approx/half_edge.rs index 116d517d2..6d0a73ba4 100644 --- a/crates/fj-core/src/algorithms/approx/half_edge.rs +++ b/crates/fj-core/src/algorithms/approx/half_edge.rs @@ -30,7 +30,7 @@ impl Approx for (&Handle, &Handle) { let boundary = geometry.of_half_edge(half_edge).boundary; let [start_position_curve, _] = boundary.inner; - let first = approx_vertex( + let start = approx_vertex( half_edge.start_vertex().clone(), half_edge.curve(), surface, @@ -48,7 +48,7 @@ impl Approx for (&Handle, &Handle) { geometry, ); - let mut points = vec![first]; + let mut points = vec![start]; points.extend(rest.points); let points = points From 3dc32934c0e239ab8b741ce55333dfeff1da17e2 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Fri, 21 Jun 2024 21:33:26 +0200 Subject: [PATCH 50/50] Save unnecessary allocation --- crates/fj-core/src/algorithms/approx/half_edge.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/fj-core/src/algorithms/approx/half_edge.rs b/crates/fj-core/src/algorithms/approx/half_edge.rs index 6d0a73ba4..2c8a07bcf 100644 --- a/crates/fj-core/src/algorithms/approx/half_edge.rs +++ b/crates/fj-core/src/algorithms/approx/half_edge.rs @@ -2,6 +2,8 @@ //! //! See [`HalfEdgeApprox`]. +use std::iter; + use crate::{ geometry::Geometry, storage::Handle, @@ -48,11 +50,8 @@ impl Approx for (&Handle, &Handle) { geometry, ); - let mut points = vec![start]; - points.extend(rest.points); - - let points = points - .into_iter() + let points = iter::once(start) + .chain(rest.points) .map(|point| { let point_surface = geometry .of_curve(half_edge.curve())