diff --git a/crates/fj-kernel/src/algorithms/approx/curve.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs index 2e26e9d2b..2f06121a0 100644 --- a/crates/fj-kernel/src/algorithms/approx/curve.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -197,6 +197,7 @@ mod tests { use crate::{ algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint}, + builder::CurveBuilder, objects::{Curve, Objects, Surface}, partial::HasPartial, path::GlobalPath, @@ -213,7 +214,7 @@ mod tests { .insert(Surface::new(GlobalPath::x_axis(), [0., 0., 1.]))?; let curve = Curve::partial() .with_surface(Some(surface)) - .as_line_from_points([[1., 1.], [2., 1.]]) + .update_as_line_from_points([[1., 1.], [2., 1.]]) .build(&objects)?; let range = RangeOnPath::from([[0.], [1.]]); @@ -234,7 +235,7 @@ mod tests { ))?; let curve = Curve::partial() .with_surface(Some(surface)) - .as_line_from_points([[1., 1.], [1., 2.]]) + .update_as_line_from_points([[1., 1.], [1., 2.]]) .build(&objects)?; let range = RangeOnPath::from([[0.], [1.]]); @@ -253,7 +254,7 @@ mod tests { objects.surfaces.insert(Surface::new(path, [0., 0., 1.]))?; let curve = Curve::partial() .with_surface(Some(surface.clone())) - .as_line_from_points([[0., 1.], [1., 1.]]) + .update_as_line_from_points([[0., 1.], [1., 1.]]) .build(&objects)?; let range = RangeOnPath::from([[0.], [TAU]]); @@ -285,7 +286,7 @@ mod tests { .insert(Surface::new(GlobalPath::x_axis(), [0., 0., 1.]))?; let curve = Curve::partial() .with_surface(Some(surface)) - .as_circle_from_radius(1.) + .update_as_circle_from_radius(1.) .build(&objects)?; let range = RangeOnPath::from([[0.], [TAU]]); diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs index c91425878..55486f2ee 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs @@ -75,6 +75,7 @@ mod tests { use fj_math::Point; use crate::{ + builder::CurveBuilder, objects::{Curve, HalfEdge, Objects}, partial::HasPartial, }; @@ -88,7 +89,7 @@ mod tests { let surface = objects.surfaces.xy_plane(); let curve = Curve::partial() .with_surface(Some(surface.clone())) - .as_u_axis() + .update_as_u_axis() .build(&objects)?; let half_edge = HalfEdge::partial() .with_surface(Some(surface)) @@ -113,7 +114,7 @@ mod tests { let surface = objects.surfaces.xy_plane(); let curve = Curve::partial() .with_surface(Some(surface.clone())) - .as_u_axis() + .update_as_u_axis() .build(&objects)?; let half_edge = HalfEdge::partial() .with_surface(Some(surface)) @@ -138,7 +139,7 @@ mod tests { let surface = objects.surfaces.xy_plane(); let curve = Curve::partial() .with_surface(Some(surface.clone())) - .as_u_axis() + .update_as_u_axis() .build(&objects)?; let half_edge = HalfEdge::partial() .with_surface(Some(surface)) @@ -158,7 +159,7 @@ mod tests { let surface = objects.surfaces.xy_plane(); let curve = Curve::partial() .with_surface(Some(surface.clone())) - .as_u_axis() + .update_as_u_axis() .build(&objects)?; let half_edge = HalfEdge::partial() .with_surface(Some(surface)) diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs index a293fc2d5..9ede7f995 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs @@ -150,6 +150,7 @@ where #[cfg(test)] mod tests { use crate::{ + builder::CurveBuilder, objects::{Curve, Face, Objects}, partial::HasPartial, }; @@ -164,7 +165,7 @@ mod tests { let curve = Curve::partial() .with_surface(Some(surface.clone())) - .as_line_from_points([[-3., 0.], [-2., 0.]]) + .update_as_line_from_points([[-3., 0.], [-2., 0.]]) .build(&objects)?; #[rustfmt::skip] diff --git a/crates/fj-kernel/src/algorithms/intersect/face_face.rs b/crates/fj-kernel/src/algorithms/intersect/face_face.rs index 455db524b..ac9e2776c 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_face.rs @@ -67,6 +67,7 @@ mod tests { use crate::{ algorithms::intersect::CurveFaceIntersection, + builder::CurveBuilder, objects::{Curve, Face, Objects}, partial::HasPartial, }; @@ -124,7 +125,7 @@ mod tests { let expected_curves = surfaces.try_map_ext(|surface| { Curve::partial() .with_surface(Some(surface)) - .as_line_from_points([[0., 0.], [1., 0.]]) + .update_as_line_from_points([[0., 0.], [1., 0.]]) .build(&objects) })?; let expected_intervals = diff --git a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs index d69090a9f..869e7cc4a 100644 --- a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs +++ b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs @@ -92,6 +92,7 @@ mod tests { use crate::{ algorithms::transform::TransformObject, + builder::CurveBuilder, objects::{Curve, Objects}, partial::HasPartial, }; @@ -122,11 +123,11 @@ mod tests { let expected_xy = Curve::partial() .with_surface(Some(xy.clone())) - .as_u_axis() + .update_as_u_axis() .build(&objects)?; let expected_xz = Curve::partial() .with_surface(Some(xz.clone())) - .as_u_axis() + .update_as_u_axis() .build(&objects)?; assert_eq!( diff --git a/crates/fj-kernel/src/algorithms/sweep/vertex.rs b/crates/fj-kernel/src/algorithms/sweep/vertex.rs index dcb71f14b..8b55c5d46 100644 --- a/crates/fj-kernel/src/algorithms/sweep/vertex.rs +++ b/crates/fj-kernel/src/algorithms/sweep/vertex.rs @@ -168,6 +168,7 @@ mod tests { use crate::{ algorithms::sweep::Sweep, + builder::CurveBuilder, objects::{Curve, HalfEdge, Objects, Vertex}, partial::HasPartial, }; @@ -179,7 +180,7 @@ mod tests { let surface = objects.surfaces.xz_plane(); let curve = Curve::partial() .with_surface(Some(surface.clone())) - .as_u_axis() + .update_as_u_axis() .build(&objects)?; let vertex = Vertex::partial() .with_position(Some([0.])) diff --git a/crates/fj-kernel/src/algorithms/transform/curve.rs b/crates/fj-kernel/src/algorithms/transform/curve.rs index 68fcb24ca..109002f10 100644 --- a/crates/fj-kernel/src/algorithms/transform/curve.rs +++ b/crates/fj-kernel/src/algorithms/transform/curve.rs @@ -1,29 +1,24 @@ use fj_math::Transform; use crate::{ - objects::{GlobalCurve, Objects}, - partial::PartialCurve, - storage::Handle, + objects::Objects, + partial::{PartialCurve, PartialGlobalCurve}, validate::ValidationError, }; use super::TransformObject; -impl TransformObject for Handle { +impl TransformObject for PartialGlobalCurve { fn transform( self, _: &Transform, - objects: &Objects, + _: &Objects, ) -> Result { // `GlobalCurve` doesn't contain any internal geometry. If it did, that // would just be redundant with the geometry of other objects, and this // other geometry is already being transformed by other implementations // of this trait. - // - // All we need to do here is create a new `GlobalCurve` instance, to - // make sure the transformed `GlobalCurve` has a different identity than - // the original one. - Ok(objects.global_curves.insert(GlobalCurve)?) + Ok(self) } } @@ -34,20 +29,19 @@ impl TransformObject for PartialCurve { objects: &Objects, ) -> Result { let surface = self - .surface + .surface() .map(|surface| surface.transform(transform, objects)) .transpose()?; let global_form = self - .global_form - .map(|global_form| global_form.0.transform(transform, objects)) + .global_form() + .map(|global_form| global_form.transform(transform, objects)) .transpose()?; // Don't need to transform `self.path`, as that's defined in surface // coordinates, and thus transforming `surface` takes care of it. - Ok(Self { - surface, - path: self.path, - global_form: global_form.map(Into::into), - }) + Ok(Self::default() + .with_surface(surface) + .with_path(self.path()) + .with_global_form(global_form)) } } diff --git a/crates/fj-kernel/src/algorithms/transform/edge.rs b/crates/fj-kernel/src/algorithms/transform/edge.rs index 0a997a3c4..2d9b3b95f 100644 --- a/crates/fj-kernel/src/algorithms/transform/edge.rs +++ b/crates/fj-kernel/src/algorithms/transform/edge.rs @@ -57,10 +57,7 @@ impl TransformObject for PartialGlobalEdge { transform: &Transform, objects: &Objects, ) -> Result { - let curve = self - .curve - .map(|curve| curve.0.transform(transform, objects)) - .transpose()?; + let curve = self.curve.transform(transform, objects)?; let vertices = self .vertices .map(|vertices| { @@ -70,9 +67,6 @@ impl TransformObject for PartialGlobalEdge { }) .transpose()?; - Ok(Self { - curve: curve.map(Into::into), - vertices, - }) + Ok(Self { curve, vertices }) } } diff --git a/crates/fj-kernel/src/algorithms/transform/vertex.rs b/crates/fj-kernel/src/algorithms/transform/vertex.rs index 32e11f798..a1a370621 100644 --- a/crates/fj-kernel/src/algorithms/transform/vertex.rs +++ b/crates/fj-kernel/src/algorithms/transform/vertex.rs @@ -14,20 +14,19 @@ impl TransformObject for PartialVertex { transform: &Transform, objects: &Objects, ) -> Result { - let curve = self.curve.transform(transform, objects)?; + let curve = self.curve().transform(transform, objects)?; let surface_form = self - .surface_form + .surface_form() .into_partial() .transform(transform, objects)? .into(); // Don't need to transform `self.position`, as that is in curve // coordinates and thus transforming the curve takes care of it. - Ok(Self { - position: self.position, - curve, - surface_form, - }) + Ok(Self::default() + .with_position(self.position()) + .with_curve(Some(curve)) + .with_surface_form(surface_form)) } } @@ -38,18 +37,17 @@ impl TransformObject for PartialSurfaceVertex { objects: &Objects, ) -> Result { let surface = self - .surface + .surface() .map(|surface| surface.transform(transform, objects)) .transpose()?; - let global_form = self.global_form.transform(transform, objects)?; + let global_form = self.global_form().transform(transform, objects)?; // Don't need to transform `self.position`, as that is in surface // coordinates and thus transforming the surface takes care of it. - Ok(Self { - position: self.position, - surface, - global_form, - }) + Ok(Self::default() + .with_position(self.position()) + .with_surface(surface) + .with_global_form(Some(global_form))) } } @@ -60,9 +58,9 @@ impl TransformObject for PartialGlobalVertex { _: &Objects, ) -> Result { let position = self - .position + .position() .map(|position| transform.transform_point(&position)); - Ok(Self { position }) + Ok(Self::default().with_position(position)) } } diff --git a/crates/fj-kernel/src/builder/curve.rs b/crates/fj-kernel/src/builder/curve.rs new file mode 100644 index 000000000..9135009e6 --- /dev/null +++ b/crates/fj-kernel/src/builder/curve.rs @@ -0,0 +1,48 @@ +use fj_math::{Point, Scalar, Vector}; + +use crate::{partial::PartialCurve, path::SurfacePath}; + +/// Builder API for [`PartialCurve`] +pub trait CurveBuilder { + /// Update partial curve to represent the u-axis + fn update_as_u_axis(self) -> Self; + + /// Update partial curve to represent the v-axis + fn update_as_v_axis(self) -> Self; + + /// Update partial curve as a circle, from the provided radius + fn update_as_circle_from_radius(self, radius: impl Into) -> Self; + + /// Update partial curve as a line, from the provided points + fn update_as_line_from_points( + self, + points: [impl Into>; 2], + ) -> Self; +} + +impl CurveBuilder for PartialCurve { + fn update_as_u_axis(self) -> Self { + let a = Point::origin(); + let b = a + Vector::unit_u(); + + self.update_as_line_from_points([a, b]) + } + + fn update_as_v_axis(self) -> Self { + let a = Point::origin(); + let b = a + Vector::unit_v(); + + self.update_as_line_from_points([a, b]) + } + + fn update_as_circle_from_radius(self, radius: impl Into) -> Self { + self.with_path(Some(SurfacePath::circle_from_radius(radius))) + } + + fn update_as_line_from_points( + self, + points: [impl Into>; 2], + ) -> Self { + self.with_path(Some(SurfacePath::line_from_points(points))) + } +} diff --git a/crates/fj-kernel/src/builder/mod.rs b/crates/fj-kernel/src/builder/mod.rs index 70b8e6fcf..ab4abf95d 100644 --- a/crates/fj-kernel/src/builder/mod.rs +++ b/crates/fj-kernel/src/builder/mod.rs @@ -1,11 +1,22 @@ //! API for building objects +// These are the old-style builders that need to be transferred to the partial +// object API. Issue: +// https://github.com/hannobraun/Fornjot/issues/1147 mod face; mod shell; mod sketch; mod solid; +// These are new-style builders that build on top of the partial object API. +mod curve; +mod vertex; + pub use self::{ - face::FaceBuilder, shell::ShellBuilder, sketch::SketchBuilder, + curve::CurveBuilder, + face::FaceBuilder, + shell::ShellBuilder, + sketch::SketchBuilder, solid::SolidBuilder, + vertex::{GlobalVertexBuilder, SurfaceVertexBuilder, VertexBuilder}, }; diff --git a/crates/fj-kernel/src/builder/vertex.rs b/crates/fj-kernel/src/builder/vertex.rs new file mode 100644 index 000000000..56c817738 --- /dev/null +++ b/crates/fj-kernel/src/builder/vertex.rs @@ -0,0 +1,78 @@ +use fj_math::Point; + +use crate::{ + objects::{Curve, GlobalVertex, Surface}, + partial::{ + HasPartial, MaybePartial, PartialGlobalVertex, PartialSurfaceVertex, + PartialVertex, + }, +}; + +/// Builder API for [`PartialVertex`] +pub trait VertexBuilder { + /// Remove the surface form of the partial vertex, inferring it on build + fn infer_surface_form(self) -> Self; +} + +impl VertexBuilder for PartialVertex { + fn infer_surface_form(self) -> Self { + self.with_surface_form(Some(PartialSurfaceVertex::default())) + } +} + +/// Builder API for [`PartialSurfaceVertex`] +pub trait SurfaceVertexBuilder { + /// Infer the global form of the partial vertex + fn infer_global_form(self) -> Self; +} + +impl SurfaceVertexBuilder for PartialSurfaceVertex { + fn infer_global_form(self) -> Self { + self.with_global_form(Some(GlobalVertex::partial())) + } +} + +/// Builder API for [`PartialGlobalVertex`] +pub trait GlobalVertexBuilder { + /// Update partial global vertex from the given curve and position on it + fn update_from_curve_and_position( + self, + curve: impl Into>, + position: impl Into>, + ) -> Self; + + /// Update partial global vertex from the given surface and position on it + fn update_from_surface_and_position( + self, + surface: &Surface, + position: impl Into>, + ) -> Self; +} + +impl GlobalVertexBuilder for PartialGlobalVertex { + fn update_from_curve_and_position( + self, + curve: impl Into>, + position: impl Into>, + ) -> Self { + let curve = curve.into().into_partial(); + + let path = curve.path().expect( + "Need path to create `GlobalVertex` from curve and position", + ); + let surface = curve.surface().expect( + "Need surface to create `GlobalVertex` from curve and position", + ); + + let position_surface = path.point_from_path_coords(position); + self.update_from_surface_and_position(&surface, position_surface) + } + + fn update_from_surface_and_position( + self, + surface: &Surface, + position: impl Into>, + ) -> Self { + self.with_position(Some(surface.point_from_surface_coords(position))) + } +} diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index 43b34f558..2538869c6 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -360,6 +360,7 @@ impl Iterator for Iter { #[cfg(test)] mod tests { use crate::{ + builder::CurveBuilder, objects::{ Curve, Cycle, Face, GlobalCurve, GlobalVertex, HalfEdge, Objects, Shell, Sketch, Solid, SurfaceVertex, Vertex, @@ -376,7 +377,7 @@ mod tests { let surface = objects.surfaces.xy_plane(); let object = Curve::partial() .with_surface(Some(surface)) - .as_u_axis() + .update_as_u_axis() .build(&objects); assert_eq!(1, object.curve_iter().count()); @@ -593,7 +594,7 @@ mod tests { let surface = objects.surfaces.xy_plane(); let curve = Curve::partial() .with_surface(Some(surface.clone())) - .as_u_axis() + .update_as_u_axis() .build(&objects)?; let global_vertex = objects .global_vertices diff --git a/crates/fj-kernel/src/partial/maybe_partial.rs b/crates/fj-kernel/src/partial/maybe_partial.rs index 71406b779..5b5e43988 100644 --- a/crates/fj-kernel/src/partial/maybe_partial.rs +++ b/crates/fj-kernel/src/partial/maybe_partial.rs @@ -5,6 +5,7 @@ use crate::{ Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects, Surface, SurfaceVertex, Vertex, }, + path::SurfacePath, storage::Handle, validate::ValidationError, }; @@ -30,6 +31,24 @@ pub enum MaybePartial { } impl MaybePartial { + /// Indicate whether this is a full object + pub fn is_full(&self) -> bool { + if let Self::Full(_) = self { + return true; + } + + false + } + + /// Indicate whether this is a partial object + pub fn is_partial(&self) -> bool { + if let Self::Partial(_) = self { + return true; + } + + false + } + /// If this is a partial object, update it /// /// This is useful whenever a partial object can infer something about its @@ -95,25 +114,37 @@ where // `MaybePartial`, as that would conflict. impl MaybePartial { - /// Access the global form - pub fn global_form(&self) -> Option> { + /// Access the path + pub fn path(&self) -> Option { match self { - Self::Full(full) => Some(full.global_form().clone()), - Self::Partial(partial) => { - partial.global_form.clone().map(Into::into) - } + MaybePartial::Full(full) => Some(full.path()), + MaybePartial::Partial(partial) => partial.path(), + } + } + + /// Access the surface + pub fn surface(&self) -> Option> { + match self { + MaybePartial::Full(full) => Some(full.surface().clone()), + MaybePartial::Partial(partial) => partial.surface(), + } + } + + /// Access the global form + pub fn global_form(&self) -> Option> { + match self { + Self::Full(full) => Some(full.global_form().clone().into()), + Self::Partial(partial) => partial.global_form(), } } } impl MaybePartial { /// Access the curve - pub fn curve(&self) -> Option<&Handle> { + pub fn curve(&self) -> MaybePartial { match self { - Self::Full(full) => Some(full.curve()), - Self::Partial(partial) => { - partial.curve.as_ref().map(|wrapper| &wrapper.0) - } + Self::Full(full) => full.curve().clone().into(), + Self::Partial(partial) => partial.curve.clone(), } } @@ -154,15 +185,15 @@ impl MaybePartial { pub fn position(&self) -> Option> { match self { Self::Full(full) => Some(full.position()), - Self::Partial(partial) => partial.position, + Self::Partial(partial) => partial.position(), } } /// Access the surface - pub fn surface(&self) -> Option<&Handle> { + pub fn surface(&self) -> Option> { match self { - Self::Full(full) => Some(full.surface()), - Self::Partial(partial) => partial.surface.as_ref(), + Self::Full(full) => Some(full.surface().clone()), + Self::Partial(partial) => partial.surface(), } } } @@ -172,7 +203,7 @@ impl MaybePartial { pub fn surface_form(&self) -> MaybePartial { match self { Self::Full(full) => full.surface_form().clone().into(), - Self::Partial(partial) => partial.surface_form.clone(), + Self::Partial(partial) => partial.surface_form(), } } } diff --git a/crates/fj-kernel/src/partial/mod.rs b/crates/fj-kernel/src/partial/mod.rs index 87567f44d..87ac53f2d 100644 --- a/crates/fj-kernel/src/partial/mod.rs +++ b/crates/fj-kernel/src/partial/mod.rs @@ -41,7 +41,7 @@ mod traits; pub use self::{ maybe_partial::MaybePartial, objects::{ - curve::PartialCurve, + curve::{PartialCurve, PartialGlobalCurve}, cycle::PartialCycle, edge::{PartialGlobalEdge, PartialHalfEdge}, vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex}, diff --git a/crates/fj-kernel/src/partial/objects/curve.rs b/crates/fj-kernel/src/partial/objects/curve.rs index b0d0a2343..f55b97924 100644 --- a/crates/fj-kernel/src/partial/objects/curve.rs +++ b/crates/fj-kernel/src/partial/objects/curve.rs @@ -1,35 +1,37 @@ -use fj_math::{Point, Scalar, Vector}; - use crate::{ objects::{Curve, GlobalCurve, Objects, Surface}, + partial::MaybePartial, path::SurfacePath, - storage::{Handle, HandleWrapper}, + storage::Handle, validate::ValidationError, }; /// A partial [`Curve`] /// /// See [`crate::partial`] for more information. -#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[derive(Clone, Debug, 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>, + path: Option, + surface: Option>, + global_form: Option>, } impl PartialCurve { + /// Access the path that defines the [`Curve`] + pub fn path(&self) -> Option { + self.path + } + + /// Access the surface that the [`Curve`] is defined in + pub fn surface(&self) -> Option> { + self.surface.clone() + } + + /// Access the global form of the [`Curve`] + pub fn global_form(&self) -> Option> { + self.global_form.clone() + } + /// Provide a path for the partial curve pub fn with_path(mut self, path: Option) -> Self { if let Some(path) = path { @@ -49,7 +51,7 @@ impl PartialCurve { /// Provide a global form for the partial curve pub fn with_global_form( mut self, - global_form: Option>>, + global_form: Option>>, ) -> Self { if let Some(global_form) = global_form { self.global_form = Some(global_form.into()); @@ -57,32 +59,6 @@ impl PartialCurve { 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(Some(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(Some(SurfacePath::line_from_points(points))) - } - /// Build a full [`Curve`] from the partial curve pub fn build( self, @@ -95,7 +71,8 @@ impl PartialCurve { let global_form = match self.global_form { Some(global_form) => global_form, None => objects.global_curves.insert(GlobalCurve)?.into(), - }; + } + .into_full(objects)?; Ok(objects .curves @@ -112,3 +89,31 @@ impl From<&Curve> for PartialCurve { } } } + +/// A partial [`GlobalCurve`] +/// +/// This struct might seem unnecessary. [`GlobalCurve`] literally has nothing in +/// it. Why would we need to represent a part of nothing? However, having this +/// provides some regularity that helps simplify some things within the partial +/// object and builder APIs. +/// +/// See [`crate::partial`] for more information. +#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub struct PartialGlobalCurve; + +impl PartialGlobalCurve { + /// Build a full [`GlobalCurve`] from the partial global curve + pub fn build( + self, + objects: &Objects, + ) -> Result, ValidationError> { + let global_curve = objects.global_curves.insert(GlobalCurve)?; + Ok(global_curve) + } +} + +impl From<&GlobalCurve> for PartialGlobalCurve { + fn from(_: &GlobalCurve) -> Self { + Self + } +} diff --git a/crates/fj-kernel/src/partial/objects/cycle.rs b/crates/fj-kernel/src/partial/objects/cycle.rs index e96be1939..65c16f6d4 100644 --- a/crates/fj-kernel/src/partial/objects/cycle.rs +++ b/crates/fj-kernel/src/partial/objects/cycle.rs @@ -1,6 +1,7 @@ use fj_math::Point; use crate::{ + builder::CurveBuilder, objects::{ Curve, Cycle, HalfEdge, Objects, Surface, SurfaceVertex, Vertex, }, @@ -12,7 +13,7 @@ use crate::{ /// A partial [`Cycle`] /// /// See [`crate::partial`] for more information. -#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[derive(Clone, Debug, Default)] pub struct PartialCycle { /// The surface that the [`Cycle`] is defined in pub surface: Option>, @@ -82,7 +83,7 @@ impl PartialCycle { let curve = Curve::partial() .with_surface(Some(surface.clone())) - .as_line_from_points([position_prev, position_next]); + .update_as_line_from_points([position_prev, position_next]); let [from, to] = [(0., from), (1., to)].map(|(position, surface_form)| { diff --git a/crates/fj-kernel/src/partial/objects/edge.rs b/crates/fj-kernel/src/partial/objects/edge.rs index 5faf78ef3..3df61f925 100644 --- a/crates/fj-kernel/src/partial/objects/edge.rs +++ b/crates/fj-kernel/src/partial/objects/edge.rs @@ -2,19 +2,20 @@ use fj_interop::ext::ArrayExt; use fj_math::{Point, Scalar}; use crate::{ + builder::{CurveBuilder, GlobalVertexBuilder}, objects::{ Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects, Surface, SurfaceVertex, Vertex, VerticesInNormalizedOrder, }, partial::{HasPartial, MaybePartial}, - storage::{Handle, HandleWrapper}, + storage::Handle, validate::ValidationError, }; /// A partial [`HalfEdge`] /// /// See [`crate::partial`] for more information. -#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[derive(Clone, Debug, Default)] pub struct PartialHalfEdge { /// The surface that the [`HalfEdge`]'s [`Curve`] is defined in pub surface: Option>, @@ -35,10 +36,10 @@ impl PartialHalfEdge { /// Extract the global curve from either the curve or global form /// /// If a global curve is available through both, the curve is preferred. - pub fn extract_global_curve(&self) -> Option> { + pub fn extract_global_curve(&self) -> MaybePartial { self.curve .global_form() - .or_else(|| self.global_form.curve().cloned()) + .unwrap_or_else(|| self.global_form.curve()) } /// Access the vertices of the global form, if available @@ -127,11 +128,11 @@ impl PartialHalfEdge { objects: &Objects, ) -> Result { let curve = Curve::partial() - .with_global_form(self.extract_global_curve()) + .with_global_form(Some(self.extract_global_curve())) .with_surface(self.surface.clone()) - .as_circle_from_radius(radius); + .update_as_circle_from_radius(radius); - let path = curve.path.expect("Expected path that was just created"); + let path = curve.path().expect("Expected path that was just created"); let [a_curve, b_curve] = [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); @@ -141,7 +142,7 @@ impl PartialHalfEdge { .map(|[global_form, _]| global_form) .unwrap_or_else(|| { GlobalVertex::partial() - .from_curve_and_position(curve.clone(), a_curve) + .update_from_curve_and_position(curve.clone(), a_curve) .into() }); @@ -190,11 +191,10 @@ impl PartialHalfEdge { let surface = self .surface - .as_ref() + .clone() .or_else(|| from_surface.surface()) .or_else(|| to_surface.surface()) - .expect("Can't infer line segment without a surface") - .clone(); + .expect("Can't infer line segment without a surface"); let points = [&from_surface, &to_surface].map(|vertex| { vertex .position() @@ -202,9 +202,9 @@ impl PartialHalfEdge { }); let curve = Curve::partial() - .with_global_form(self.extract_global_curve()) + .with_global_form(Some(self.extract_global_curve())) .with_surface(Some(surface)) - .as_line_from_points(points); + .update_as_line_from_points(points); let [back, front] = { let vertices = [(from, 0.), (to, 1.)].map(|(vertex, position)| { @@ -252,9 +252,11 @@ impl PartialHalfEdge { vertices.zip_ext(global_forms).map(|(vertex, global_form)| { vertex.update_partial(|vertex| { vertex.clone().with_surface_form(Some( - vertex.surface_form.update_partial(|surface_vertex| { - surface_vertex.with_global_form(global_form) - }), + vertex.surface_form().update_partial( + |surface_vertex| { + surface_vertex.with_global_form(global_form) + }, + ), )) }) }) @@ -312,12 +314,12 @@ impl From<&HalfEdge> for PartialHalfEdge { /// A partial [`GlobalEdge`] /// /// See [`crate::partial`] for more information. -#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[derive(Clone, Debug, Default)] pub struct PartialGlobalEdge { /// The curve that the [`GlobalEdge`] is defined in /// /// Must be provided before [`PartialGlobalEdge::build`] is called. - pub curve: Option>, + pub curve: MaybePartial, /// The vertices that bound the [`GlobalEdge`] in the curve /// @@ -327,9 +329,12 @@ pub struct PartialGlobalEdge { impl PartialGlobalEdge { /// Update the partial global edge with the given curve - pub fn with_curve(mut self, curve: Option>) -> Self { + pub fn with_curve( + mut self, + curve: Option>>, + ) -> Self { if let Some(curve) = curve { - self.curve = Some(curve.into()); + self.curve = curve.into(); } self } @@ -362,9 +367,7 @@ impl PartialGlobalEdge { self, objects: &Objects, ) -> Result, ValidationError> { - let curve = self - .curve - .expect("Can't build `GlobalEdge` without `GlobalCurve`"); + let curve = self.curve.into_full(objects)?; let vertices = self .vertices .expect("Can't build `GlobalEdge` without vertices") @@ -379,7 +382,7 @@ impl PartialGlobalEdge { impl From<&GlobalEdge> for PartialGlobalEdge { fn from(global_edge: &GlobalEdge) -> Self { Self { - curve: Some(global_edge.curve().clone().into()), + curve: global_edge.curve().clone().into(), vertices: Some( global_edge .vertices() diff --git a/crates/fj-kernel/src/partial/objects/mod.rs b/crates/fj-kernel/src/partial/objects/mod.rs index 19d5ef35a..4032519a7 100644 --- a/crates/fj-kernel/src/partial/objects/mod.rs +++ b/crates/fj-kernel/src/partial/objects/mod.rs @@ -5,7 +5,7 @@ pub mod vertex; use crate::{ objects::{ - Curve, Cycle, GlobalEdge, GlobalVertex, HalfEdge, Objects, + Curve, Cycle, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects, SurfaceVertex, Vertex, }, storage::Handle, @@ -13,8 +13,8 @@ use crate::{ use super::{ HasPartial, MaybePartial, Partial, PartialCurve, PartialCycle, - PartialGlobalEdge, PartialGlobalVertex, PartialHalfEdge, - PartialSurfaceVertex, PartialVertex, + PartialGlobalCurve, PartialGlobalEdge, PartialGlobalVertex, + PartialHalfEdge, PartialSurfaceVertex, PartialVertex, }; macro_rules! impl_traits { @@ -49,6 +49,7 @@ macro_rules! impl_traits { impl_traits!( Curve, PartialCurve; Cycle, PartialCycle; + GlobalCurve, PartialGlobalCurve; GlobalEdge, PartialGlobalEdge; GlobalVertex, PartialGlobalVertex; HalfEdge, PartialHalfEdge; diff --git a/crates/fj-kernel/src/partial/objects/vertex.rs b/crates/fj-kernel/src/partial/objects/vertex.rs index 75cd15a4a..778d9b068 100644 --- a/crates/fj-kernel/src/partial/objects/vertex.rs +++ b/crates/fj-kernel/src/partial/objects/vertex.rs @@ -1,8 +1,9 @@ use fj_math::Point; use crate::{ + builder::GlobalVertexBuilder, objects::{Curve, GlobalVertex, Objects, Surface, SurfaceVertex, Vertex}, - partial::{HasPartial, MaybePartial}, + partial::MaybePartial, storage::Handle, validate::ValidationError, }; @@ -10,26 +11,29 @@ use crate::{ /// A partial [`Vertex`] /// /// See [`crate::partial`] for more information. -#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] +#[derive(Clone, Debug, Default)] pub struct PartialVertex { - /// The position of the [`Vertex`] on the [`Curve`] - /// - /// Must be provided before [`PartialVertex::build`] is called. - pub position: Option>, - - /// The curve that the [`Vertex`] is defined in - /// - /// Must be provided before [`PartialVertex::build`] is called. - pub curve: MaybePartial, - - /// The surface form of the [`Vertex`] - /// - /// Can be provided, if already available, or computed from the position on - /// the [`Curve`]. - pub surface_form: MaybePartial, + position: Option>, + curve: MaybePartial, + surface_form: MaybePartial, } impl PartialVertex { + /// Access the position of the [`Vertex`] on the curve + pub fn position(&self) -> Option> { + self.position + } + + /// Access the curve that the [`Vertex`] is defined in + pub fn curve(&self) -> MaybePartial { + self.curve.clone() + } + + /// Access the surface form of the [`Vertex`] + pub fn surface_form(&self) -> MaybePartial { + self.surface_form.clone() + } + /// Provide a position for the partial vertex pub fn with_position( mut self, @@ -63,19 +67,13 @@ impl PartialVertex { self } - /// Remove the surface form of the partial vertex, inferring it on build - pub fn infer_surface_form(mut self) -> Self { - self.surface_form = SurfaceVertex::partial().into(); - self - } - /// Build a full [`Vertex`] from the partial vertex /// /// # Panics /// - /// Panics, if no position has been provided. + /// Panics, if position has not been provided. /// - /// Panics, if no curve has been provided. + /// Panics, if curve has not been provided. pub fn build( self, objects: &Objects, @@ -121,24 +119,27 @@ impl From<&Vertex> for PartialVertex { /// See [`crate::partial`] for more information. #[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct PartialSurfaceVertex { - /// The position of the [`SurfaceVertex`] in the [`Surface`] - /// - /// Must be provided before [`PartialSurfaceVertex::build`] is called. - pub position: Option>, - - /// The surface that the [`SurfaceVertex`] is defined in - /// - /// Must be provided before [`PartialSurfaceVertex::build`] is called. - pub surface: Option>, - - /// The global form of the [`SurfaceVertex`] - /// - /// Can be provided, if already available, or computed from the position on - /// the [`Surface`]. - pub global_form: MaybePartial, + position: Option>, + surface: Option>, + global_form: MaybePartial, } impl PartialSurfaceVertex { + /// Access the position of the [`SurfaceVertex`] + pub fn position(&self) -> Option> { + self.position + } + + /// Access the surface that the [`SurfaceVertex`] is defined in + pub fn surface(&self) -> Option> { + self.surface.clone() + } + + /// Access the global form of the [`SurfaceVertex`] + pub fn global_form(&self) -> MaybePartial { + self.global_form.clone() + } + /// Provide a position for the partial surface vertex pub fn with_position( mut self, @@ -169,19 +170,7 @@ impl PartialSurfaceVertex { self } - /// Remove the global form of the partial vertex, inferring it on build - pub fn infer_global_form(mut self) -> Self { - self.global_form = GlobalVertex::partial().into(); - self - } - /// Build a full [`SurfaceVertex`] from the partial surface vertex - /// - /// # Panics - /// - /// Panics, if no position has been provided. - /// - /// Panics, if no surface has been provided. pub fn build( self, objects: &Objects, @@ -196,7 +185,7 @@ impl PartialSurfaceVertex { let global_form = self .global_form .update_partial(|global_form| { - global_form.from_surface_and_position(&surface, position) + global_form.update_from_surface_and_position(&surface, position) }) .into_full(objects)?; @@ -223,13 +212,15 @@ impl From<&SurfaceVertex> for PartialSurfaceVertex { /// See [`crate::partial`] for more information. #[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct PartialGlobalVertex { - /// The position of the [`GlobalVertex`] - /// - /// Must be provided before [`PartialGlobalVertex::build`] is called. - pub position: Option>, + position: Option>, } impl PartialGlobalVertex { + /// Access the position of the [`GlobalVertex`] + pub fn position(&self) -> Option> { + self.position + } + /// Provide a position for the partial global vertex pub fn with_position( mut self, @@ -241,35 +232,6 @@ impl PartialGlobalVertex { self } - /// Update partial global vertex from the given curve and position on it - pub fn from_curve_and_position( - self, - curve: impl Into>, - position: impl Into>, - ) -> Self { - let curve = curve.into().into_partial(); - - let path = curve.path.expect( - "Need path to create `GlobalVertex` from curve and position", - ); - let surface = curve.surface.expect( - "Need surface to create `GlobalVertex` from curve and position", - ); - - let position_surface = path.point_from_path_coords(position); - self.from_surface_and_position(&surface, position_surface) - } - - /// Update partial global vertex from the given surface and position on it - pub fn from_surface_and_position( - mut self, - surface: &Surface, - position: impl Into>, - ) -> Self { - self.position = Some(surface.point_from_surface_coords(position)); - self - } - /// Build a full [`GlobalVertex`] from the partial global vertex pub fn build( self, diff --git a/crates/fj-kernel/src/validate/edge.rs b/crates/fj-kernel/src/validate/edge.rs index 31779a45b..fddd9a618 100644 --- a/crates/fj-kernel/src/validate/edge.rs +++ b/crates/fj-kernel/src/validate/edge.rs @@ -196,6 +196,7 @@ mod tests { use fj_interop::ext::ArrayExt; use crate::{ + builder::VertexBuilder, objects::{GlobalCurve, HalfEdge, Objects}, partial::HasPartial, validate::Validate2, diff --git a/crates/fj-kernel/src/validate/vertex.rs b/crates/fj-kernel/src/validate/vertex.rs index 790c88aba..2257510e0 100644 --- a/crates/fj-kernel/src/validate/vertex.rs +++ b/crates/fj-kernel/src/validate/vertex.rs @@ -179,6 +179,7 @@ impl SurfaceVertexValidationError { #[cfg(test)] mod tests { use crate::{ + builder::{CurveBuilder, SurfaceVertexBuilder}, objects::{Curve, GlobalVertex, Objects, SurfaceVertex, Vertex}, partial::HasPartial, validate::Validate2, @@ -193,7 +194,7 @@ mod tests { .with_curve(Some( Curve::partial() .with_surface(Some(objects.surfaces.xy_plane())) - .as_u_axis(), + .update_as_u_axis(), )) .build(&objects)?; let invalid = Vertex::new( @@ -221,7 +222,7 @@ mod tests { .with_curve(Some( Curve::partial() .with_surface(Some(objects.surfaces.xy_plane())) - .as_u_axis(), + .update_as_u_axis(), )) .build(&objects)?; let invalid = Vertex::new(