diff --git a/crates/fj-kernel/src/algorithms/approx/faces.rs b/crates/fj-kernel/src/algorithms/approx/faces.rs index 64f72a961..f7e183f07 100644 --- a/crates/fj-kernel/src/algorithms/approx/faces.rs +++ b/crates/fj-kernel/src/algorithms/approx/faces.rs @@ -85,7 +85,7 @@ mod tests { use crate::{ local::Local, - objects::{Cycle, Face, Surface}, + objects::{Face, Surface}, }; use super::{CycleApprox, FaceApprox, Tolerance}; @@ -107,15 +107,9 @@ mod tests { let h = Point::from([1., 2.]); let surface = Surface::xy_plane(); - let face = Face::new(surface) - .with_exteriors([Cycle::polygon_from_points( - &surface, - [a, b, c, d], - )]) - .with_interiors([Cycle::polygon_from_points( - &surface, - [e, f, g, h], - )]); + let face = Face::build(surface) + .polygon_from_points([a, b, c, d]) + .with_hole([e, f, g, h]); let a = Local::new(a, a.to_xyz()); let b = Local::new(b, b.to_xyz()); diff --git a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs index 50475209c..979ba4c8c 100644 --- a/crates/fj-kernel/src/algorithms/intersection/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersection/curve_face.rs @@ -184,7 +184,7 @@ pub type CurveFaceIntersection = [Scalar; 2]; mod tests { use fj_math::{Line, Point, Vector}; - use crate::objects::{Curve, Cycle, Face, Surface}; + use crate::objects::{Curve, Face, Surface}; use super::CurveFaceIntersectionList; @@ -211,9 +211,9 @@ mod tests { ]; let surface = Surface::xy_plane(); - let face = Face::new(surface) - .with_exteriors([Cycle::polygon_from_points(&surface, exterior)]) - .with_interiors([Cycle::polygon_from_points(&surface, interior)]); + let face = Face::build(surface) + .polygon_from_points(exterior) + .with_hole(interior); let expected = CurveFaceIntersectionList::from_intervals([[1., 2.], [4., 5.]]); diff --git a/crates/fj-kernel/src/algorithms/reverse.rs b/crates/fj-kernel/src/algorithms/reverse.rs index ce7c6a86b..54513b3c8 100644 --- a/crates/fj-kernel/src/algorithms/reverse.rs +++ b/crates/fj-kernel/src/algorithms/reverse.rs @@ -60,25 +60,23 @@ fn reverse_local_coordinates_in_cycle<'r>( mod tests { use pretty_assertions::assert_eq; - use crate::objects::{Cycle, Face, Surface}; + use crate::objects::{Face, Surface}; #[test] fn reverse_face() { let surface = Surface::xy_plane(); - let original = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [[0., 0.], [1., 0.], [0., 1.]], - )]); + let original = Face::build(surface).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., 1.], + ]); let reversed = super::reverse_face(&original); let surface = Surface::xy_plane().reverse(); - let expected = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [[0., 0.], [1., 0.], [0., -1.]], - )]); + let expected = Face::build(surface) + .polygon_from_points([[0., 0.], [1., 0.], [0., -1.]]) + .into_face(); assert_eq!(expected, reversed); } diff --git a/crates/fj-kernel/src/algorithms/sweep.rs b/crates/fj-kernel/src/algorithms/sweep.rs index 645930bb9..cc78784a0 100644 --- a/crates/fj-kernel/src/algorithms/sweep.rs +++ b/crates/fj-kernel/src/algorithms/sweep.rs @@ -205,7 +205,7 @@ mod tests { use crate::{ algorithms::Tolerance, iter::ObjectIters, - objects::{Cycle, Face, Sketch, Surface}, + objects::{Face, Sketch, Surface}, }; #[test] @@ -295,11 +295,11 @@ mod tests { let tolerance = Tolerance::from_scalar(Scalar::ONE)?; let surface = Surface::xy_plane(); - let face = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [[0., 0.], [1., 0.], [0., 1.]], - )]); + let face = Face::build(surface).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., 1.], + ]); let sketch = Sketch::from_faces([face]); let solid = @@ -313,10 +313,9 @@ mod tests { let faces = expected_surfaces.into_iter().map(|surface| { let surface = Surface::plane_from_points(surface); - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - expected_vertices.clone(), - )]) + Face::build(surface) + .polygon_from_points(expected_vertices.clone()) + .into_face() }); for face in faces { diff --git a/crates/fj-kernel/src/algorithms/triangulate/mod.rs b/crates/fj-kernel/src/algorithms/triangulate/mod.rs index 7cc73fde1..1c7c94727 100644 --- a/crates/fj-kernel/src/algorithms/triangulate/mod.rs +++ b/crates/fj-kernel/src/algorithms/triangulate/mod.rs @@ -67,7 +67,7 @@ mod tests { use crate::{ algorithms::Tolerance, - objects::{Cycle, Face, Surface}, + objects::{Face, Surface}, }; #[test] @@ -78,11 +78,7 @@ mod tests { let d = [0., 1.]; let surface = Surface::xy_plane(); - let face = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [a, b, c, d], - )]); + let face = Face::build(surface).polygon_from_points([a, b, c, d]); let a = Point::from(a).to_xyz(); let b = Point::from(b).to_xyz(); @@ -112,15 +108,9 @@ mod tests { let h = [1., 2.]; let surface = Surface::xy_plane(); - let face = Face::new(surface) - .with_exteriors([Cycle::polygon_from_points( - &surface, - [a, b, c, d], - )]) - .with_interiors([Cycle::polygon_from_points( - &surface, - [e, f, g, h], - )]); + let face = Face::build(surface) + .polygon_from_points([a, b, c, d]) + .with_hole([e, f, g, h]); let triangles = triangulate(face)?; @@ -168,11 +158,7 @@ mod tests { let e = Point::from([0., 0.8]); let surface = Surface::xy_plane(); - let face = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [a, b, c, d, e], - )]); + let face = Face::build(surface).polygon_from_points([a, b, c, d, e]); let triangles = triangulate(face)?; @@ -191,10 +177,14 @@ mod tests { Ok(()) } - fn triangulate(face: Face) -> anyhow::Result>> { + fn triangulate(face: impl Into) -> anyhow::Result>> { let tolerance = Tolerance::from_scalar(Scalar::ONE)?; let mut debug_info = DebugInfo::new(); - Ok(super::triangulate(vec![face], tolerance, &mut debug_info)) + Ok(super::triangulate( + vec![face.into()], + tolerance, + &mut debug_info, + )) } } diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs new file mode 100644 index 000000000..fd2472199 --- /dev/null +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -0,0 +1,45 @@ +use fj_math::Point; + +use crate::objects::{Cycle, Edge, Surface}; + +/// API for building a [`Cycle`] +pub struct CycleBuilder { + surface: Surface, +} + +impl CycleBuilder { + /// Construct an instance of `CycleBuilder` + /// + /// Also see [`Cycle::build`]. + pub fn new(surface: Surface) -> Self { + Self { surface } + } + + /// Create a polygon from a list of points + pub fn polygon_from_points( + &self, + points: impl IntoIterator>>, + ) -> Cycle { + let mut points: Vec<_> = points.into_iter().map(Into::into).collect(); + + // A polygon is closed, so we need to add the first point at the end + // again, for the next step. + if let Some(point) = points.first().cloned() { + points.push(point); + } + + let mut edges = Vec::new(); + for points in points.windows(2) { + // Can't panic, as we passed `2` to `windows`. + // + // Can be cleaned up, once `array_windows` is stable. + let points = [points[0], points[1]]; + + edges.push( + Edge::build().line_segment_from_points(&self.surface, points), + ); + } + + Cycle::new().with_edges(edges) + } +} diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/builder/edge.rs new file mode 100644 index 000000000..bfe587928 --- /dev/null +++ b/crates/fj-kernel/src/builder/edge.rs @@ -0,0 +1,64 @@ +use fj_math::{Circle, Line, Point, Scalar, Vector}; + +use crate::{ + local::Local, + objects::{Curve, Edge, GlobalVertex, Surface, Vertex, VerticesOfEdge}, +}; + +/// API for building an [`Edge`] +pub struct EdgeBuilder; + +impl EdgeBuilder { + /// Create a circle from the given radius + pub fn circle_from_radius(&self, radius: Scalar) -> Edge { + let curve_local = Curve::Circle(Circle { + center: Point::origin(), + a: Vector::from([radius, Scalar::ZERO]), + b: Vector::from([Scalar::ZERO, radius]), + }); + let curve_canonical = Curve::Circle(Circle { + center: Point::origin(), + a: Vector::from([radius, Scalar::ZERO, Scalar::ZERO]), + b: Vector::from([Scalar::ZERO, radius, Scalar::ZERO]), + }); + + Edge::new( + Local::new(curve_local, curve_canonical), + VerticesOfEdge::none(), + ) + } + + /// Create a line segment from two points + pub fn line_segment_from_points( + &self, + surface: &Surface, + points: [impl Into>; 2], + ) -> Edge { + let points = points.map(Into::into); + + let global_vertices = points.map(|position| { + let position = surface.point_from_surface_coords(position); + GlobalVertex::from_position(position) + }); + + let curve_local = Curve::Line(Line::from_points(points)); + let curve_canonical = { + let points = + global_vertices.map(|global_vertex| global_vertex.position()); + Curve::Line(Line::from_points(points)) + }; + + let vertices = { + let [a, b] = global_vertices; + [ + Vertex::new(Point::from([0.]), a), + Vertex::new(Point::from([1.]), b), + ] + }; + + Edge::new( + Local::new(curve_local, curve_canonical), + VerticesOfEdge::from_vertices(vertices), + ) + } +} diff --git a/crates/fj-kernel/src/builder/face.rs b/crates/fj-kernel/src/builder/face.rs new file mode 100644 index 000000000..e68d61dc4 --- /dev/null +++ b/crates/fj-kernel/src/builder/face.rs @@ -0,0 +1,72 @@ +use std::ops::Deref; + +use fj_math::Point; + +use crate::objects::{Cycle, Face, Surface}; + +/// API for building a [`Face`] +pub struct FaceBuilder { + surface: Surface, +} + +impl FaceBuilder { + /// Construct an instance of `FaceBuilder` + /// + /// Also see [`Face::build`]. + pub fn new(surface: Surface) -> Self { + Self { surface } + } + + /// Construct a polygon from a list of points + pub fn polygon_from_points( + &self, + points: impl IntoIterator>>, + ) -> FacePolygon { + let face = Face::new(self.surface) + .with_exteriors([ + Cycle::build(self.surface).polygon_from_points(points) + ]); + + FacePolygon { face } + } +} + +/// A polygon +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct FacePolygon { + face: Face, +} + +impl FacePolygon { + /// Add a hole to the polygon + pub fn with_hole( + mut self, + points: impl IntoIterator>>, + ) -> Self { + let surface = *self.face.surface(); + self.face = self.face.with_interiors([ + Cycle::build(surface).polygon_from_points(points) + ]); + + self + } + + /// Consume the `Polygon` and return the [`Face`] it wraps + pub fn into_face(self) -> Face { + self.face + } +} + +impl From for Face { + fn from(polygon: FacePolygon) -> Self { + polygon.into_face() + } +} + +impl Deref for FacePolygon { + type Target = Face; + + fn deref(&self) -> &Self::Target { + &self.face + } +} diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs new file mode 100644 index 000000000..9edf20fac --- /dev/null +++ b/crates/fj-kernel/src/builder/mod.rs @@ -0,0 +1,13 @@ +//! API for building objects + +mod cycle; +mod edge; +mod face; +mod solid; + +pub use self::{ + cycle::CycleBuilder, + edge::EdgeBuilder, + face::{FaceBuilder, FacePolygon}, + solid::SolidBuilder, +}; diff --git a/crates/fj-kernel/src/builder/solid.rs b/crates/fj-kernel/src/builder/solid.rs new file mode 100644 index 000000000..7e3ce29f0 --- /dev/null +++ b/crates/fj-kernel/src/builder/solid.rs @@ -0,0 +1,38 @@ +use fj_math::Scalar; + +use crate::{ + algorithms::TransformObject, + objects::{Face, Solid, Surface}, +}; + +/// API for building a [`Solid`] +pub struct SolidBuilder; + +impl SolidBuilder { + /// Create a cube from the length of its edges + pub fn cube_from_edge_length( + &self, + edge_length: impl Into, + ) -> Solid { + // Let's define a short-hand for half the edge length. We're going to + // need it a lot. + let h = edge_length.into() / 2.; + + let points = [[-h, -h], [h, -h], [h, h], [-h, h]]; + + const Z: Scalar = Scalar::ZERO; + let planes = [ + Surface::xy_plane().translate([Z, Z, -h]), // bottom + Surface::xy_plane().translate([Z, Z, h]), // top + Surface::xz_plane().translate([Z, -h, Z]), // front + Surface::xz_plane().translate([Z, h, Z]), // back + Surface::yz_plane().translate([-h, Z, Z]), // left + Surface::yz_plane().translate([h, Z, Z]), // right + ]; + + let faces = + planes.map(|plane| Face::build(plane).polygon_from_points(points)); + + Solid::from_faces(faces) + } +} diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index 4ae9aae23..6e99a2dcf 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -319,10 +319,11 @@ mod tests { #[test] fn cycle() { - let object = Cycle::polygon_from_points( - &Surface::xy_plane(), - [[0., 0.], [1., 0.], [0., 1.]], - ); + let object = Cycle::build(Surface::xy_plane()).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., 1.], + ]); assert_eq!(3, object.curve_iter().count()); assert_eq!(1, object.cycle_iter().count()); @@ -337,7 +338,7 @@ mod tests { #[test] fn edge() { - let object = Edge::line_segment_from_points( + let object = Edge::build().line_segment_from_points( &Surface::xy_plane(), [[0., 0.], [1., 0.]], ); @@ -356,11 +357,11 @@ mod tests { #[test] fn face() { let surface = Surface::xy_plane(); - let object = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [[0., 0.], [1., 0.], [0., 1.]], - )]); + let object = Face::build(surface).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., 1.], + ]); assert_eq!(3, object.curve_iter().count()); assert_eq!(1, object.cycle_iter().count()); @@ -391,11 +392,11 @@ mod tests { #[test] fn sketch() { let surface = Surface::xy_plane(); - let face = - Face::new(surface).with_exteriors([Cycle::polygon_from_points( - &surface, - [[0., 0.], [1., 0.], [0., 1.]], - )]); + let face = Face::build(surface).polygon_from_points([ + [0., 0.], + [1., 0.], + [0., 1.], + ]); let object = Sketch::from_faces([face]); assert_eq!(3, object.curve_iter().count()); @@ -411,7 +412,7 @@ mod tests { #[test] fn solid() { - let object = Solid::cube_from_edge_length(1.); + let object = Solid::build().cube_from_edge_length(1.); assert_eq!(18, object.curve_iter().count()); assert_eq!(6, object.cycle_iter().count()); diff --git a/crates/fj-kernel/src/lib.rs b/crates/fj-kernel/src/lib.rs index d8171eecc..fed8991f1 100644 --- a/crates/fj-kernel/src/lib.rs +++ b/crates/fj-kernel/src/lib.rs @@ -88,6 +88,7 @@ #![warn(missing_docs)] pub mod algorithms; +pub mod builder; pub mod iter; pub mod local; pub mod objects; diff --git a/crates/fj-kernel/src/objects/cycle.rs b/crates/fj-kernel/src/objects/cycle.rs index c25efdebf..f2f44a531 100644 --- a/crates/fj-kernel/src/objects/cycle.rs +++ b/crates/fj-kernel/src/objects/cycle.rs @@ -1,4 +1,4 @@ -use fj_math::Point; +use crate::builder::CycleBuilder; use super::{Edge, Surface}; @@ -33,30 +33,9 @@ impl Cycle { self } - /// Create a polygon from a list of points - pub fn polygon_from_points( - surface: &Surface, - points: impl IntoIterator>>, - ) -> Cycle { - let mut points: Vec<_> = points.into_iter().map(Into::into).collect(); - - // A polygon is closed, so we need to add the first point at the end - // again, for the next step. - if let Some(point) = points.first().cloned() { - points.push(point); - } - - let mut edges = Vec::new(); - for points in points.windows(2) { - // Can't panic, as we passed `2` to `windows`. - // - // Can be cleaned up, once `array_windows` is stable. - let points = [points[0], points[1]]; - - edges.push(Edge::line_segment_from_points(surface, points)); - } - - Cycle { edges } + /// Build a cycle using [`CycleBuilder`] + pub fn build(surface: Surface) -> CycleBuilder { + CycleBuilder::new(surface) } /// Access edges that make up the cycle diff --git a/crates/fj-kernel/src/objects/edge.rs b/crates/fj-kernel/src/objects/edge.rs index 445f585e6..677545277 100644 --- a/crates/fj-kernel/src/objects/edge.rs +++ b/crates/fj-kernel/src/objects/edge.rs @@ -1,10 +1,8 @@ use std::fmt; -use fj_math::{Circle, Line, Point, Scalar, Vector}; +use crate::{builder::EdgeBuilder, local::Local}; -use crate::local::Local; - -use super::{Curve, GlobalVertex, Surface, Vertex}; +use super::{Curve, Vertex}; /// An edge of a shape #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] @@ -19,56 +17,9 @@ impl Edge { Self { curve, vertices } } - /// Create a circle from the given radius - pub fn circle_from_radius(radius: Scalar) -> Self { - let curve_local = Curve::Circle(Circle { - center: Point::origin(), - a: Vector::from([radius, Scalar::ZERO]), - b: Vector::from([Scalar::ZERO, radius]), - }); - let curve_canonical = Curve::Circle(Circle { - center: Point::origin(), - a: Vector::from([radius, Scalar::ZERO, Scalar::ZERO]), - b: Vector::from([Scalar::ZERO, radius, Scalar::ZERO]), - }); - - Edge { - curve: Local::new(curve_local, curve_canonical), - vertices: VerticesOfEdge::none(), - } - } - - /// Create a line segment from two points - pub fn line_segment_from_points( - surface: &Surface, - points: [impl Into>; 2], - ) -> Self { - let points = points.map(Into::into); - - let global_vertices = points.map(|position| { - let position = surface.point_from_surface_coords(position); - GlobalVertex::from_position(position) - }); - - let curve_local = Curve::Line(Line::from_points(points)); - let curve_canonical = { - let points = - global_vertices.map(|global_vertex| global_vertex.position()); - Curve::Line(Line::from_points(points)) - }; - - let vertices = { - let [a, b] = global_vertices; - [ - Vertex::new(Point::from([0.]), a), - Vertex::new(Point::from([1.]), b), - ] - }; - - Self { - curve: Local::new(curve_local, curve_canonical), - vertices: VerticesOfEdge::from_vertices(vertices), - } + /// Build an edge using [`EdgeBuilder`] + pub fn build() -> EdgeBuilder { + EdgeBuilder } /// Access the curve that defines the edge's geometry diff --git a/crates/fj-kernel/src/objects/face.rs b/crates/fj-kernel/src/objects/face.rs index 32c834a4c..d8ddb42aa 100644 --- a/crates/fj-kernel/src/objects/face.rs +++ b/crates/fj-kernel/src/objects/face.rs @@ -1,6 +1,8 @@ use fj_interop::mesh::Color; use fj_math::Triangle; +use crate::builder::FaceBuilder; + use super::{Cycle, Surface}; /// A face of a shape @@ -32,6 +34,47 @@ impl Face { } } + /// Add exterior cycles to the face + /// + /// Consumes the face and returns the updated instance. + pub fn with_exteriors( + mut self, + exteriors: impl IntoIterator, + ) -> Self { + for exterior in exteriors.into_iter() { + self.brep_mut().exteriors.push(exterior); + } + + self + } + + /// Add interior cycles to the face + /// + /// Consumes the face and returns the updated instance. + pub fn with_interiors( + mut self, + interiors: impl IntoIterator, + ) -> Self { + for interior in interiors.into_iter() { + self.brep_mut().interiors.push(interior); + } + + self + } + + /// Update the color of the face + /// + /// Consumes the face and returns the updated instance. + pub fn with_color(mut self, color: Color) -> Self { + self.brep_mut().color = color; + self + } + + /// Build a face using [`FaceBuilder`] + pub fn build(surface: Surface) -> FaceBuilder { + FaceBuilder::new(surface) + } + /// Access this face's surface pub fn surface(&self) -> &Surface { &self.brep().surface @@ -75,42 +118,6 @@ impl Face { None } - /// Add exterior cycles to the face - /// - /// Consumes the face and returns the updated instance. - pub fn with_exteriors( - mut self, - exteriors: impl IntoIterator, - ) -> Self { - for exterior in exteriors.into_iter() { - self.brep_mut().exteriors.push(exterior); - } - - self - } - - /// Add interior cycles to the face - /// - /// Consumes the face and returns the updated instance. - pub fn with_interiors( - mut self, - interiors: impl IntoIterator, - ) -> Self { - for interior in interiors.into_iter() { - self.brep_mut().interiors.push(interior); - } - - self - } - - /// Update the color of the face - /// - /// Consumes the face and returns the updated instance. - pub fn with_color(mut self, color: Color) -> Self { - self.brep_mut().color = color; - self - } - /// Access the boundary representation of the face fn brep(&self) -> &BRep { if let Representation::BRep(face) = &self.representation { diff --git a/crates/fj-kernel/src/objects/sketch.rs b/crates/fj-kernel/src/objects/sketch.rs index 69a4f7411..3f63b37e5 100644 --- a/crates/fj-kernel/src/objects/sketch.rs +++ b/crates/fj-kernel/src/objects/sketch.rs @@ -15,8 +15,10 @@ pub struct Sketch { impl Sketch { /// Construct a sketch from faces - pub fn from_faces(faces: impl IntoIterator) -> Self { - let faces = faces.into_iter().collect(); + pub fn from_faces( + faces: impl IntoIterator>, + ) -> Self { + let faces = faces.into_iter().map(Into::into).collect(); Self { faces } } diff --git a/crates/fj-kernel/src/objects/solid.rs b/crates/fj-kernel/src/objects/solid.rs index 0ae94bf0c..a63da5e24 100644 --- a/crates/fj-kernel/src/objects/solid.rs +++ b/crates/fj-kernel/src/objects/solid.rs @@ -1,10 +1,8 @@ use std::collections::BTreeSet; -use fj_math::Scalar; +use crate::builder::SolidBuilder; -use crate::{algorithms::TransformObject, objects::Cycle}; - -use super::{Face, Surface}; +use super::Face; /// A 3-dimensional shape /// @@ -25,35 +23,16 @@ pub struct Solid { impl Solid { /// Construct a solid from faces - pub fn from_faces(faces: impl IntoIterator) -> Self { - let faces = faces.into_iter().collect(); + pub fn from_faces( + faces: impl IntoIterator>, + ) -> Self { + let faces = faces.into_iter().map(Into::into).collect(); Self { faces } } - /// Create a cube from the length of its edges - pub fn cube_from_edge_length(edge_length: impl Into) -> Self { - // Let's define a short-hand for half the edge length. We're going to - // need it a lot. - let h = edge_length.into() / 2.; - - let points = [[-h, -h], [h, -h], [h, h], [-h, h]]; - - const Z: Scalar = Scalar::ZERO; - let planes = [ - Surface::xy_plane().translate([Z, Z, -h]), // bottom - Surface::xy_plane().translate([Z, Z, h]), // top - Surface::xz_plane().translate([Z, -h, Z]), // front - Surface::xz_plane().translate([Z, h, Z]), // back - Surface::yz_plane().translate([-h, Z, Z]), // left - Surface::yz_plane().translate([h, Z, Z]), // right - ]; - - let faces = planes.map(|plane| { - Face::new(plane) - .with_exteriors([Cycle::polygon_from_points(&plane, points)]) - }); - - Solid::from_faces(faces) + /// Build a solid using [`SolidBuilder`] + pub fn build() -> SolidBuilder { + SolidBuilder } /// Access the solid's faces diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index 4fff1c49f..0eea8f950 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -24,8 +24,8 @@ impl Shape for fj::Sketch { // Circles have just a single round edge with no vertices. So // none need to be added here. - let edge = - Edge::circle_from_radius(Scalar::from_f64(circle.radius())); + let edge = Edge::build() + .circle_from_radius(Scalar::from_f64(circle.radius())); let cycle = Cycle::new().with_edges([edge]); Face::new(surface) @@ -36,10 +36,9 @@ impl Shape for fj::Sketch { let points = poly_chain.to_points().into_iter().map(Point::from); - Face::new(surface) - .with_exteriors([Cycle::polygon_from_points( - &surface, points, - )]) + Face::build(surface) + .polygon_from_points(points) + .into_face() .with_color(Color(self.color())) } };