diff --git a/crates/fj-kernel/src/algorithms/approx/curve.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs index 09fd66828..b1b80628d 100644 --- a/crates/fj-kernel/src/algorithms/approx/curve.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -209,8 +209,10 @@ mod tests { let stores = Stores::new(); let surface = Surface::new(GlobalPath::x_axis(), [0., 0., 1.]); - let curve = Curve::builder(&stores, surface) - .build_line_from_points([[1., 1.], [2., 1.]]); + let curve = Curve::partial() + .with_surface(surface) + .as_line_from_points([[1., 1.], [2., 1.]]) + .build(&stores); let range = RangeOnPath::from([[0.], [1.]]); let approx = (&curve, range).approx(1.); @@ -224,8 +226,10 @@ mod tests { let surface = Surface::new(GlobalPath::circle_from_radius(1.), [0., 0., 1.]); - let curve = Curve::builder(&stores, surface) - .build_line_from_points([[1., 1.], [1., 2.]]); + let curve = Curve::partial() + .with_surface(surface) + .as_line_from_points([[1., 1.], [1., 2.]]) + .build(&stores); let range = RangeOnPath::from([[0.], [1.]]); let approx = (&curve, range).approx(1.); @@ -239,8 +243,10 @@ mod tests { let path = GlobalPath::circle_from_radius(1.); let surface = Surface::new(path, [0., 0., 1.]); - let curve = Curve::builder(&stores, surface) - .build_line_from_points([[0., 1.], [1., 1.]]); + let curve = Curve::partial() + .with_surface(surface) + .as_line_from_points([[0., 1.], [1., 1.]]) + .build(&stores); let range = RangeOnPath::from([[0.], [TAU]]); let tolerance = 1.; @@ -266,8 +272,10 @@ mod tests { let stores = Stores::new(); let surface = Surface::new(GlobalPath::x_axis(), [0., 0., 1.]); - let curve = - Curve::builder(&stores, surface).build_circle_from_radius(1.); + let curve = Curve::partial() + .with_surface(surface) + .as_circle_from_radius(1.) + .build(&stores); let range = RangeOnPath::from([[0.], [TAU]]); let tolerance = 1.; diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs index 47746980a..76cf88a13 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs @@ -86,7 +86,10 @@ mod tests { let stores = Stores::new(); let surface = Surface::xy_plane(); - let curve = Curve::builder(&stores, surface).build_u_axis(); + let curve = Curve::partial() + .with_surface(surface) + .as_u_axis() + .build(&stores); let half_edge = HalfEdge::builder(&stores, surface) .as_line_segment_from_points([[1., -1.], [1., 1.]]) .build(); @@ -106,7 +109,10 @@ mod tests { let stores = Stores::new(); let surface = Surface::xy_plane(); - let curve = Curve::builder(&stores, surface).build_u_axis(); + let curve = Curve::partial() + .with_surface(surface) + .as_u_axis() + .build(&stores); let half_edge = HalfEdge::builder(&stores, surface) .as_line_segment_from_points([[-1., -1.], [-1., 1.]]) .build(); @@ -126,7 +132,10 @@ mod tests { let stores = Stores::new(); let surface = Surface::xy_plane(); - let curve = Curve::builder(&stores, surface).build_u_axis(); + let curve = Curve::partial() + .with_surface(surface) + .as_u_axis() + .build(&stores); let half_edge = HalfEdge::builder(&stores, surface) .as_line_segment_from_points([[-1., -1.], [1., -1.]]) .build(); @@ -141,7 +150,10 @@ mod tests { let stores = Stores::new(); let surface = Surface::xy_plane(); - let curve = Curve::builder(&stores, surface).build_u_axis(); + let curve = Curve::partial() + .with_surface(surface) + .as_u_axis() + .build(&stores); let half_edge = HalfEdge::builder(&stores, surface) .as_line_segment_from_points([[-1., 0.], [1., 0.]]) .build(); diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs index 35ee1f673..b0b461a2f 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs @@ -168,8 +168,10 @@ mod tests { let surface = Surface::xy_plane(); - let curve = Curve::builder(&stores, surface) - .build_line_from_points([[-3., 0.], [-2., 0.]]); + let curve = Curve::partial() + .with_surface(surface) + .as_line_from_points([[-3., 0.], [-2., 0.]]) + .build(&stores); #[rustfmt::skip] let exterior = [ diff --git a/crates/fj-kernel/src/algorithms/intersect/face_face.rs b/crates/fj-kernel/src/algorithms/intersect/face_face.rs index 1e93b9151..a0708fee9 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_face.rs @@ -114,8 +114,10 @@ mod tests { let intersection = FaceFaceIntersection::compute([&a, &b], &stores); let expected_curves = surfaces.map(|surface| { - Curve::builder(&stores, surface) - .build_line_from_points([[0., 0.], [1., 0.]]) + Curve::partial() + .with_surface(surface) + .as_line_from_points([[0., 0.], [1., 0.]]) + .build(&stores) }); let expected_intervals = CurveFaceIntersection::from_intervals([[[-1.], [1.]]]); diff --git a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs index 7676a4170..895168a8d 100644 --- a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs +++ b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs @@ -186,8 +186,10 @@ mod tests { None, ); - let expected_xy = Curve::builder(&stores, xy).build_u_axis(); - let expected_xz = Curve::builder(&stores, xz).build_u_axis(); + let expected_xy = + Curve::partial().with_surface(xy).as_u_axis().build(&stores); + let expected_xz = + Curve::partial().with_surface(xz).as_u_axis().build(&stores); assert_eq!( SurfaceSurfaceIntersection::compute([&xy, &xz], &stores), diff --git a/crates/fj-kernel/src/algorithms/sweep/vertex.rs b/crates/fj-kernel/src/algorithms/sweep/vertex.rs index 7de75d8c0..d0dcf535f 100644 --- a/crates/fj-kernel/src/algorithms/sweep/vertex.rs +++ b/crates/fj-kernel/src/algorithms/sweep/vertex.rs @@ -137,8 +137,9 @@ impl Sweep for GlobalVertex { let a = self; let b = GlobalVertex::from_position(self.position() + path.into()); - let curve = GlobalCurve::builder(stores) - .line_from_points([a.position(), b.position()]); + let curve = GlobalCurve::partial() + .as_line_from_points([a.position(), b.position()]) + .build(stores); GlobalEdge::new(curve, [a, b]) } @@ -160,7 +161,10 @@ mod tests { let stores = Stores::new(); let surface = Surface::xz_plane(); - let curve = Curve::builder(&stores, surface).build_u_axis(); + let curve = Curve::partial() + .with_surface(surface) + .as_u_axis() + .build(&stores); let vertex = Vertex::partial() .with_position([0.]) .with_curve(curve) @@ -182,7 +186,7 @@ mod tests { .sweep([0., 0., 1.], &stores); let expected_edge = GlobalEdge::new( - GlobalCurve::builder(&stores).z_axis(), + GlobalCurve::partial().as_z_axis().build(&stores), [[0., 0., 0.], [0., 0., 1.]].map(GlobalVertex::from_position), ); assert_eq!(edge, expected_edge); diff --git a/crates/fj-kernel/src/builder/curve.rs b/crates/fj-kernel/src/builder/curve.rs deleted file mode 100644 index bc421a319..000000000 --- a/crates/fj-kernel/src/builder/curve.rs +++ /dev/null @@ -1,118 +0,0 @@ -use fj_math::{Line, Point, Scalar, Vector}; - -use crate::{ - objects::{Curve, GlobalCurve, Surface}, - path::{GlobalPath, SurfacePath}, - stores::{Handle, Stores}, -}; - -/// API for building a [`Curve`] -/// -/// Also see [`Curve::builder`]. -pub struct CurveBuilder<'a> { - /// The stores that the created objects are put in - pub stores: &'a Stores, - - /// The surface that the [`Curve`] is defined in - pub surface: Surface, -} - -impl<'a> CurveBuilder<'a> { - /// Build a line that represents the u-axis on the surface - pub fn build_u_axis(self) -> Curve { - let a = Point::origin(); - let b = a + Vector::unit_u(); - - self.build_line_from_points([a, b]) - } - - /// Build a line that represents the v-axis on the surface - pub fn build_v_axis(self) -> Curve { - let a = Point::origin(); - let b = a + Vector::unit_v(); - - self.build_line_from_points([a, b]) - } - - /// Build a circle from the given radius - pub fn build_circle_from_radius(self, radius: impl Into) -> Curve { - let radius = radius.into(); - - let path = SurfacePath::circle_from_radius(radius); - let global_form = - GlobalCurve::builder(self.stores).circle_from_radius(radius); - - Curve::new(self.surface, path, global_form) - } - - /// Build a line from the given points - pub fn build_line_from_points( - &self, - points: [impl Into>; 2], - ) -> Curve { - let points = points.map(Into::into); - - let path = SurfacePath::Line(Line::from_points(points)); - let global_form = self.stores.global_curves.insert( - GlobalCurve::from_path(GlobalPath::Line(Line::from_points( - points - .map(|point| self.surface.point_from_surface_coords(point)), - ))), - ); - - Curve::new(self.surface, path, global_form) - } -} - -/// API for building a [`GlobalCurve`] -/// -/// Also see [`GlobalCurve::builder`]. -pub struct GlobalCurveBuilder<'a> { - /// The stores that the created objects are put in - pub stores: &'a Stores, -} - -impl<'a> GlobalCurveBuilder<'a> { - /// Build a line that represents the x-axis - pub fn x_axis(&self) -> Handle { - self.stores - .global_curves - .insert(GlobalCurve::from_path(GlobalPath::x_axis())) - } - - /// Build a line that represents the y-axis - pub fn y_axis(&self) -> Handle { - self.stores - .global_curves - .insert(GlobalCurve::from_path(GlobalPath::y_axis())) - } - - /// Build a line that represents the z-axis - pub fn z_axis(&self) -> Handle { - self.stores - .global_curves - .insert(GlobalCurve::from_path(GlobalPath::z_axis())) - } - - /// Build a circle from the given radius - pub fn circle_from_radius( - &self, - radius: impl Into, - ) -> Handle { - let path = GlobalPath::circle_from_radius(radius); - self.stores - .global_curves - .insert(GlobalCurve::from_path(path)) - } - - /// Create a line from the given points - pub fn line_from_points( - &self, - points: [impl Into>; 2], - ) -> Handle { - let line = Line::from_points(points); - self.stores - .global_curves - .insert(GlobalCurve::from_path(GlobalPath::Line(line))) - } -} diff --git a/crates/fj-kernel/src/builder/edge.rs b/crates/fj-kernel/src/builder/edge.rs index 335cb50a6..983706969 100644 --- a/crates/fj-kernel/src/builder/edge.rs +++ b/crates/fj-kernel/src/builder/edge.rs @@ -53,8 +53,10 @@ impl<'a> HalfEdgeBuilder<'a> { /// Build the [`HalfEdge`] as a circle from the given radius pub fn as_circle_from_radius(mut self, radius: impl Into) -> Self { - let curve = Curve::builder(self.stores, self.surface) - .build_circle_from_radius(radius); + let curve = Curve::partial() + .with_surface(self.surface) + .as_circle_from_radius(radius) + .build(self.stores); let vertices = { let [a_curve, b_curve] = diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index 680d3fff0..f71b4b476 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -1,6 +1,5 @@ //! API for building objects -mod curve; mod cycle; mod edge; mod face; @@ -9,7 +8,6 @@ mod sketch; mod solid; pub use self::{ - curve::{CurveBuilder, GlobalCurveBuilder}, cycle::CycleBuilder, edge::{GlobalEdgeBuilder, HalfEdgeBuilder}, face::FaceBuilder, diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index 70902efa0..aff05ce1c 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -362,7 +362,10 @@ mod tests { let stores = Stores::new(); let surface = Surface::xy_plane(); - let object = Curve::builder(&stores, surface).build_u_axis(); + let object = Curve::partial() + .with_surface(surface) + .as_u_axis() + .build(&stores); assert_eq!(1, object.curve_iter().count()); assert_eq!(0, object.cycle_iter().count()); @@ -425,7 +428,7 @@ mod tests { fn global_curve() { let stores = Stores::new(); - let object = GlobalCurve::builder(&stores).x_axis(); + let object = GlobalCurve::partial().as_x_axis().build(&stores); assert_eq!(0, object.curve_iter().count()); assert_eq!(0, object.cycle_iter().count()); @@ -561,7 +564,10 @@ mod tests { let stores = Stores::new(); let surface = Surface::xy_plane(); - let curve = Curve::builder(&stores, surface).build_u_axis(); + let curve = Curve::partial() + .with_surface(surface) + .as_u_axis() + .build(&stores); let global_vertex = GlobalVertex::from_position([0., 0., 0.]); let surface_vertex = SurfaceVertex::new([0., 0.], surface, global_vertex); diff --git a/crates/fj-kernel/src/objects/curve.rs b/crates/fj-kernel/src/objects/curve.rs index 934a2fa3b..d65b1702c 100644 --- a/crates/fj-kernel/src/objects/curve.rs +++ b/crates/fj-kernel/src/objects/curve.rs @@ -1,7 +1,7 @@ use crate::{ - builder::{CurveBuilder, GlobalCurveBuilder}, + partial::{PartialCurve, PartialGlobalCurve}, path::{GlobalPath, SurfacePath}, - stores::{Handle, Stores}, + stores::Handle, }; use super::Surface; @@ -15,9 +15,12 @@ pub struct Curve { } impl Curve { - /// Build a `Curve` using [`CurveBuilder`] - pub fn builder(stores: &Stores, surface: Surface) -> CurveBuilder { - CurveBuilder { stores, surface } + /// Create a [`PartialCurve`] + /// + /// This function exists just for convenience, and will just return a + /// default [`PartialCurve`]. + pub fn partial() -> PartialCurve { + PartialCurve::default() } /// Construct a new instance of `Curve` @@ -56,9 +59,12 @@ pub struct GlobalCurve { } impl GlobalCurve { - /// Build a `Curve` using [`GlobalCurveBuilder`] - pub fn builder(stores: &Stores) -> GlobalCurveBuilder { - GlobalCurveBuilder { stores } + /// Create a [`PartialGlobalCurve`] + /// + /// This function exists just for convenience, and will just return a + /// default [`PartialGlobalCurve`]. + pub fn partial() -> PartialGlobalCurve { + PartialGlobalCurve::default() } /// Construct a `GlobalCurve` from the path that defines it diff --git a/crates/fj-kernel/src/partial/curve.rs b/crates/fj-kernel/src/partial/curve.rs new file mode 100644 index 000000000..f56a5c953 --- /dev/null +++ b/crates/fj-kernel/src/partial/curve.rs @@ -0,0 +1,156 @@ +use fj_math::{Point, Scalar, Vector}; + +use crate::{ + objects::{Curve, GlobalCurve, Surface}, + path::{GlobalPath, SurfacePath}, + stores::{Handle, Stores}, +}; + +/// A partial [`Curve`] +/// +/// See [`crate::partial`] for more information. +#[derive(Default)] +pub struct PartialCurve { + /// The path that defines the [`Curve`] + /// + /// Must be provided before calling [`PartialCurve::build`]. + pub path: Option, + + /// The surface that the [`Curve`] is defined in + /// + /// Must be provided before calling [`PartialCurve::build`]. + pub surface: Option, + + /// The global form of the [`Curve`] + /// + /// Will be computed from `path` and `surface` in [`PartialCurve::build`], + /// if not provided. + pub global_form: Option, +} + +impl PartialCurve { + /// Provide a path for the partial curve + pub fn with_path(mut self, path: SurfacePath) -> Self { + self.path = Some(path); + self + } + + /// Provide a surface for the partial curve + pub fn with_surface(mut self, surface: Surface) -> Self { + self.surface = Some(surface); + self + } + + /// Provide a global form for the partial curve + pub fn with_global_form(mut self, global_form: PartialGlobalCurve) -> Self { + self.global_form = Some(global_form); + self + } + + /// Update partial curve to represent the u-axis + pub fn as_u_axis(self) -> Self { + let a = Point::origin(); + let b = a + Vector::unit_u(); + + self.as_line_from_points([a, b]) + } + + /// Update partial curve to represent the v-axis + pub fn as_v_axis(self) -> Self { + let a = Point::origin(); + let b = a + Vector::unit_v(); + + self.as_line_from_points([a, b]) + } + + /// Update partial curve as a circle, from the provided radius + pub fn as_circle_from_radius(self, radius: impl Into) -> Self { + self.with_path(SurfacePath::circle_from_radius(radius)) + } + + /// Update partial curve as a line, from the provided points + pub fn as_line_from_points(self, points: [impl Into>; 2]) -> Self { + self.with_path(SurfacePath::line_from_points(points)) + } + + /// Build a full [`Curve`] from the partial curve + pub fn build(self, stores: &Stores) -> Curve { + let path = self.path.expect("Can't build `Curve` without path"); + let surface = + self.surface.expect("Can't build `Curve` without surface"); + + let global_form = self + .global_form + .unwrap_or_else(|| { + let path = match path { + SurfacePath::Circle(circle) => { + GlobalPath::circle_from_radius(circle.radius()) + } + SurfacePath::Line(line) => GlobalPath::line_from_points( + [line.origin(), line.origin() + line.direction()].map( + |point| surface.point_from_surface_coords(point), + ), + ), + }; + + GlobalCurve::partial().with_path(path) + }) + .build(stores); + + Curve::new(surface, path, global_form) + } +} + +/// A partial [`GlobalCurve`] +/// +/// See [`crate::partial`] for more information. +#[derive(Default)] +pub struct PartialGlobalCurve { + /// The path that defines the [`GlobalCurve`] + /// + /// Must be provided before [`PartialGlobalCurve::build`] is called. + pub path: Option, +} + +impl PartialGlobalCurve { + /// Provide a path for the partial global curve + pub fn with_path(mut self, path: GlobalPath) -> Self { + self.path = Some(path); + self + } + + /// Update partial global curve to represent the x-axis + pub fn as_x_axis(self) -> Self { + self.with_path(GlobalPath::x_axis()) + } + + /// Update partial global curve to represent the y-axis + pub fn as_y_axis(self) -> Self { + self.with_path(GlobalPath::y_axis()) + } + + /// Update partial global curve to represent the z-axis + pub fn as_z_axis(self) -> Self { + self.with_path(GlobalPath::z_axis()) + } + + /// Update partial global curve as a circle, from the provided radius + pub fn as_circle_from_radius(self, radius: impl Into) -> Self { + self.with_path(GlobalPath::circle_from_radius(radius)) + } + + /// Update partial global curve as a line, from the provided points + pub fn as_line_from_points(self, points: [impl Into>; 2]) -> Self { + self.with_path(GlobalPath::line_from_points(points)) + } + + /// Build a full [`GlobalCurve`] from the partial global curve + /// + /// # Panics + /// + /// Panics, if no path was provided. + pub fn build(self, stores: &Stores) -> Handle { + let path = self.path.expect("Can't build `GlobalCurve` without a path"); + stores.global_curves.insert(GlobalCurve::from_path(path)) + } +} diff --git a/crates/fj-kernel/src/partial/mod.rs b/crates/fj-kernel/src/partial/mod.rs index 70f138d6a..6ffa28d08 100644 --- a/crates/fj-kernel/src/partial/mod.rs +++ b/crates/fj-kernel/src/partial/mod.rs @@ -25,8 +25,10 @@ //! - All `with_*`, `as_*`, and `build` methods can be chained, to provide a //! convenient API. +mod curve; mod vertex; -pub use self::vertex::{ - PartialGlobalVertex, PartialSurfaceVertex, PartialVertex, +pub use self::{ + curve::{PartialCurve, PartialGlobalCurve}, + vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex}, };