Skip to content

Commit

Permalink
Merge pull request #1082 from hannobraun/approx
Browse files Browse the repository at this point in the history
Cache `GlobalCurve` approximations
  • Loading branch information
hannobraun authored Sep 14, 2022
2 parents cb3c7e0 + d64c0fb commit f88e399
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 31 deletions.
36 changes: 29 additions & 7 deletions crates/fj-kernel/src/algorithms/approx/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,21 @@ 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)
.points
.into_iter()
.map(|point| {
let point_surface =
Expand All @@ -42,17 +47,27 @@ impl Approx for (&Curve, RangeOnCurve) {
}

impl Approx for (&GlobalCurve, RangeOnCurve) {
type Approximation = Vec<ApproxPoint<1>>;
type Approximation = GlobalCurveApprox;

fn approx(self, tolerance: Tolerance) -> Self::Approximation {
fn approx_with_cache(
self,
tolerance: Tolerance,
cache: &mut ApproxCache,
) -> Self::Approximation {
let (curve, range) = self;

match curve.path() {
if let Some(approx) = cache.global_curve(curve) {
return approx;
}

let points = match curve.path() {
GlobalPath::Circle(circle) => {
approx_circle(&circle, range, tolerance)
}
GlobalPath::Line(_) => vec![],
}
};

cache.insert_global_curve(curve, GlobalCurveApprox { points })
}
}

Expand Down Expand Up @@ -182,6 +197,13 @@ pub struct CurveApprox {
pub points: Vec<ApproxPoint<2>>,
}

/// 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<ApproxPoint<1>>,
}

#[cfg(test)]
mod tests {
use fj_math::Scalar;
Expand Down
12 changes: 9 additions & 3 deletions crates/fj-kernel/src/algorithms/approx/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}
Expand Down
11 changes: 8 additions & 3 deletions crates/fj-kernel/src/algorithms/approx/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,26 @@ use crate::objects::HalfEdge;

use super::{
curve::{CurveApprox, RangeOnCurve},
Approx, ApproxPoint,
Approx, ApproxCache, ApproxPoint, Tolerance,
};

impl Approx for &HalfEdge {
type Approximation = HalfEdgeApprox;

fn approx(self, tolerance: super::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]);

let first = ApproxPoint::new(
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,
Expand Down
20 changes: 14 additions & 6 deletions crates/fj-kernel/src/algorithms/approx/face.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FaceApprox>;

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;
Expand Down Expand Up @@ -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`.
Expand All @@ -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);
}

Expand Down
49 changes: 46 additions & 3 deletions crates/fj-kernel/src/algorithms/approx/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,70 @@ pub mod tolerance;
use std::{
any::Any,
cmp::Ordering,
collections::BTreeMap,
fmt::Debug,
hash::{Hash, Hasher},
rc::Rc,
};

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
pub trait Approx {
pub trait Approx: Sized {
/// The approximation of the object
type Approximation;

/// Approximate the object
///
/// `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
#[derive(Default)]
pub struct ApproxCache {
global_curves: BTreeMap<GlobalCurve, GlobalCurveApprox>,
}

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<GlobalCurveApprox> {
self.global_curves.get(object).cloned()
}
}

/// A point from an approximation, with local and global forms
Expand Down
10 changes: 7 additions & 3 deletions crates/fj-kernel/src/algorithms/approx/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FaceApprox>;

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)
}
}
10 changes: 7 additions & 3 deletions crates/fj-kernel/src/algorithms/approx/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FaceApprox>;

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)
}
}
10 changes: 7 additions & 3 deletions crates/fj-kernel/src/algorithms/approx/solid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FaceApprox>;

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()
}
}

0 comments on commit f88e399

Please sign in to comment.