diff --git a/crates/fj-kernel/src/algorithms/approx/faces.rs b/crates/fj-kernel/src/algorithms/approx/faces.rs index 9bcefddd0..c296dd123 100644 --- a/crates/fj-kernel/src/algorithms/approx/faces.rs +++ b/crates/fj-kernel/src/algorithms/approx/faces.rs @@ -46,13 +46,13 @@ impl FaceApprox { let mut interiors = HashSet::new(); for cycle in face.exteriors() { - let cycle = CycleApprox::new(&cycle, tolerance); + let cycle = CycleApprox::new(cycle, tolerance); points.extend(cycle.points.iter().copied()); exteriors.push(cycle); } for cycle in face.interiors() { - let cycle = CycleApprox::new(&cycle, tolerance); + let cycle = CycleApprox::new(cycle, tolerance); points.extend(cycle.points.iter().copied()); interiors.insert(cycle); diff --git a/crates/fj-kernel/src/algorithms/reverse.rs b/crates/fj-kernel/src/algorithms/reverse.rs index 46ddd27d5..d6dfa0ba7 100644 --- a/crates/fj-kernel/src/algorithms/reverse.rs +++ b/crates/fj-kernel/src/algorithms/reverse.rs @@ -2,30 +2,27 @@ use fj_math::{Circle, Line, Point, Vector}; use crate::{ local::Local, - objects::{Curve, Cycle, CyclesInFace, Edge, Face}, + objects::{Curve, Cycle, Edge, Face}, }; /// Reverse the direction of a face pub fn reverse_face(face: &Face) -> Face { - let face = match face { - Face::Face(face) => face, - Face::Triangles(_) => { - panic!("Reversing tri-rep faces is not supported") - } - }; + if face.triangles().is_some() { + panic!("Reversing tri-rep faces is not supported"); + } let surface = face.surface().reverse(); - let exteriors = reverse_local_coordinates_in_cycle(&face.exteriors); - let interiors = reverse_local_coordinates_in_cycle(&face.interiors); + let exteriors = reverse_local_coordinates_in_cycle(face.exteriors()); + let interiors = reverse_local_coordinates_in_cycle(face.interiors()); - Face::new(surface, exteriors, interiors, face.color) + Face::new(surface, exteriors, interiors, face.color()) } -fn reverse_local_coordinates_in_cycle( - cycles: &CyclesInFace, -) -> impl Iterator + '_ { - let cycles = cycles.as_local().map(|cycle| { +fn reverse_local_coordinates_in_cycle<'r>( + cycles: impl IntoIterator + 'r, +) -> impl Iterator + 'r { + cycles.into_iter().map(|cycle| { let edges = cycle .edges .iter() @@ -57,9 +54,7 @@ fn reverse_local_coordinates_in_cycle( .collect(); Cycle { edges } - }); - - cycles + }) } #[cfg(test)] diff --git a/crates/fj-kernel/src/algorithms/sweep.rs b/crates/fj-kernel/src/algorithms/sweep.rs index 2e0d1c752..2563ef24e 100644 --- a/crates/fj-kernel/src/algorithms/sweep.rs +++ b/crates/fj-kernel/src/algorithms/sweep.rs @@ -39,7 +39,7 @@ pub fn sweep( ); for cycle in face.all_cycles() { - for edge in cycle.edges { + for edge in &cycle.edges { if let Some(vertices) = edge.vertices().get() { create_non_continuous_side_face( path, @@ -52,7 +52,7 @@ pub fn sweep( } create_continuous_side_face( - edge, + *edge, path, tolerance, color, @@ -193,7 +193,7 @@ fn create_continuous_side_face( side_face.push(([v0, v2, v3].into(), color)); } - target.push(Face::Triangles(side_face)); + target.push(Face::from_triangles(side_face)); } #[cfg(test)] diff --git a/crates/fj-kernel/src/algorithms/transform.rs b/crates/fj-kernel/src/algorithms/transform.rs index 6a22e8268..fa304c20f 100644 --- a/crates/fj-kernel/src/algorithms/transform.rs +++ b/crates/fj-kernel/src/algorithms/transform.rs @@ -3,8 +3,7 @@ use fj_math::{Transform, Vector}; use crate::{ local::Local, objects::{ - Curve, Cycle, CyclesInFace, Edge, Face, FaceBRep, GlobalVertex, Sketch, - Solid, Surface, Vertex, + Curve, Cycle, Edge, Face, GlobalVertex, Sketch, Solid, Surface, Vertex, }, }; @@ -72,33 +71,25 @@ impl TransformObject for Edge { impl TransformObject for Face { fn transform(self, transform: &Transform) -> Self { - match self { - Self::Face(face) => { - let surface = face.surface.transform(transform); + if let Some(triangles) = self.triangles() { + let mut target = Vec::new(); - let exteriors = transform_cycles(&face.exteriors, transform); - let interiors = transform_cycles(&face.interiors, transform); + for (triangle, color) in triangles.clone() { + let triangle = transform.transform_triangle(&triangle); + target.push((triangle, color)); + } - let color = face.color; + return Self::from_triangles(target); + } - Self::Face(FaceBRep { - surface, - exteriors, - interiors, - color, - }) - } - Self::Triangles(triangles) => { - let mut target = Vec::new(); + let surface = self.surface().transform(transform); - for (triangle, color) in triangles { - let triangle = transform.transform_triangle(&triangle); - target.push((triangle, color)); - } + let exteriors = transform_cycles(self.exteriors(), transform); + let interiors = transform_cycles(self.interiors(), transform); - Self::Triangles(target) - } - } + let color = self.color(); + + Face::new(surface, exteriors, interiors, color) } } @@ -152,11 +143,11 @@ pub fn transform_faces(faces: &mut Vec, transform: &Transform) { } } -fn transform_cycles( - cycles: &CyclesInFace, - transform: &Transform, -) -> CyclesInFace { - let cycles = cycles.as_local().map(|cycle| cycle.transform(transform)); - - CyclesInFace::new(cycles) +fn transform_cycles<'a>( + cycles: impl IntoIterator + 'a, + transform: &'a Transform, +) -> impl Iterator + 'a { + cycles + .into_iter() + .map(|cycle| cycle.clone().transform(transform)) } diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 6e17777df..bb38e73ce 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -20,47 +20,40 @@ pub fn triangulate( let mut mesh = Mesh::new(); for face in faces { - match &face { - Face::Face(brep) => { - let surface = brep.surface; - let approx = FaceApprox::new(&face, tolerance); - - let points: Vec<_> = approx.points.into_iter().collect(); - let face_as_polygon = Polygon::new(surface) - .with_exterior( - approx - .exterior - .points - .into_iter() - .map(|point| point.local()), - ) - .with_interiors(approx.interiors.into_iter().map( - |interior| { - interior - .points - .into_iter() - .map(|point| point.local()) - }, - )); - - let mut triangles = delaunay::triangulate(points); - triangles.retain(|triangle| { - face_as_polygon.contains_triangle( - triangle.map(|point| point.local()), - debug_info, - ) - }); - - for triangle in triangles { - let points = triangle.map(|point| point.global()); - mesh.push_triangle(points, brep.color); - } - } - Face::Triangles(triangles) => { - for &(triangle, color) in triangles { - mesh.push_triangle(triangle.points(), color); - } + if let Some(triangles) = face.triangles() { + for &(triangle, color) in triangles { + mesh.push_triangle(triangle.points(), color); } + continue; + } + + let surface = face.surface(); + let approx = FaceApprox::new(&face, tolerance); + + let points: Vec<_> = approx.points.into_iter().collect(); + let face_as_polygon = Polygon::new(*surface) + .with_exterior( + approx + .exterior + .points + .into_iter() + .map(|point| point.local()), + ) + .with_interiors(approx.interiors.into_iter().map(|interior| { + interior.points.into_iter().map(|point| point.local()) + })); + + let mut triangles = delaunay::triangulate(points); + triangles.retain(|triangle| { + face_as_polygon.contains_triangle( + triangle.map(|point| point.local()), + debug_info, + ) + }); + + for triangle in triangles { + let points = triangle.map(|point| point.global()); + mesh.push_triangle(points, face.color()); } } diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index 95f7507b6..ffb27f783 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -252,45 +252,45 @@ impl ObjectIters for Edge { impl ObjectIters for Face { fn curve_iter(&self) -> Iter> { - if let Face::Face(face) = self { - let mut iter = Iter::empty().with(face.surface().curve_iter()); + if self.triangles().is_some() { + return Iter::empty(); + } - for cycle in face.all_cycles() { - iter = iter.with(cycle.curve_iter()); - } + let mut iter = Iter::empty().with(self.surface().curve_iter()); - return iter; + for cycle in self.all_cycles() { + iter = iter.with(cycle.curve_iter()); } - Iter::empty() + iter } fn cycle_iter(&self) -> Iter { - if let Face::Face(face) = self { - let mut iter = Iter::empty().with(face.surface().cycle_iter()); + if self.triangles().is_some() { + return Iter::empty(); + } - for cycle in face.all_cycles() { - iter = iter.with(cycle.cycle_iter()); - } + let mut iter = Iter::empty().with(self.surface().cycle_iter()); - return iter; + for cycle in self.all_cycles() { + iter = iter.with(cycle.cycle_iter()); } - Iter::empty() + iter } fn edge_iter(&self) -> Iter { - if let Face::Face(face) = self { - let mut iter = Iter::empty().with(face.surface().edge_iter()); + if self.triangles().is_some() { + return Iter::empty(); + } - for cycle in face.all_cycles() { - iter = iter.with(cycle.edge_iter()); - } + let mut iter = Iter::empty().with(self.surface().edge_iter()); - return iter; + for cycle in self.all_cycles() { + iter = iter.with(cycle.edge_iter()); } - Iter::empty() + iter } fn face_iter(&self) -> Iter { @@ -298,74 +298,73 @@ impl ObjectIters for Face { } fn global_vertex_iter(&self) -> Iter { - if let Face::Face(face) = self { - let mut iter = - Iter::empty().with(face.surface().global_vertex_iter()); + if self.triangles().is_some() { + return Iter::empty(); + } - for cycle in face.all_cycles() { - iter = iter.with(cycle.global_vertex_iter()); - } + let mut iter = Iter::empty().with(self.surface().global_vertex_iter()); - return iter; + for cycle in self.all_cycles() { + iter = iter.with(cycle.global_vertex_iter()); } - Iter::empty() + iter } fn sketch_iter(&self) -> Iter { - if let Face::Face(face) = self { - let mut iter = Iter::empty().with(face.surface().sketch_iter()); + if self.triangles().is_some() { + return Iter::empty(); + } - for cycle in face.all_cycles() { - iter = iter.with(cycle.sketch_iter()); - } + let mut iter = Iter::empty().with(self.surface().sketch_iter()); - return iter; + for cycle in self.all_cycles() { + iter = iter.with(cycle.sketch_iter()); } - Iter::empty() + iter } fn solid_iter(&self) -> Iter { - if let Face::Face(face) = self { - let mut iter = Iter::empty().with(face.surface().solid_iter()); + if self.triangles().is_some() { + return Iter::empty(); + } - for cycle in face.all_cycles() { - iter = iter.with(cycle.solid_iter()); - } + let mut iter = Iter::empty().with(self.surface().solid_iter()); - return iter; + for cycle in self.all_cycles() { + iter = iter.with(cycle.solid_iter()); } - Iter::empty() + iter } fn surface_iter(&self) -> Iter { - if let Face::Face(face) = self { - let mut iter = Iter::empty().with(face.surface().surface_iter()); + if self.triangles().is_some() { + return Iter::empty(); + } - for cycle in face.all_cycles() { - iter = iter.with(cycle.surface_iter()); - } + let mut iter = Iter::empty().with(self.surface().surface_iter()); - return iter; + for cycle in self.all_cycles() { + iter = iter.with(cycle.surface_iter()); } - Iter::empty() + iter } fn vertex_iter(&self) -> Iter { - if let Face::Face(face) = self { - let mut iter = Iter::empty().with(face.surface().vertex_iter()); + if self.triangles().is_some() { + return Iter::empty(); + } - for cycle in face.all_cycles() { - iter = iter.with(cycle.vertex_iter()); - } + let mut iter = Iter::empty().with(self.surface().vertex_iter()); - return iter; + for cycle in self.all_cycles() { + iter = iter.with(cycle.vertex_iter()); } - Iter::empty() + iter } } diff --git a/crates/fj-kernel/src/objects/face.rs b/crates/fj-kernel/src/objects/face.rs index 1076fa08c..c9eaa6f80 100644 --- a/crates/fj-kernel/src/objects/face.rs +++ b/crates/fj-kernel/src/objects/face.rs @@ -7,20 +7,8 @@ use super::{Cycle, Surface}; /// A face of a shape #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub enum Face { - /// A face of a shape - /// - /// A face is defined by a surface, and is bounded by edges that lie in that - /// surface. - Face(FaceBRep), - - /// The triangles of the face - /// - /// Representing faces as a collection of triangles is a temporary state. - /// The plan is to eventually represent faces as a geometric surface, - /// bounded by edges. While the transition is being made, this variant is - /// still required. - Triangles(Vec<(Triangle<3>, Color)>), +pub struct Face { + representation: Representation, } impl Face { @@ -31,53 +19,53 @@ impl Face { interiors: impl IntoIterator, color: [u8; 4], ) -> Self { - let exteriors = CyclesInFace::new(exteriors); - let interiors = CyclesInFace::new(interiors); - - Self::Face(FaceBRep { - surface, - exteriors, - interiors, - color, - }) + let exteriors = exteriors.into_iter().collect(); + let interiors = interiors.into_iter().collect(); + + Self { + representation: Representation::BRep(BRep { + surface, + exteriors, + interiors, + color, + }), + } } + + /// Contact an instance that uses triangle representation + pub fn from_triangles(triangles: TriRep) -> Self { + Self { + representation: Representation::TriRep(triangles), + } + } + /// Build a face using the [`FaceBuilder`] API pub fn builder(surface: Surface) -> FaceBuilder { FaceBuilder::new(surface) } - /// Access the boundary representation of the face - pub fn brep(&self) -> &FaceBRep { - match self { - Self::Face(face) => face, - _ => { - // No code that still uses triangle representation is calling - // this method. - unreachable!() - } - } - } - /// Access this face's surface - pub fn surface(&self) -> Surface { - self.brep().surface() + pub fn surface(&self) -> &Surface { + &self.brep().surface } - /// Access this face's exterior cycles - pub fn exteriors(&self) -> impl Iterator + '_ { - self.brep().exteriors() + /// Access the cycles that bound the face on the outside + pub fn exteriors(&self) -> impl Iterator + '_ { + self.brep().exteriors.iter() } - /// Access this face's interior cycles - pub fn interiors(&self) -> impl Iterator + '_ { - self.brep().interiors() + /// Access the cycles that bound the face on the inside + /// + /// Each of these cycles defines a hole in the face. + pub fn interiors(&self) -> impl Iterator + '_ { + self.brep().interiors.iter() } /// Access all cycles of this face /// /// This is equivalent to chaining the iterators returned by /// [`Face::exteriors`] and [`Face::interiors`]. - pub fn all_cycles(&self) -> impl Iterator + '_ { + pub fn all_cycles(&self) -> impl Iterator + '_ { self.exteriors().chain(self.interiors()) } @@ -85,80 +73,44 @@ impl Face { pub fn color(&self) -> [u8; 4] { self.brep().color } -} - -/// The boundary representation of a face -/// -/// This type exists to ease the handling of faces that use boundary -/// representation. It will eventually be merged into `Face`, once -/// `Face::Triangles` can finally be removed. -#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub struct FaceBRep { - /// The surface that defines this face - pub surface: Surface, - /// The cycles that bound the face on the outside - /// - /// # Implementation Note - /// - /// Since these cycles bound the face, the edges they consist of must - /// lie in the surface. The data we're using here is 3-dimensional - /// though, so no such limitation is enforced. - /// - /// It might be less error-prone to specify the cycles in surface - /// coordinates. - pub exteriors: CyclesInFace, - - /// The cycles that bound the face on the inside - /// - /// Each of these cycles defines a hole in the face. - /// - /// # Implementation note + /// Access triangles, if this face uses triangle representation /// - /// See note on `exterior` field. - pub interiors: CyclesInFace, - - /// The color of the face - pub color: [u8; 4], -} - -impl FaceBRep { - /// Access this face's surface - pub fn surface(&self) -> Surface { - self.surface - } + /// Only some faces still use triangle representation. At some point, none + /// will. This method exists as a workaround, while the transition is still + /// in progress. + pub fn triangles(&self) -> Option<&TriRep> { + if let Representation::TriRep(triangles) = &self.representation { + return Some(triangles); + } - /// Access this face's exterior cycles - pub fn exteriors(&self) -> impl Iterator + '_ { - self.exteriors.as_local() + None } - /// Access this face's interior cycles - pub fn interiors(&self) -> impl Iterator + '_ { - self.interiors.as_local() - } + /// Access the boundary representation of the face + fn brep(&self) -> &BRep { + if let Representation::BRep(face) = &self.representation { + return face; + } - /// Access all cycles of this face - /// - /// This is equivalent to chaining the iterators returned by - /// [`Face::exteriors`] and [`Face::interiors`]. - pub fn all_cycles(&self) -> impl Iterator + '_ { - self.exteriors().chain(self.interiors()) + // No code that still uses triangle representation is calling this + // method. + unreachable!() } } -/// A list of cycles, as they are stored in `Face` #[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub struct CyclesInFace(Vec); - -impl CyclesInFace { - /// Create a new instance of `CyclesInFace` - pub fn new(cycles: impl IntoIterator) -> Self { - Self(cycles.into_iter().collect()) - } +enum Representation { + BRep(BRep), + TriRep(TriRep), +} - /// Access an iterator over the canonical forms of the cycles - pub fn as_local(&self) -> impl Iterator + '_ { - self.0.iter().cloned() - } +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +struct BRep { + surface: Surface, + exteriors: Vec, + interiors: Vec, + color: [u8; 4], } + +type TriRep = Vec<(Triangle<3>, Color)>; diff --git a/crates/fj-kernel/src/objects/mod.rs b/crates/fj-kernel/src/objects/mod.rs index 72021c07b..3615cdf15 100644 --- a/crates/fj-kernel/src/objects/mod.rs +++ b/crates/fj-kernel/src/objects/mod.rs @@ -18,7 +18,7 @@ pub use self::{ curve::Curve, cycle::Cycle, edge::{Edge, VerticesOfEdge}, - face::{CyclesInFace, Face, FaceBRep}, + face::Face, global_vertex::GlobalVertex, sketch::Sketch, solid::Solid, diff --git a/crates/fj-operations/src/difference_2d.rs b/crates/fj-operations/src/difference_2d.rs index 35e6d6bc2..8d0a9238f 100644 --- a/crates/fj-operations/src/difference_2d.rs +++ b/crates/fj-operations/src/difference_2d.rs @@ -38,43 +38,39 @@ impl Shape for fj::Difference2d { if let Some(face) = a.face_iter().next() { // If there's at least one face to subtract from, we can proceed. - let surface = face.brep().surface; + let surface = face.surface(); for face in a.face_iter() { - let face = face.brep(); - assert_eq!( surface, face.surface(), "Trying to subtract faces with different surfaces.", ); - for cycle in face.exteriors.as_local() { - let cycle = add_cycle(cycle, false); + for cycle in face.exteriors() { + let cycle = add_cycle(cycle.clone(), false); exteriors.push(cycle); } - for cycle in face.interiors.as_local() { - let cycle = add_cycle(cycle, true); + for cycle in face.interiors() { + let cycle = add_cycle(cycle.clone(), true); interiors.push(cycle); } } for face in b.face_iter() { - let face = face.brep(); - assert_eq!( surface, face.surface(), "Trying to subtract faces with different surfaces.", ); - for cycle in face.exteriors.as_local() { - let cycle = add_cycle(cycle, true); + for cycle in face.exteriors() { + let cycle = add_cycle(cycle.clone(), true); interiors.push(cycle); } } - faces.push(Face::new(surface, exteriors, interiors, self.color())); + faces.push(Face::new(*surface, exteriors, interiors, self.color())); } let difference = Sketch::from_faces(faces);