diff --git a/crates/fj-kernel/src/partial/curve.rs b/crates/fj-kernel/src/partial/curve.rs index 58f411286..40350fed3 100644 --- a/crates/fj-kernel/src/partial/curve.rs +++ b/crates/fj-kernel/src/partial/curve.rs @@ -6,6 +6,8 @@ use crate::{ stores::{Handle, Stores}, }; +use super::MaybePartial; + /// A partial [`Curve`] /// /// See [`crate::partial`] for more information. @@ -25,7 +27,7 @@ pub struct PartialCurve { /// /// Will be computed from `path` and `surface` in [`PartialCurve::build`], /// if not provided. - pub global_form: Option>, + pub global_form: Option>>, } impl PartialCurve { @@ -44,9 +46,9 @@ impl PartialCurve { /// Provide a global form for the partial curve pub fn with_global_form( mut self, - global_form: Handle, + global_form: impl Into>>, ) -> Self { - self.global_form = Some(global_form); + self.global_form = Some(global_form.into()); self } @@ -82,19 +84,23 @@ impl PartialCurve { 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) - }); + 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).into() + }) + .into_full(stores); Curve::new(surface, path, global_form) } @@ -105,7 +111,7 @@ impl From for PartialCurve { Self { path: Some(curve.path()), surface: Some(*curve.surface()), - global_form: Some(curve.global_form().clone()), + global_form: Some(curve.global_form().clone().into()), } } } diff --git a/crates/fj-kernel/src/partial/edge.rs b/crates/fj-kernel/src/partial/edge.rs index aefe4865e..55639ef52 100644 --- a/crates/fj-kernel/src/partial/edge.rs +++ b/crates/fj-kernel/src/partial/edge.rs @@ -7,7 +7,7 @@ use crate::{ stores::{Handle, Stores}, }; -use super::MaybePartial; +use super::{MaybePartial, PartialCurve}; /// A partial [`HalfEdge`] /// @@ -18,12 +18,12 @@ pub struct PartialHalfEdge { pub curve: Option>, /// The vertices that bound this [`HalfEdge`] in the [`Curve`] - pub vertices: [Option>; 2], + pub vertices: Option<[MaybePartial; 2]>, /// The global form of the [`HalfEdge`] /// /// Can be computed by [`PartialHalfEdge::build`], if not available. - pub global_form: Option, + pub global_form: Option>, } impl PartialHalfEdge { @@ -38,31 +38,16 @@ impl PartialHalfEdge { mut self, vertices: [impl Into>; 2], ) -> Self { - self.vertices = vertices.map(Into::into).map(Some); + self.vertices = Some(vertices.map(Into::into)); self } - /// Update the partial half-edge, starting it from the given vertex - pub fn with_from_vertex( - mut self, - vertex: impl Into>, - ) -> Self { - self.vertices[0] = Some(vertex.into()); - self - } - - /// Update the partial half-edge with the given end vertex - pub fn with_to_vertex( + /// Update the partial half-edge with the given global form + pub fn with_global_form( mut self, - vertex: impl Into>, + global_form: impl Into>, ) -> Self { - self.vertices[1] = Some(vertex.into()); - self - } - - /// Update the partial half-edge with the given global form - pub fn with_global_form(mut self, global_form: GlobalEdge) -> Self { - self.global_form = Some(global_form); + self.global_form = Some(global_form.into()); self } @@ -92,7 +77,7 @@ impl PartialHalfEdge { }; self.curve = Some(curve.into()); - self.vertices = vertices.map(Into::into).map(Some); + self.vertices = Some(vertices.map(Into::into)); self } @@ -103,9 +88,12 @@ impl PartialHalfEdge { surface: Surface, points: [impl Into>; 2], ) -> Self { - let curve = Curve::partial() - .with_surface(surface) - .as_line_from_points(points); + let curve = PartialCurve { + global_form: self.extract_global_curve(), + ..PartialCurve::default() + } + .with_surface(surface) + .as_line_from_points(points); let vertices = [0., 1.].map(|position| { Vertex::partial() @@ -114,7 +102,42 @@ impl PartialHalfEdge { }); self.curve = Some(curve.into()); - self.vertices = vertices.map(Into::into).map(Some); + self.vertices = Some(vertices.map(Into::into)); + + self + } + + /// Update partial half-edge as a line segment, reusing existing vertices + pub fn as_line_segment(mut self) -> Self { + let [from, to] = self + .vertices + .clone() + .expect("Can't infer line segment without vertices") + .map(|vertex| { + vertex.surface_form().expect( + "Can't infer line segment without two surface vertices", + ) + }); + + let surface = from + .surface() + .copied() + .or_else(|| to.surface().copied()) + .expect("Can't infer line segment without a surface"); + let points = [from, to].map(|vertex| { + vertex + .position() + .expect("Can't infer line segment without surface position") + }); + + let curve = PartialCurve { + global_form: self.extract_global_curve(), + ..PartialCurve::default() + } + .with_surface(surface) + .as_line_from_points(points); + + self.curve = Some(curve.into()); self } @@ -125,28 +148,37 @@ impl PartialHalfEdge { .curve .expect("Can't build `HalfEdge` without curve") .into_full(stores); - let vertices = self.vertices.map(|vertex| { - vertex - .expect("Can't build `HalfEdge` without vertices") - .into_full(stores) - }); - - let global_form = self.global_form.unwrap_or_else(|| { - GlobalEdge::partial() - .from_curve_and_vertices(&curve, &vertices) - .build(stores) - }); + let vertices = self + .vertices + .expect("Can't build `HalfEdge` without vertices") + .map(|vertex| vertex.into_full(stores)); + + let global_form = self + .global_form + .unwrap_or_else(|| { + GlobalEdge::partial() + .from_curve_and_vertices(&curve, &vertices) + .into() + }) + .into_full(stores); HalfEdge::new(curve, vertices, global_form) } + + fn extract_global_curve( + &self, + ) -> Option>> { + let global_curve = self.global_form.as_ref()?.curve()?.clone(); + Some(global_curve.into()) + } } impl From for PartialHalfEdge { fn from(half_edge: HalfEdge) -> Self { Self { curve: Some(half_edge.curve().clone().into()), - vertices: half_edge.vertices().clone().map(Into::into).map(Some), - global_form: Some(half_edge.global_form().clone()), + vertices: Some(half_edge.vertices().clone().map(Into::into)), + global_form: Some(half_edge.global_form().clone().into()), } } } diff --git a/crates/fj-kernel/src/partial/maybe_partial.rs b/crates/fj-kernel/src/partial/maybe_partial.rs new file mode 100644 index 000000000..277ef9902 --- /dev/null +++ b/crates/fj-kernel/src/partial/maybe_partial.rs @@ -0,0 +1,74 @@ +use fj_math::Point; + +use crate::{ + objects::{GlobalCurve, GlobalEdge, Surface, SurfaceVertex, Vertex}, + stores::{Handle, Stores}, +}; + +use super::HasPartialForm; + +/// Either a partial object or a full one +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] +pub enum MaybePartial { + /// A full object + Full(T), + + /// A partial object + Partial(T::PartialForm), +} + +impl MaybePartial { + /// Return the full object, either directly or by building it + pub fn into_full(self, stores: &Stores) -> T { + match self { + Self::Partial(partial) => T::from_partial(partial, stores), + Self::Full(full) => full, + } + } + + /// Return the partial object, either directly or via conversion + pub fn into_partial(self) -> T::PartialForm { + match self { + Self::Partial(partial) => partial, + Self::Full(full) => full.into(), + } + } +} + +impl MaybePartial { + /// Access the curve + pub fn curve(&self) -> Option<&Handle> { + match self { + Self::Full(full) => Some(full.curve()), + Self::Partial(partial) => partial.curve.as_ref(), + } + } +} + +impl MaybePartial { + /// Access the position + pub fn position(&self) -> Option> { + match self { + Self::Full(full) => Some(full.position()), + Self::Partial(partial) => partial.position, + } + } + + /// Access the surface + pub fn surface(&self) -> Option<&Surface> { + match self { + Self::Full(full) => Some(full.surface()), + Self::Partial(partial) => partial.surface.as_ref(), + } + } +} + +impl MaybePartial { + /// Access the surface form + pub fn surface_form(&self) -> Option> { + match self { + Self::Full(full) => Some((*full.surface_form()).into()), + Self::Partial(partial) => partial.surface_form.clone(), + } + } +} diff --git a/crates/fj-kernel/src/partial/mod.rs b/crates/fj-kernel/src/partial/mod.rs index 6b8fca4fb..4bce8b36b 100644 --- a/crates/fj-kernel/src/partial/mod.rs +++ b/crates/fj-kernel/src/partial/mod.rs @@ -27,11 +27,13 @@ mod curve; mod edge; +mod maybe_partial; mod vertex; pub use self::{ curve::{PartialCurve, PartialGlobalCurve}, edge::{PartialGlobalEdge, PartialHalfEdge}, + maybe_partial::MaybePartial, vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex}, }; @@ -43,34 +45,6 @@ use crate::{ stores::{Handle, Stores}, }; -/// Either a partial object or a full one -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub enum MaybePartial { - /// A full object - Full(T), - - /// A partial object - Partial(T::PartialForm), -} - -impl MaybePartial { - /// Return the full object, either directly or by building it - pub fn into_full(self, stores: &Stores) -> T { - match self { - Self::Partial(partial) => T::from_partial(partial, stores), - Self::Full(full) => full, - } - } - - /// Return the partial object, either directly or via conversion - pub fn into_partial(self) -> T::PartialForm { - match self { - Self::Partial(partial) => partial, - Self::Full(full) => full.into(), - } - } -} - /// Implemented for types that are partial objects /// /// # Implementation Note diff --git a/crates/fj-kernel/src/partial/vertex.rs b/crates/fj-kernel/src/partial/vertex.rs index 9fdc5f967..a9d8ce648 100644 --- a/crates/fj-kernel/src/partial/vertex.rs +++ b/crates/fj-kernel/src/partial/vertex.rs @@ -26,7 +26,7 @@ pub struct PartialVertex { /// /// Can be provided, if already available, or computed from the position on /// the [`Curve`]. - pub surface_form: Option, + pub surface_form: Option>, /// The global form of the [`Vertex`] /// @@ -49,8 +49,11 @@ impl PartialVertex { } /// Provide a surface form for the partial vertex - pub fn with_surface_form(mut self, surface_form: SurfaceVertex) -> Self { - self.surface_form = Some(surface_form); + pub fn with_surface_form( + mut self, + surface_form: impl Into>, + ) -> Self { + self.surface_form = Some(surface_form.into()); self } @@ -79,14 +82,19 @@ impl PartialVertex { .expect("Can't build `Vertex` without `Curve`") .into_full(stores); - let surface_form = self.surface_form.unwrap_or_else(|| { - PartialSurfaceVertex { - position: Some(curve.path().point_from_path_coords(position)), - surface: Some(*curve.surface()), - global_form: self.global_form, - } - .build(stores) - }); + let surface_form = self + .surface_form + .unwrap_or_else(|| { + PartialSurfaceVertex { + position: Some( + curve.path().point_from_path_coords(position), + ), + surface: Some(*curve.surface()), + global_form: self.global_form, + } + .into() + }) + .into_full(stores); let global_form = *surface_form.global_form(); @@ -99,7 +107,7 @@ impl From for PartialVertex { Self { position: Some(vertex.position()), curve: Some(vertex.curve().clone().into()), - surface_form: Some(*vertex.surface_form()), + surface_form: Some((*vertex.surface_form()).into()), global_form: Some((*vertex.global_form()).into()), } }