diff --git a/src/kernel/approximation.rs b/src/kernel/approximation.rs index dd677d8e4..886f3f389 100644 --- a/src/kernel/approximation.rs +++ b/src/kernel/approximation.rs @@ -5,7 +5,10 @@ use parry3d_f64::shape::Segment; use crate::math::Point; +use super::topology::edges::Edge; + /// An approximation of an edge, multiple edges, or a face +#[derive(Debug, PartialEq)] pub struct Approximation { /// All points that make up the approximation /// @@ -25,6 +28,39 @@ pub struct Approximation { } impl Approximation { + /// Compute an approximate for an edge + /// + /// `tolerance` defines how far the approximation is allowed to deviate from + /// the actual edge. + pub fn for_edge(edge: &Edge, tolerance: f64) -> Self { + let mut points = Vec::new(); + edge.curve.approx(tolerance, &mut points); + + if edge.reverse { + points.reverse() + } + + let mut segment_points = points.clone(); + if edge.vertices.is_none() { + // The edge has no vertices, which means it connects to itself. We + // need to reflect that in the approximation. + + if let Some(&point) = points.first() { + segment_points.push(point); + } + } + + let mut segments = Vec::new(); + for segment in segment_points.windows(2) { + let p0 = segment[0]; + let p1 = segment[1]; + + segments.push([p0, p1].into()); + } + + Self { points, segments } + } + /// Validate the approximation /// /// Returns an `Err(ValidationError)`, if the validation is not valid. See @@ -111,8 +147,59 @@ mod tests { use nalgebra::point; use parry3d_f64::shape::Segment; + use crate::kernel::{geometry::Curve, topology::edges::Edge}; + use super::Approximation; + #[test] + fn test_for_edge() { + let tolerance = 1.; + + let a = point![1., 2., 3.]; + let b = point![3., 5., 8.]; + + let curve = Curve::Mock { approx: vec![a, b] }; + + let edge_regular = Edge { + curve: curve.clone(), + vertices: Some([(), ()]), + reverse: false, + }; + assert_eq!( + Approximation::for_edge(&edge_regular, tolerance), + Approximation { + points: vec![a, b], + segments: vec![Segment { a, b }], + } + ); + + let edge_self_connected = Edge { + curve: curve.clone(), + vertices: None, + reverse: false, + }; + assert_eq!( + Approximation::for_edge(&edge_self_connected, tolerance), + Approximation { + points: vec![a, b], + segments: vec![Segment { a, b }, Segment { a: b, b: a }], + } + ); + + let edge_reversed = Edge { + curve: curve.clone(), + vertices: Some([(), ()]), + reverse: true, + }; + assert_eq!( + Approximation::for_edge(&edge_reversed, tolerance), + Approximation { + points: vec![b, a], + segments: vec![Segment { a: b, b: a }], + } + ); + } + #[test] fn test_validate() { let a = point![0., 1., 2.]; diff --git a/src/kernel/geometry/curves/mod.rs b/src/kernel/geometry/curves/mod.rs index e2cd128a0..e332bc096 100644 --- a/src/kernel/geometry/curves/mod.rs +++ b/src/kernel/geometry/curves/mod.rs @@ -19,13 +19,17 @@ use crate::math::Point; /// /// This distinction is not observed here, but moving things into that direction /// is the intention. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub enum Curve { /// A circle Circle(Circle), /// A line Line(Line), + + /// A mock curve used for testing + #[cfg(test)] + Mock { approx: Vec> }, } impl Curve { @@ -34,6 +38,9 @@ impl Curve { match self { Self::Circle(circle) => Self::Circle(circle.transform(transform)), Self::Line(line) => Self::Line(line.transform(transform)), + + #[cfg(test)] + Self::Mock { .. } => todo!(), } } @@ -57,6 +64,9 @@ impl Curve { match self { Self::Circle(circle) => circle.approx(tolerance, out), Self::Line(Line { a, b }) => out.extend([*a, *b]), + + #[cfg(test)] + Self::Mock { approx } => out.extend(approx), } } } diff --git a/src/kernel/topology/edges.rs b/src/kernel/topology/edges.rs index 8f94bd1c0..e49022e6e 100644 --- a/src/kernel/topology/edges.rs +++ b/src/kernel/topology/edges.rs @@ -37,7 +37,7 @@ impl Edges { pub fn transform(mut self, transform: &Isometry) -> Self { for cycle in &mut self.cycles { for edge in &mut cycle.edges { - edge.curve = edge.curve.transform(transform); + edge.curve = edge.curve.clone().transform(transform); } } @@ -100,7 +100,7 @@ impl Cycle { let mut segments = Vec::new(); for edge in &self.edges { - let approx = edge.approx(tolerance); + let approx = Approximation::for_edge(&edge, tolerance); points.extend(approx.points); segments.extend(approx.segments); @@ -170,37 +170,4 @@ impl Edge { pub fn reverse(&mut self) { self.reverse = !self.reverse; } - - /// Compute an approximation of the edge - /// - /// `tolerance` defines how far the approximation is allowed to deviate from - /// the actual edge. - pub fn approx(&self, tolerance: f64) -> Approximation { - let mut points = Vec::new(); - self.curve.approx(tolerance, &mut points); - - if self.reverse { - points.reverse() - } - - let mut segment_vertices = points.clone(); - if self.vertices.is_none() { - // The edge has no vertices, which means it connects to itself. We - // need to reflect that in the approximation. - - if let Some(&vertex) = points.first() { - segment_vertices.push(vertex); - } - } - - let mut segments = Vec::new(); - for segment in segment_vertices.windows(2) { - let v0 = segment[0]; - let v1 = segment[1]; - - segments.push([v0, v1].into()); - } - - Approximation { points, segments } - } }