diff --git a/crates/fj-app/src/args.rs b/crates/fj-app/src/args.rs index d72c7d1ef..b0828cb79 100644 --- a/crates/fj-app/src/args.rs +++ b/crates/fj-app/src/args.rs @@ -2,7 +2,7 @@ use std::{path::PathBuf, str::FromStr as _}; use anyhow::anyhow; use fj_host::Parameters; -use fj_kernel::algorithms::Tolerance; +use fj_kernel::algorithms::approx::Tolerance; use fj_math::Scalar; /// Fornjot - Experimental CAD System diff --git a/crates/fj-kernel/src/algorithms/approx/curves.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs similarity index 63% rename from crates/fj-kernel/src/algorithms/approx/curves.rs rename to crates/fj-kernel/src/algorithms/approx/curve.rs index 33f763442..34c0a9cde 100644 --- a/crates/fj-kernel/src/algorithms/approx/curves.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -2,36 +2,46 @@ use std::cmp::max; use fj_math::{Circle, Point, Scalar}; -use crate::objects::{CurveKind, GlobalCurve}; +use crate::objects::{Curve, CurveKind, GlobalCurve}; -use super::{Local, Tolerance}; +use super::{Approx, Local, Tolerance}; -/// Compute an approximation of the curve -/// -/// `tolerance` defines how far the approximation is allowed to deviate from the -/// actual edge. -/// -/// # Implementation Note -/// -/// This only works as it is, because edges are severely limited and don't -/// define which section of the curve they inhabit. Once they do that, we need -/// an `approximate_between(a, b)` method instead, where `a` and `b` are the -/// vertices that bound the edge on the curve. -/// -/// The `approximate_between` methods of the curves then need to make sure to -/// only return points in between those vertices, not the vertices themselves. -pub fn approx_curve( - curve: &GlobalCurve, - tolerance: Tolerance, - out: &mut Vec>>, -) { - match curve.kind() { - CurveKind::Circle(curve) => approx_circle(curve, tolerance, out), - CurveKind::Line(_) => {} +impl Approx for Curve { + type Approximation = Vec>>; + + fn approx(&self, tolerance: Tolerance) -> Self::Approximation { + self.global().approx(tolerance) + } +} + +impl Approx for GlobalCurve { + type Approximation = Vec>>; + + /// Approximate the global curve + /// + /// # Implementation Note + /// + /// This only works as-is, because only circles need to be approximated + /// right now and because only edges that are full circles are supported, as + /// opposed to edges that only inhabit part of the circle. + /// + /// To support that, we will need additional information here, to define + /// between which points the curve needs to be approximated. + fn approx(&self, tolerance: Tolerance) -> Self::Approximation { + let mut points = Vec::new(); + + match self.kind() { + CurveKind::Circle(curve) => { + approx_circle(curve, tolerance, &mut points) + } + CurveKind::Line(_) => {} + } + + points } } -/// Approximate the circle +/// Approximate a circle /// /// `tolerance` specifies how much the approximation is allowed to deviate /// from the circle. @@ -69,7 +79,7 @@ fn number_of_vertices_for_circle(tolerance: Tolerance, radius: Scalar) -> u64 { mod tests { use fj_math::Scalar; - use crate::algorithms::Tolerance; + use crate::algorithms::approx::Tolerance; #[test] fn number_of_vertices_for_circle() { diff --git a/crates/fj-kernel/src/algorithms/approx/cycles.rs b/crates/fj-kernel/src/algorithms/approx/cycle.rs similarity index 71% rename from crates/fj-kernel/src/algorithms/approx/cycles.rs rename to crates/fj-kernel/src/algorithms/approx/cycle.rs index 4a3ec9a6c..727b77699 100644 --- a/crates/fj-kernel/src/algorithms/approx/cycles.rs +++ b/crates/fj-kernel/src/algorithms/approx/cycle.rs @@ -2,27 +2,16 @@ use fj_math::{Point, Segment}; use crate::objects::Cycle; -use super::{curves::approx_curve, edges::approx_edge, Local, Tolerance}; +use super::{Approx, Local, Tolerance}; -/// An approximation of a [`Cycle`] -#[derive(Debug, Eq, PartialEq, Hash)] -pub struct CycleApprox { - /// The points that approximate the cycle - pub points: Vec>>, -} +impl Approx for Cycle { + type Approximation = CycleApprox; -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: Tolerance) -> Self { + fn approx(&self, tolerance: Tolerance) -> Self::Approximation { let mut points = Vec::new(); - for edge in cycle.edges() { - let mut edge_points = Vec::new(); - approx_curve(edge.curve().global(), tolerance, &mut edge_points); - approx_edge(*edge.vertices(), &mut edge_points); + for edge in self.edges() { + let edge_points = edge.approx(tolerance); points.extend(edge_points.into_iter().map(|point| { let local = edge @@ -37,9 +26,18 @@ impl CycleApprox { // could lead to subtly different surface coordinates. points.dedup_by(|a, b| a.global_form() == b.global_form()); - Self { points } + CycleApprox { points } } +} +/// An approximation of a [`Cycle`] +#[derive(Debug, Eq, PartialEq, Hash)] +pub struct CycleApprox { + /// The points that approximate the cycle + pub points: Vec>>, +} + +impl CycleApprox { /// Construct the segments that approximate the cycle pub fn segments(&self) -> Vec> { let mut segments = Vec::new(); diff --git a/crates/fj-kernel/src/algorithms/approx/edges.rs b/crates/fj-kernel/src/algorithms/approx/edge.rs similarity index 86% rename from crates/fj-kernel/src/algorithms/approx/edges.rs rename to crates/fj-kernel/src/algorithms/approx/edge.rs index 13c440311..c632c967a 100644 --- a/crates/fj-kernel/src/algorithms/approx/edges.rs +++ b/crates/fj-kernel/src/algorithms/approx/edge.rs @@ -1,8 +1,19 @@ use fj_math::Point; -use crate::objects::{Vertex, VerticesOfEdge}; +use crate::objects::{Edge, Vertex, VerticesOfEdge}; -use super::Local; +use super::{Approx, Local}; + +impl Approx for Edge { + type Approximation = Vec>>; + + fn approx(&self, tolerance: super::Tolerance) -> Self::Approximation { + let mut points = self.curve().approx(tolerance); + approx_edge(*self.vertices(), &mut points); + + points + } +} pub fn approx_edge( vertices: VerticesOfEdge, diff --git a/crates/fj-kernel/src/algorithms/approx/faces.rs b/crates/fj-kernel/src/algorithms/approx/face.rs similarity index 86% rename from crates/fj-kernel/src/algorithms/approx/faces.rs rename to crates/fj-kernel/src/algorithms/approx/face.rs index 602b1c702..a88aeee77 100644 --- a/crates/fj-kernel/src/algorithms/approx/faces.rs +++ b/crates/fj-kernel/src/algorithms/approx/face.rs @@ -4,30 +4,12 @@ use fj_math::Point; use crate::objects::Face; -use super::{CycleApprox, Local, Tolerance}; +use super::{Approx, CycleApprox, Local, Tolerance}; -/// An approximation of a [`Face`] -#[derive(Debug, Eq, PartialEq)] -pub struct FaceApprox { - /// All points that make up the approximation - /// - /// These could be actual vertices from the model, points that approximate - /// an edge, or points that approximate a face. - pub points: HashSet>>, +impl Approx for Face { + type Approximation = FaceApprox; - /// Approximation of the exterior cycle - pub exterior: CycleApprox, - - /// Approximations of the interior cycles - pub interiors: HashSet, -} - -impl FaceApprox { - /// Compute the approximation of a face - /// - /// `tolerance` defines how far the approximation is allowed to deviate from - /// the actual face. - pub fn new(face: &Face, tolerance: Tolerance) -> Self { + fn approx(&self, tolerance: Tolerance) -> 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`. @@ -45,14 +27,14 @@ impl FaceApprox { let mut exteriors = Vec::new(); let mut interiors = HashSet::new(); - for cycle in face.exteriors() { - let cycle = CycleApprox::new(cycle, tolerance); + for cycle in self.exteriors() { + let cycle = cycle.approx(tolerance); points.extend(cycle.points.iter().copied()); exteriors.push(cycle); } - for cycle in face.interiors() { - let cycle = CycleApprox::new(cycle, tolerance); + for cycle in self.interiors() { + let cycle = cycle.approx(tolerance); points.extend(cycle.points.iter().copied()); interiors.insert(cycle); @@ -70,7 +52,7 @@ impl FaceApprox { "Approximation only supports faces with one exterior cycle", ); - Self { + FaceApprox { points, exterior, interiors, @@ -78,13 +60,29 @@ impl FaceApprox { } } +/// An approximation of a [`Face`] +#[derive(Debug, Eq, PartialEq)] +pub struct FaceApprox { + /// All points that make up the approximation + /// + /// These could be actual vertices from the model, points that approximate + /// an edge, or points that approximate a face. + pub points: HashSet>>, + + /// Approximation of the exterior cycle + pub exterior: CycleApprox, + + /// Approximations of the interior cycles + pub interiors: HashSet, +} + #[cfg(test)] mod tests { use fj_math::{Point, Scalar}; use map_macro::set; use crate::{ - algorithms::approx::Local, + algorithms::approx::{Approx, Local}, objects::{Face, Surface}, }; @@ -120,7 +118,7 @@ mod tests { let g = Local::new(g, g.to_xyz()); let h = Local::new(h, h.to_xyz()); - let approx = FaceApprox::new(&face, tolerance); + let approx = face.approx(tolerance); let expected = FaceApprox { points: set![a, b, c, d, e, f, g, h], exterior: CycleApprox { diff --git a/crates/fj-kernel/src/algorithms/approx/mod.rs b/crates/fj-kernel/src/algorithms/approx/mod.rs index e17e84a51..3e389255f 100644 --- a/crates/fj-kernel/src/algorithms/approx/mod.rs +++ b/crates/fj-kernel/src/algorithms/approx/mod.rs @@ -1,13 +1,27 @@ -mod curves; -mod cycles; -mod edges; -mod faces; +//! Approximation of objects + +mod curve; +mod cycle; +mod edge; +mod face; mod local; mod tolerance; pub use self::{ - cycles::CycleApprox, - faces::FaceApprox, + cycle::CycleApprox, + face::FaceApprox, local::{Local, LocalForm}, tolerance::{InvalidTolerance, Tolerance}, }; + +/// Approximate an object +pub trait Approx { + /// 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; +} diff --git a/crates/fj-kernel/src/algorithms/mod.rs b/crates/fj-kernel/src/algorithms/mod.rs index 2ff51f54d..14333948a 100644 --- a/crates/fj-kernel/src/algorithms/mod.rs +++ b/crates/fj-kernel/src/algorithms/mod.rs @@ -3,16 +3,15 @@ //! Algorithmic code is collected in this module, to keep other modules focused //! on their respective purpose. -mod approx; mod reverse; mod transform; mod triangulate; +pub mod approx; pub mod intersect; pub mod sweep; pub use self::{ - approx::{CycleApprox, FaceApprox, InvalidTolerance, Tolerance}, reverse::reverse_face, transform::{transform_faces, TransformObject}, triangulate::triangulate, diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index ff9930281..21ce2e32b 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -2,7 +2,7 @@ use fj_interop::mesh::Color; use fj_math::{Point, Transform, Triangle}; use crate::{ - algorithms::{CycleApprox, Tolerance}, + algorithms::approx::{Approx, Tolerance}, objects::{ Curve, CurveKind, Cycle, Edge, Face, GlobalCurve, GlobalVertex, Surface, Vertex, VerticesOfEdge, @@ -119,7 +119,7 @@ fn create_continuous_side_face( let placeholder = Surface::xy_plane(); let cycle = Cycle::new(placeholder).with_edges([edge]); - let approx = CycleApprox::new(&cycle, tolerance); + let approx = cycle.approx(tolerance); let mut quads = Vec::new(); for segment in approx.segments() { diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index dccca595d..e6f412a50 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -1,7 +1,7 @@ use fj_interop::mesh::Color; use crate::{ - algorithms::{reverse_face, Tolerance, TransformObject}, + algorithms::{approx::Tolerance, reverse_face, TransformObject}, objects::{Face, Shell}, }; diff --git a/crates/fj-kernel/src/algorithms/sweep/mod.rs b/crates/fj-kernel/src/algorithms/sweep/mod.rs index 4ced4d0df..746bba07a 100644 --- a/crates/fj-kernel/src/algorithms/sweep/mod.rs +++ b/crates/fj-kernel/src/algorithms/sweep/mod.rs @@ -7,7 +7,7 @@ mod sketch; use fj_interop::mesh::Color; use fj_math::{Scalar, Vector}; -use super::Tolerance; +use super::approx::Tolerance; /// Sweep an object along a path to create another object pub trait Sweep { diff --git a/crates/fj-kernel/src/algorithms/sweep/sketch.rs b/crates/fj-kernel/src/algorithms/sweep/sketch.rs index cf10fe25c..fa1986d2b 100644 --- a/crates/fj-kernel/src/algorithms/sweep/sketch.rs +++ b/crates/fj-kernel/src/algorithms/sweep/sketch.rs @@ -1,7 +1,7 @@ use fj_interop::mesh::Color; use crate::{ - algorithms::Tolerance, + algorithms::approx::Tolerance, objects::{Sketch, Solid}, }; @@ -35,7 +35,7 @@ mod tests { use fj_math::{Point, Scalar, Vector}; use crate::{ - algorithms::Tolerance, + algorithms::approx::Tolerance, iter::ObjectIters, objects::{Face, Sketch, Surface}, }; diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 27ddb01e8..3eed06237 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -8,7 +8,7 @@ use crate::objects::Face; use self::polygon::Polygon; -use super::{FaceApprox, Tolerance}; +use super::approx::{Approx, Tolerance}; /// Triangulate a shape pub fn triangulate( @@ -27,7 +27,7 @@ pub fn triangulate( } let surface = face.surface(); - let approx = FaceApprox::new(&face, tolerance); + let approx = face.approx(tolerance); let points: Vec<_> = approx.points.into_iter().collect(); let face_as_polygon = Polygon::new(*surface) @@ -65,7 +65,7 @@ mod tests { use fj_math::{Point, Scalar}; use crate::{ - algorithms::Tolerance, + algorithms::approx::Tolerance, objects::{Face, Surface}, }; diff --git a/crates/fj-operations/src/difference_2d.rs b/crates/fj-operations/src/difference_2d.rs index ade0640e9..d00c0af77 100644 --- a/crates/fj-operations/src/difference_2d.rs +++ b/crates/fj-operations/src/difference_2d.rs @@ -1,6 +1,6 @@ use fj_interop::{debug::DebugInfo, mesh::Color}; use fj_kernel::{ - algorithms::Tolerance, + algorithms::approx::Tolerance, iter::ObjectIters, objects::{Curve, Cycle, Edge, Face, GlobalCurve, Sketch}, validation::{validate, Validated, ValidationConfig, ValidationError}, diff --git a/crates/fj-operations/src/group.rs b/crates/fj-operations/src/group.rs index 610f1ec77..ebc91725b 100644 --- a/crates/fj-operations/src/group.rs +++ b/crates/fj-operations/src/group.rs @@ -1,6 +1,6 @@ use fj_interop::debug::DebugInfo; use fj_kernel::{ - algorithms::Tolerance, + algorithms::approx::Tolerance, objects::Face, validation::{validate, Validated, ValidationConfig, ValidationError}, }; diff --git a/crates/fj-operations/src/lib.rs b/crates/fj-operations/src/lib.rs index da6d659fc..4aea443fd 100644 --- a/crates/fj-operations/src/lib.rs +++ b/crates/fj-operations/src/lib.rs @@ -26,7 +26,7 @@ mod transform; use fj_interop::debug::DebugInfo; use fj_kernel::{ - algorithms::Tolerance, + algorithms::approx::Tolerance, objects::{Face, Sketch}, validation::{validate, Validated, ValidationConfig, ValidationError}, }; diff --git a/crates/fj-operations/src/shape_processor.rs b/crates/fj-operations/src/shape_processor.rs index 3ea3f3777..6e3ecbcc2 100644 --- a/crates/fj-operations/src/shape_processor.rs +++ b/crates/fj-operations/src/shape_processor.rs @@ -2,7 +2,10 @@ use fj_interop::{debug::DebugInfo, processed_shape::ProcessedShape}; use fj_kernel::{ - algorithms::{triangulate, InvalidTolerance, Tolerance}, + algorithms::{ + approx::{InvalidTolerance, Tolerance}, + triangulate, + }, validation::{ValidationConfig, ValidationError}, }; use fj_math::Scalar; diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index 50c028df7..d0c535254 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -1,6 +1,6 @@ use fj_interop::{debug::DebugInfo, mesh::Color}; use fj_kernel::{ - algorithms::Tolerance, + algorithms::approx::Tolerance, objects::{Cycle, Edge, Face, Sketch, Surface}, validation::{validate, Validated, ValidationConfig, ValidationError}, }; diff --git a/crates/fj-operations/src/sweep.rs b/crates/fj-operations/src/sweep.rs index 828ad0702..754e01c6d 100644 --- a/crates/fj-operations/src/sweep.rs +++ b/crates/fj-operations/src/sweep.rs @@ -1,6 +1,6 @@ use fj_interop::{debug::DebugInfo, mesh::Color}; use fj_kernel::{ - algorithms::{sweep::Sweep, Tolerance}, + algorithms::{approx::Tolerance, sweep::Sweep}, objects::Solid, validation::{validate, Validated, ValidationConfig, ValidationError}, }; diff --git a/crates/fj-operations/src/transform.rs b/crates/fj-operations/src/transform.rs index 6d4c2998d..e999859f2 100644 --- a/crates/fj-operations/src/transform.rs +++ b/crates/fj-operations/src/transform.rs @@ -1,6 +1,6 @@ use fj_interop::debug::DebugInfo; use fj_kernel::{ - algorithms::{transform_faces, Tolerance}, + algorithms::{approx::Tolerance, transform_faces}, objects::Face, validation::{validate, Validated, ValidationConfig, ValidationError}, };