From e0954ea7c19be0b0db2b0f400d48b2942eae85ba Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Mon, 4 Apr 2022 15:51:21 +0200 Subject: [PATCH 1/5] Rename `Approximation` to `FaceApprox` The new name is more specific, which is necessary because I need to add more such structs. --- fj-kernel/src/algorithms/approximation.rs | 10 +++++----- fj-kernel/src/algorithms/mod.rs | 3 +-- fj-kernel/src/algorithms/triangulation.rs | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/fj-kernel/src/algorithms/approximation.rs b/fj-kernel/src/algorithms/approximation.rs index ff36b3f27..793201608 100644 --- a/fj-kernel/src/algorithms/approximation.rs +++ b/fj-kernel/src/algorithms/approximation.rs @@ -6,7 +6,7 @@ use crate::topology::{Cycle, Face, Vertex}; /// The approximation of a face #[derive(Debug, PartialEq)] -pub struct Approximation { +pub struct FaceApprox { /// All points that make up the approximation /// /// These could be actual vertices from the model, points that approximate @@ -23,7 +23,7 @@ pub struct Approximation { pub segments: HashSet>, } -impl Approximation { +impl FaceApprox { /// Compute the approximation of a face /// /// `tolerance` defines how far the approximation is allowed to deviate from @@ -123,7 +123,7 @@ mod tests { topology::{Face, Vertex}, }; - use super::Approximation; + use super::FaceApprox; #[test] fn approximate_edge() -> anyhow::Result<()> { @@ -167,8 +167,8 @@ mod tests { .build()?; assert_eq!( - Approximation::new(&face.get(), tolerance), - Approximation { + FaceApprox::new(&face.get(), tolerance), + FaceApprox { points: set![a, b, c, d], segments: set![ Segment::from([a, b]), diff --git a/fj-kernel/src/algorithms/mod.rs b/fj-kernel/src/algorithms/mod.rs index 14e0f3d1d..806723865 100644 --- a/fj-kernel/src/algorithms/mod.rs +++ b/fj-kernel/src/algorithms/mod.rs @@ -8,6 +8,5 @@ mod sweep; mod triangulation; pub use self::{ - approximation::Approximation, sweep::sweep_shape, - triangulation::triangulate, + approximation::FaceApprox, sweep::sweep_shape, triangulation::triangulate, }; diff --git a/fj-kernel/src/algorithms/triangulation.rs b/fj-kernel/src/algorithms/triangulation.rs index 0c62791a3..076a71d39 100644 --- a/fj-kernel/src/algorithms/triangulation.rs +++ b/fj-kernel/src/algorithms/triangulation.rs @@ -11,7 +11,7 @@ use spade::HasPosition; use crate::{geometry, shape::Shape, topology::Face}; -use super::Approximation; +use super::FaceApprox; /// Triangulate a shape pub fn triangulate( @@ -25,7 +25,7 @@ pub fn triangulate( match &face { Face::Face { surface, color, .. } => { let surface = surface.get(); - let approx = Approximation::new(&face, tolerance); + let approx = FaceApprox::new(&face, tolerance); let points: Vec<_> = approx .points From a6bf6fb19f9fd18eff9aa64d30298d68b834770c Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Mon, 4 Apr 2022 16:01:11 +0200 Subject: [PATCH 2/5] Improve doc comment --- fj-kernel/src/algorithms/approximation.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fj-kernel/src/algorithms/approximation.rs b/fj-kernel/src/algorithms/approximation.rs index 793201608..61bc85df2 100644 --- a/fj-kernel/src/algorithms/approximation.rs +++ b/fj-kernel/src/algorithms/approximation.rs @@ -4,7 +4,7 @@ use fj_math::{Point, Scalar, Segment}; use crate::topology::{Cycle, Face, Vertex}; -/// The approximation of a face +/// An approximation of a [`Face`] #[derive(Debug, PartialEq)] pub struct FaceApprox { /// All points that make up the approximation From 94d6c9f693b8c56c5fdefc0d00835282268407ae Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Mon, 4 Apr 2022 16:08:29 +0200 Subject: [PATCH 3/5] Add `CycleApprox` --- fj-kernel/src/algorithms/approximation.rs | 53 +++++++++++++---------- fj-kernel/src/algorithms/mod.rs | 4 +- fj-kernel/src/algorithms/sweep.rs | 6 +-- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/fj-kernel/src/algorithms/approximation.rs b/fj-kernel/src/algorithms/approximation.rs index 61bc85df2..32c65ea6c 100644 --- a/fj-kernel/src/algorithms/approximation.rs +++ b/fj-kernel/src/algorithms/approximation.rs @@ -46,17 +46,17 @@ impl FaceApprox { let mut segments = HashSet::new(); for cycle in face.all_cycles() { - let cycle_points = approximate_cycle(&cycle, tolerance); + let cycle = CycleApprox::new(&cycle, tolerance); let mut cycle_segments = Vec::new(); - for segment in cycle_points.windows(2) { + for segment in cycle.points.windows(2) { let p0 = segment[0]; let p1 = segment[1]; cycle_segments.push(Segment::from([p0, p1])); } - points.extend(cycle_points); + points.extend(cycle.points); segments.extend(cycle_segments); } @@ -64,6 +64,34 @@ impl FaceApprox { } } +/// An approximation of a [`Cycle`] +#[derive(Debug, Eq, PartialEq, Hash)] +pub struct CycleApprox { + /// The points that approximate the cycle + pub points: Vec>, +} + +impl CycleApprox { + /// Compute the approximation of a cycle + /// + /// `tolerance` defines how far the approximation is allowed to deviate from + /// the actual face. + pub fn new(cycle: &Cycle, tolerance: Scalar) -> Self { + let mut points = Vec::new(); + + for edge in cycle.edges() { + let mut edge_points = Vec::new(); + edge.curve().approx(tolerance, &mut edge_points); + + points.extend(approximate_edge(edge_points, edge.vertices())); + } + + points.dedup(); + + Self { points } + } +} + fn approximate_edge( mut points: Vec>, vertices: Option<[Vertex; 2]>, @@ -93,25 +121,6 @@ fn approximate_edge( points } -/// Compute an approximation for a cycle -/// -/// `tolerance` defines how far the approximation is allowed to deviate from the -/// actual cycle. -pub fn approximate_cycle(cycle: &Cycle, tolerance: Scalar) -> Vec> { - let mut points = Vec::new(); - - for edge in cycle.edges() { - let mut edge_points = Vec::new(); - edge.curve().approx(tolerance, &mut edge_points); - - points.extend(approximate_edge(edge_points, edge.vertices())); - } - - points.dedup(); - - points -} - #[cfg(test)] mod tests { use fj_math::{Point, Scalar, Segment}; diff --git a/fj-kernel/src/algorithms/mod.rs b/fj-kernel/src/algorithms/mod.rs index 806723865..7c7ce5681 100644 --- a/fj-kernel/src/algorithms/mod.rs +++ b/fj-kernel/src/algorithms/mod.rs @@ -8,5 +8,7 @@ mod sweep; mod triangulation; pub use self::{ - approximation::FaceApprox, sweep::sweep_shape, triangulation::triangulate, + approximation::{CycleApprox, FaceApprox}, + sweep::sweep_shape, + triangulation::triangulate, }; diff --git a/fj-kernel/src/algorithms/sweep.rs b/fj-kernel/src/algorithms/sweep.rs index 0c5ade1dc..4d71a52f5 100644 --- a/fj-kernel/src/algorithms/sweep.rs +++ b/fj-kernel/src/algorithms/sweep.rs @@ -8,7 +8,7 @@ use crate::{ topology::{Cycle, Edge, Face, Vertex}, }; -use super::approximation::approximate_cycle; +use super::CycleApprox; /// Create a new shape by sweeping an existing one pub fn sweep_shape( @@ -135,10 +135,10 @@ pub fn sweep_shape( // This is the last piece of code that still uses the triangle // representation. - let approx = approximate_cycle(&cycle_source.get(), tolerance); + let approx = CycleApprox::new(&cycle_source.get(), tolerance); let mut quads = Vec::new(); - for segment in approx.windows(2) { + for segment in approx.points.windows(2) { let segment = Segment::from_points([segment[0], segment[1]]); let [v0, v1] = segment.points(); From 163b9ee2ec3c61da8640f580f1e726b116de00c2 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Mon, 4 Apr 2022 16:11:54 +0200 Subject: [PATCH 4/5] Add `CycleApprox::segments` --- fj-kernel/src/algorithms/approximation.rs | 26 +++++++++++++++-------- fj-kernel/src/algorithms/sweep.rs | 6 ++---- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/fj-kernel/src/algorithms/approximation.rs b/fj-kernel/src/algorithms/approximation.rs index 32c65ea6c..1c2b66bfe 100644 --- a/fj-kernel/src/algorithms/approximation.rs +++ b/fj-kernel/src/algorithms/approximation.rs @@ -48,16 +48,8 @@ impl FaceApprox { for cycle in face.all_cycles() { let cycle = CycleApprox::new(&cycle, tolerance); - let mut cycle_segments = Vec::new(); - for segment in cycle.points.windows(2) { - let p0 = segment[0]; - let p1 = segment[1]; - - cycle_segments.push(Segment::from([p0, p1])); - } - + segments.extend(cycle.segments()); points.extend(cycle.points); - segments.extend(cycle_segments); } Self { points, segments } @@ -90,6 +82,22 @@ impl CycleApprox { Self { points } } + + /// Construct the segments that approximate the cycle + pub fn segments(&self) -> Vec> { + let mut segments = Vec::new(); + + for segment in self.points.windows(2) { + // This can't panic, as we passed `2` to `windows`. Can be cleaned + // up, once `array_windows` is stable. + let p0 = segment[0]; + let p1 = segment[1]; + + segments.push(Segment::from([p0, p1])); + } + + segments + } } fn approximate_edge( diff --git a/fj-kernel/src/algorithms/sweep.rs b/fj-kernel/src/algorithms/sweep.rs index 4d71a52f5..47d268cfe 100644 --- a/fj-kernel/src/algorithms/sweep.rs +++ b/fj-kernel/src/algorithms/sweep.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use fj_math::{Scalar, Segment, Transform, Triangle, Vector}; +use fj_math::{Scalar, Transform, Triangle, Vector}; use crate::{ geometry::{Surface, SweptCurve}, @@ -138,9 +138,7 @@ pub fn sweep_shape( let approx = CycleApprox::new(&cycle_source.get(), tolerance); let mut quads = Vec::new(); - for segment in approx.points.windows(2) { - let segment = Segment::from_points([segment[0], segment[1]]); - + for segment in approx.segments() { let [v0, v1] = segment.points(); let [v3, v2] = { let segment = Transform::translation(path) From d3eafab6b2f112e9f0107d96f821bed0456d687e Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Mon, 4 Apr 2022 16:49:54 +0200 Subject: [PATCH 5/5] Use `CycleApprox` in `FaceApprox` --- fj-kernel/src/algorithms/approximation.rs | 31 ++++++++--------------- fj-kernel/src/algorithms/triangulation.rs | 4 ++- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/fj-kernel/src/algorithms/approximation.rs b/fj-kernel/src/algorithms/approximation.rs index 1c2b66bfe..3b3c90b42 100644 --- a/fj-kernel/src/algorithms/approximation.rs +++ b/fj-kernel/src/algorithms/approximation.rs @@ -13,14 +13,8 @@ pub struct FaceApprox { /// an edge, or points that approximate a face. pub points: HashSet>, - /// Segments that approximate edges - /// - /// Every approximation will involve edges, typically, and these are - /// approximated by these segments. - /// - /// All the points of these segments will also be available in the `points` - /// field of this struct. - pub segments: HashSet>, + /// The approximation of the face's cycles + pub cycles: HashSet, } impl FaceApprox { @@ -43,16 +37,16 @@ impl FaceApprox { // it have nothing to do with its curvature. let mut points = HashSet::new(); - let mut segments = HashSet::new(); + let mut cycles = HashSet::new(); for cycle in face.all_cycles() { let cycle = CycleApprox::new(&cycle, tolerance); - segments.extend(cycle.segments()); - points.extend(cycle.points); + points.extend(cycle.points.iter().copied()); + cycles.insert(cycle); } - Self { points, segments } + Self { points, cycles } } } @@ -131,7 +125,7 @@ fn approximate_edge( #[cfg(test)] mod tests { - use fj_math::{Point, Scalar, Segment}; + use fj_math::{Point, Scalar}; use map_macro::set; use crate::{ @@ -140,7 +134,7 @@ mod tests { topology::{Face, Vertex}, }; - use super::FaceApprox; + use super::{CycleApprox, FaceApprox}; #[test] fn approximate_edge() -> anyhow::Result<()> { @@ -187,12 +181,9 @@ mod tests { FaceApprox::new(&face.get(), tolerance), FaceApprox { points: set![a, b, c, d], - segments: set![ - Segment::from([a, b]), - Segment::from([b, c]), - Segment::from([c, d]), - Segment::from([d, a]), - ], + cycles: set![CycleApprox { + points: vec![a, b, c, d, a], + }], } ); diff --git a/fj-kernel/src/algorithms/triangulation.rs b/fj-kernel/src/algorithms/triangulation.rs index 076a71d39..f2d2d1182 100644 --- a/fj-kernel/src/algorithms/triangulation.rs +++ b/fj-kernel/src/algorithms/triangulation.rs @@ -38,8 +38,10 @@ pub fn triangulate( .collect(); let segments: Vec<_> = approx - .segments + .cycles .into_iter() + .map(|cycle| cycle.segments()) + .flatten() .map(|segment| { let [a, b] = segment.points();