From 03f88d7cd2cff166085e1333ec5568af5f2e843f Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 14 Sep 2022 11:22:59 +0200 Subject: [PATCH 1/6] Require `Approx` implementations to be `Sized` --- crates/fj-kernel/src/algorithms/approx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/fj-kernel/src/algorithms/approx/mod.rs b/crates/fj-kernel/src/algorithms/approx/mod.rs index 0284b2b51..2e5229f84 100644 --- a/crates/fj-kernel/src/algorithms/approx/mod.rs +++ b/crates/fj-kernel/src/algorithms/approx/mod.rs @@ -24,7 +24,7 @@ use crate::objects::Curve; pub use self::tolerance::{InvalidTolerance, Tolerance}; /// Approximate an object -pub trait Approx { +pub trait Approx: Sized { /// The approximation of the object type Approximation; From bc8db60a254cfdca4cba0930d20e7420ca21bb08 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 14 Sep 2022 11:24:52 +0200 Subject: [PATCH 2/6] Clean up type import --- crates/fj-kernel/src/algorithms/approx/edge.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/approx/edge.rs b/crates/fj-kernel/src/algorithms/approx/edge.rs index fdfa73bcb..15aa3b5d2 100644 --- a/crates/fj-kernel/src/algorithms/approx/edge.rs +++ b/crates/fj-kernel/src/algorithms/approx/edge.rs @@ -9,13 +9,13 @@ use crate::objects::HalfEdge; use super::{ curve::{CurveApprox, RangeOnCurve}, - Approx, ApproxPoint, + Approx, ApproxPoint, Tolerance, }; impl Approx for &HalfEdge { type Approximation = HalfEdgeApprox; - fn approx(self, tolerance: super::Tolerance) -> Self::Approximation { + fn approx(self, tolerance: Tolerance) -> Self::Approximation { let &[a, b] = self.vertices(); let range = RangeOnCurve::new([a, b]); From fd2f69f878b084b4647d2f1c901fd93000d8314f Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 14 Sep 2022 11:31:04 +0200 Subject: [PATCH 3/6] Add `ApproxCache` This is a placeholder type right now, so I can start the integration of caching capability into the `Approx` trait. --- crates/fj-kernel/src/algorithms/approx/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/fj-kernel/src/algorithms/approx/mod.rs b/crates/fj-kernel/src/algorithms/approx/mod.rs index 2e5229f84..b4261d708 100644 --- a/crates/fj-kernel/src/algorithms/approx/mod.rs +++ b/crates/fj-kernel/src/algorithms/approx/mod.rs @@ -35,6 +35,17 @@ pub trait Approx: Sized { fn approx(self, tolerance: Tolerance) -> Self::Approximation; } +/// A cache for results of an approximation +#[derive(Default)] +pub struct ApproxCache; + +impl ApproxCache { + /// Create an empty cache + pub fn new() -> Self { + Self::default() + } +} + /// A point from an approximation, with local and global forms #[derive(Debug, Clone)] pub struct ApproxPoint { From b616d57e791830baaa17b14c15a2d35add292b24 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 14 Sep 2022 11:32:13 +0200 Subject: [PATCH 4/6] Add `Approx::approx_with_cache` --- .../fj-kernel/src/algorithms/approx/curve.rs | 16 +++++++++++---- .../fj-kernel/src/algorithms/approx/cycle.rs | 12 ++++++++--- .../fj-kernel/src/algorithms/approx/edge.rs | 11 +++++++--- .../fj-kernel/src/algorithms/approx/face.rs | 20 +++++++++++++------ crates/fj-kernel/src/algorithms/approx/mod.rs | 12 ++++++++++- .../fj-kernel/src/algorithms/approx/shell.rs | 10 +++++++--- .../fj-kernel/src/algorithms/approx/sketch.rs | 10 +++++++--- .../fj-kernel/src/algorithms/approx/solid.rs | 10 +++++++--- 8 files changed, 75 insertions(+), 26 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/approx/curve.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs index 19c2465c4..59feb78e9 100644 --- a/crates/fj-kernel/src/algorithms/approx/curve.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -18,16 +18,20 @@ use crate::{ path::GlobalPath, }; -use super::{Approx, ApproxPoint, Tolerance}; +use super::{Approx, ApproxCache, ApproxPoint, Tolerance}; impl Approx for (&Curve, RangeOnCurve) { type Approximation = CurveApprox; - fn approx(self, tolerance: Tolerance) -> Self::Approximation { + fn approx_with_cache( + self, + tolerance: Tolerance, + cache: &mut ApproxCache, + ) -> Self::Approximation { let (curve, range) = self; let points = (curve.global_form(), range) - .approx(tolerance) + .approx_with_cache(tolerance, cache) .into_iter() .map(|point| { let point_surface = @@ -44,7 +48,11 @@ impl Approx for (&Curve, RangeOnCurve) { impl Approx for (&GlobalCurve, RangeOnCurve) { type Approximation = Vec>; - fn approx(self, tolerance: Tolerance) -> Self::Approximation { + fn approx_with_cache( + self, + tolerance: Tolerance, + _: &mut ApproxCache, + ) -> Self::Approximation { let (curve, range) = self; match curve.path() { diff --git a/crates/fj-kernel/src/algorithms/approx/cycle.rs b/crates/fj-kernel/src/algorithms/approx/cycle.rs index 33556a794..57eff6520 100644 --- a/crates/fj-kernel/src/algorithms/approx/cycle.rs +++ b/crates/fj-kernel/src/algorithms/approx/cycle.rs @@ -6,15 +6,21 @@ use fj_math::Segment; use crate::objects::Cycle; -use super::{edge::HalfEdgeApprox, Approx, ApproxPoint, Tolerance}; +use super::{ + edge::HalfEdgeApprox, Approx, ApproxCache, ApproxPoint, Tolerance, +}; impl Approx for &Cycle { type Approximation = CycleApprox; - fn approx(self, tolerance: Tolerance) -> Self::Approximation { + fn approx_with_cache( + self, + tolerance: Tolerance, + cache: &mut ApproxCache, + ) -> Self::Approximation { let half_edges = self .half_edges() - .map(|half_edge| half_edge.approx(tolerance)) + .map(|half_edge| half_edge.approx_with_cache(tolerance, cache)) .collect(); CycleApprox { half_edges } } diff --git a/crates/fj-kernel/src/algorithms/approx/edge.rs b/crates/fj-kernel/src/algorithms/approx/edge.rs index 15aa3b5d2..c30f65721 100644 --- a/crates/fj-kernel/src/algorithms/approx/edge.rs +++ b/crates/fj-kernel/src/algorithms/approx/edge.rs @@ -9,13 +9,17 @@ use crate::objects::HalfEdge; use super::{ curve::{CurveApprox, RangeOnCurve}, - Approx, ApproxPoint, Tolerance, + Approx, ApproxCache, ApproxPoint, Tolerance, }; impl Approx for &HalfEdge { type Approximation = HalfEdgeApprox; - fn approx(self, tolerance: Tolerance) -> Self::Approximation { + fn approx_with_cache( + self, + tolerance: Tolerance, + cache: &mut ApproxCache, + ) -> Self::Approximation { let &[a, b] = self.vertices(); let range = RangeOnCurve::new([a, b]); @@ -23,7 +27,8 @@ impl Approx for &HalfEdge { a.surface_form().position(), a.global_form().position(), ); - let curve_approx = (self.curve(), range).approx(tolerance); + let curve_approx = + (self.curve(), range).approx_with_cache(tolerance, cache); HalfEdgeApprox { first, diff --git a/crates/fj-kernel/src/algorithms/approx/face.rs b/crates/fj-kernel/src/algorithms/approx/face.rs index 1906a0613..911062d7e 100644 --- a/crates/fj-kernel/src/algorithms/approx/face.rs +++ b/crates/fj-kernel/src/algorithms/approx/face.rs @@ -11,15 +11,19 @@ use crate::{ objects::{Face, Faces, Handedness}, }; -use super::{cycle::CycleApprox, Approx, ApproxPoint, Tolerance}; +use super::{cycle::CycleApprox, Approx, ApproxCache, ApproxPoint, Tolerance}; impl Approx for &Faces { type Approximation = BTreeSet; - fn approx(self, tolerance: Tolerance) -> Self::Approximation { + fn approx_with_cache( + self, + tolerance: Tolerance, + cache: &mut ApproxCache, + ) -> Self::Approximation { let approx = self .into_iter() - .map(|face| face.approx(tolerance)) + .map(|face| face.approx_with_cache(tolerance, cache)) .collect(); let min_distance = ValidationConfig::default().distinct_min_distance; @@ -61,7 +65,11 @@ impl Approx for &Faces { impl Approx for &Face { type Approximation = FaceApprox; - fn approx(self, tolerance: Tolerance) -> Self::Approximation { + fn approx_with_cache( + self, + tolerance: Tolerance, + cache: &mut ApproxCache, + ) -> Self::Approximation { // Curved faces whose curvature is not fully defined by their edges // are not supported yet. For that reason, we can fully ignore `face`'s // `surface` field and just pass the edges to `Self::for_edges`. @@ -75,11 +83,11 @@ impl Approx for &Face { // would need to provide its own approximation, as the edges that bound // it have nothing to do with its curvature. - let exterior = self.exterior().approx(tolerance); + let exterior = self.exterior().approx_with_cache(tolerance, cache); let mut interiors = BTreeSet::new(); for cycle in self.interiors() { - let cycle = cycle.approx(tolerance); + let cycle = cycle.approx_with_cache(tolerance, cache); interiors.insert(cycle); } diff --git a/crates/fj-kernel/src/algorithms/approx/mod.rs b/crates/fj-kernel/src/algorithms/approx/mod.rs index b4261d708..57f23d54e 100644 --- a/crates/fj-kernel/src/algorithms/approx/mod.rs +++ b/crates/fj-kernel/src/algorithms/approx/mod.rs @@ -32,7 +32,17 @@ pub trait Approx: Sized { /// /// `tolerance` defines how far the approximation is allowed to deviate from /// the actual object. - fn approx(self, tolerance: Tolerance) -> Self::Approximation; + fn approx(self, tolerance: Tolerance) -> Self::Approximation { + let mut cache = ApproxCache::new(); + self.approx_with_cache(tolerance, &mut cache) + } + + /// Approximate the object, using the provided cache + fn approx_with_cache( + self, + tolerance: Tolerance, + cache: &mut ApproxCache, + ) -> Self::Approximation; } /// A cache for results of an approximation diff --git a/crates/fj-kernel/src/algorithms/approx/shell.rs b/crates/fj-kernel/src/algorithms/approx/shell.rs index fcce75511..00f772b42 100644 --- a/crates/fj-kernel/src/algorithms/approx/shell.rs +++ b/crates/fj-kernel/src/algorithms/approx/shell.rs @@ -4,12 +4,16 @@ use std::collections::BTreeSet; use crate::objects::Shell; -use super::{face::FaceApprox, Approx, Tolerance}; +use super::{face::FaceApprox, Approx, ApproxCache, Tolerance}; impl Approx for &Shell { type Approximation = BTreeSet; - fn approx(self, tolerance: Tolerance) -> Self::Approximation { - self.faces().approx(tolerance) + fn approx_with_cache( + self, + tolerance: Tolerance, + cache: &mut ApproxCache, + ) -> Self::Approximation { + self.faces().approx_with_cache(tolerance, cache) } } diff --git a/crates/fj-kernel/src/algorithms/approx/sketch.rs b/crates/fj-kernel/src/algorithms/approx/sketch.rs index 43938b4b4..d3edb5177 100644 --- a/crates/fj-kernel/src/algorithms/approx/sketch.rs +++ b/crates/fj-kernel/src/algorithms/approx/sketch.rs @@ -4,12 +4,16 @@ use std::collections::BTreeSet; use crate::objects::Sketch; -use super::{face::FaceApprox, Approx, Tolerance}; +use super::{face::FaceApprox, Approx, ApproxCache, Tolerance}; impl Approx for &Sketch { type Approximation = BTreeSet; - fn approx(self, tolerance: Tolerance) -> Self::Approximation { - self.faces().approx(tolerance) + fn approx_with_cache( + self, + tolerance: Tolerance, + cache: &mut ApproxCache, + ) -> Self::Approximation { + self.faces().approx_with_cache(tolerance, cache) } } diff --git a/crates/fj-kernel/src/algorithms/approx/solid.rs b/crates/fj-kernel/src/algorithms/approx/solid.rs index 34126f82c..7c26396c0 100644 --- a/crates/fj-kernel/src/algorithms/approx/solid.rs +++ b/crates/fj-kernel/src/algorithms/approx/solid.rs @@ -4,14 +4,18 @@ use std::collections::BTreeSet; use crate::objects::Solid; -use super::{face::FaceApprox, Approx, Tolerance}; +use super::{face::FaceApprox, Approx, ApproxCache, Tolerance}; impl Approx for &Solid { type Approximation = BTreeSet; - fn approx(self, tolerance: Tolerance) -> Self::Approximation { + fn approx_with_cache( + self, + tolerance: Tolerance, + cache: &mut ApproxCache, + ) -> Self::Approximation { self.shells() - .flat_map(|shell| shell.approx(tolerance)) + .flat_map(|shell| shell.approx_with_cache(tolerance, cache)) .collect() } } From 17bbf5983f34a681e841e7715f00cc0f25c0b00b Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 14 Sep 2022 11:37:41 +0200 Subject: [PATCH 5/6] Add `GlobalCurveApprox` --- crates/fj-kernel/src/algorithms/approx/curve.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/approx/curve.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs index 59feb78e9..427e68df9 100644 --- a/crates/fj-kernel/src/algorithms/approx/curve.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -32,6 +32,7 @@ impl Approx for (&Curve, RangeOnCurve) { let points = (curve.global_form(), range) .approx_with_cache(tolerance, cache) + .points .into_iter() .map(|point| { let point_surface = @@ -46,7 +47,7 @@ impl Approx for (&Curve, RangeOnCurve) { } impl Approx for (&GlobalCurve, RangeOnCurve) { - type Approximation = Vec>; + type Approximation = GlobalCurveApprox; fn approx_with_cache( self, @@ -55,12 +56,14 @@ impl Approx for (&GlobalCurve, RangeOnCurve) { ) -> Self::Approximation { let (curve, range) = self; - match curve.path() { + let points = match curve.path() { GlobalPath::Circle(circle) => { approx_circle(&circle, range, tolerance) } GlobalPath::Line(_) => vec![], - } + }; + + GlobalCurveApprox { points } } } @@ -190,6 +193,13 @@ pub struct CurveApprox { pub points: Vec>, } +/// An approximation of a [`GlobalCurve`] +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct GlobalCurveApprox { + /// The points that approximate the curve + pub points: Vec>, +} + #[cfg(test)] mod tests { use fj_math::Scalar; From d64c0fb31c04abd2ebe6c120aa6d237fc90d2bbf Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Wed, 14 Sep 2022 11:50:31 +0200 Subject: [PATCH 6/6] Cache `GlobalCurve` approximations --- .../fj-kernel/src/algorithms/approx/curve.rs | 8 ++++-- crates/fj-kernel/src/algorithms/approx/mod.rs | 26 +++++++++++++++++-- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/crates/fj-kernel/src/algorithms/approx/curve.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs index 427e68df9..970c3f788 100644 --- a/crates/fj-kernel/src/algorithms/approx/curve.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -52,10 +52,14 @@ impl Approx for (&GlobalCurve, RangeOnCurve) { fn approx_with_cache( self, tolerance: Tolerance, - _: &mut ApproxCache, + cache: &mut ApproxCache, ) -> Self::Approximation { let (curve, range) = self; + if let Some(approx) = cache.global_curve(curve) { + return approx; + } + let points = match curve.path() { GlobalPath::Circle(circle) => { approx_circle(&circle, range, tolerance) @@ -63,7 +67,7 @@ impl Approx for (&GlobalCurve, RangeOnCurve) { GlobalPath::Line(_) => vec![], }; - GlobalCurveApprox { points } + cache.insert_global_curve(curve, GlobalCurveApprox { points }) } } diff --git a/crates/fj-kernel/src/algorithms/approx/mod.rs b/crates/fj-kernel/src/algorithms/approx/mod.rs index 57f23d54e..49729fa33 100644 --- a/crates/fj-kernel/src/algorithms/approx/mod.rs +++ b/crates/fj-kernel/src/algorithms/approx/mod.rs @@ -12,6 +12,7 @@ pub mod tolerance; use std::{ any::Any, cmp::Ordering, + collections::BTreeMap, fmt::Debug, hash::{Hash, Hasher}, rc::Rc, @@ -19,8 +20,9 @@ use std::{ use fj_math::Point; -use crate::objects::Curve; +use crate::objects::{Curve, GlobalCurve}; +use self::curve::GlobalCurveApprox; pub use self::tolerance::{InvalidTolerance, Tolerance}; /// Approximate an object @@ -47,13 +49,33 @@ pub trait Approx: Sized { /// A cache for results of an approximation #[derive(Default)] -pub struct ApproxCache; +pub struct ApproxCache { + global_curves: BTreeMap, +} impl ApproxCache { /// Create an empty cache pub fn new() -> Self { Self::default() } + + /// Insert the approximation of a [`GlobalCurve`] + pub fn insert_global_curve( + &mut self, + object: &GlobalCurve, + approx: GlobalCurveApprox, + ) -> GlobalCurveApprox { + self.global_curves.insert(*object, approx.clone()); + approx + } + + /// Access the approximation for the given [`GlobalCurve`], if available + pub fn global_curve( + &self, + object: &GlobalCurve, + ) -> Option { + self.global_curves.get(object).cloned() + } } /// A point from an approximation, with local and global forms