diff --git a/crates/fj-kernel/src/algorithms/approx/curve.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs index 33a99aa19..783729e23 100644 --- a/crates/fj-kernel/src/algorithms/approx/curve.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -7,7 +7,7 @@ use crate::objects::{Curve, CurveKind, GlobalCurve}; use super::{Approx, Tolerance}; impl Approx for Curve { - type Approximation = Vec<(Point<1>, Point<3>)>; + type Approximation = Vec<(Point<2>, Point<3>)>; type Params = RangeOnCurve; fn approx( @@ -15,7 +15,15 @@ impl Approx for Curve { tolerance: Tolerance, range: Self::Params, ) -> Self::Approximation { - self.global().approx(tolerance, range) + self.global() + .approx(tolerance, range) + .into_iter() + .map(|(point_curve, point_global)| { + let point_surface = + self.kind().point_from_curve_coords(point_curve); + (point_surface, point_global) + }) + .collect() } } @@ -30,7 +38,7 @@ impl Approx for GlobalCurve { ) -> Self::Approximation { match self.kind() { CurveKind::Circle(curve) => approx_circle(curve, range, tolerance), - CurveKind::Line(_) => Vec::new(), + CurveKind::Line(_) => vec![range.start()], } } } @@ -56,9 +64,10 @@ fn approx_circle( let n = number_of_vertices_for_circle(tolerance, radius, range.length()); let mut points = Vec::new(); + points.push(range.start()); - for i in 0..n { - let angle = range.start().t + for i in 1..n { + let angle = range.start().0.t + (Scalar::TAU / n as f64 * i as f64) * range.direction(); let point_curve = Point::from([angle]); @@ -83,20 +92,20 @@ fn number_of_vertices_for_circle( } pub struct RangeOnCurve { - pub boundary: [Point<1>; 2], + pub boundary: [(Point<1>, Point<3>); 2], } impl RangeOnCurve { - fn start(&self) -> Point<1> { + fn start(&self) -> (Point<1>, Point<3>) { self.boundary[0] } - fn end(&self) -> Point<1> { + fn end(&self) -> (Point<1>, Point<3>) { self.boundary[1] } fn signed_length(&self) -> Scalar { - (self.end() - self.start()).t + (self.end().0 - self.start().0).t } fn length(&self) -> Scalar { diff --git a/crates/fj-kernel/src/algorithms/approx/cycle.rs b/crates/fj-kernel/src/algorithms/approx/cycle.rs index 855e4102e..6c373609c 100644 --- a/crates/fj-kernel/src/algorithms/approx/cycle.rs +++ b/crates/fj-kernel/src/algorithms/approx/cycle.rs @@ -17,19 +17,12 @@ impl Approx for Cycle { for edge in self.edges() { let edge_points = edge.approx(tolerance, ()); - - points.extend(edge_points.into_iter().map(|point| { - let (point_curve, point_global) = point; - - let point_surface = - edge.curve().kind().point_from_curve_coords(point_curve); - (point_surface, point_global) - })); + points.extend(edge_points); } - // Can't just rely on `dedup`, as the conversion from curve coordinates - // could lead to subtly different surface coordinates. - points.dedup_by(|(_, a), (_, b)| a == b); + if let Some(&point) = points.first() { + points.push(point); + } CycleApprox { points } } diff --git a/crates/fj-kernel/src/algorithms/approx/edge.rs b/crates/fj-kernel/src/algorithms/approx/edge.rs index f49132bcc..cf76001e0 100644 --- a/crates/fj-kernel/src/algorithms/approx/edge.rs +++ b/crates/fj-kernel/src/algorithms/approx/edge.rs @@ -1,11 +1,11 @@ use fj_math::{Point, Scalar}; -use crate::objects::{Edge, Vertex, VerticesOfEdge}; +use crate::objects::Edge; use super::{curve::RangeOnCurve, Approx}; impl Approx for Edge { - type Approximation = Vec<(Point<1>, Point<3>)>; + type Approximation = Vec<(Point<2>, Point<3>)>; type Params = (); fn approx( @@ -13,82 +13,27 @@ impl Approx for Edge { tolerance: super::Tolerance, (): Self::Params, ) -> Self::Approximation { - let mut points = self.curve().approx( - tolerance, - // The range is only used for circles right now. - RangeOnCurve { - boundary: [[Scalar::ZERO].into(), [Scalar::TAU].into()], - }, - ); - approx_edge(*self.vertices(), &mut points); - - points - } -} - -pub fn approx_edge( - vertices: VerticesOfEdge, - points: &mut Vec<(Point<1>, Point<3>)>, -) { - // Insert the exact vertices of this edge into the approximation. This means - // we don't rely on the curve approximation to deliver accurate - // representations of these vertices, which they might not be able to do. - // - // If we used inaccurate representations of those vertices here, then that - // would lead to bugs in the approximation, as points that should refer to - // the same vertex would be understood to refer to very close, but distinct - // vertices. - let vertices = vertices - .convert(|vertex| (vertex.position(), vertex.global().position())); - if let Some([a, b]) = vertices { - points.insert(0, a); - points.push(b); - } - - if 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() { - points.push(point); - } - } -} - -#[cfg(test)] -mod test { - use fj_math::Point; - - use crate::objects::{GlobalVertex, Vertex, VerticesOfEdge}; - - #[test] - fn approx_edge() { - let a = Point::from([1., 2., 3.]); - let b = Point::from([2., 3., 5.]); - let c = Point::from([3., 5., 8.]); - let d = Point::from([5., 8., 13.]); - - let v1 = GlobalVertex::from_position(a); - let v2 = GlobalVertex::from_position(d); + // The range is only used for circles right now. + let range = { + let start_curve = Point::from([Scalar::ZERO]); + let end_curve = Point::from([Scalar::TAU]); + + // We're dealing with a circle here. Start and end are identical + // points, in global coordinates. + let point_global = self + .global() + .curve() + .kind() + .point_from_curve_coords(start_curve); - let vertices = VerticesOfEdge::from_vertices([ - Vertex::new(Point::from([0.]), v1), - Vertex::new(Point::from([1.]), v2), - ]); - - let a = (Point::from([0.0]), a); - let b = (Point::from([0.25]), b); - let c = (Point::from([0.75]), c); - let d = (Point::from([1.0]), d); - - // Regular edge - let mut points = vec![b, c]; - super::approx_edge(vertices, &mut points); - assert_eq!(points, vec![a, b, c, d]); - - // Continuous edge - let mut points = vec![b, c]; - super::approx_edge(VerticesOfEdge::none(), &mut points); - assert_eq!(points, vec![b, c, b]); + RangeOnCurve { + boundary: [ + (start_curve, point_global), + (end_curve, point_global), + ], + } + }; + + self.curve().approx(tolerance, range) } }