diff --git a/crates/fj-kernel/src/algorithms/approx/curve.rs b/crates/fj-kernel/src/algorithms/approx/curve.rs index b1b80628d..197cdff70 100644 --- a/crates/fj-kernel/src/algorithms/approx/curve.rs +++ b/crates/fj-kernel/src/algorithms/approx/curve.rs @@ -198,6 +198,7 @@ mod tests { use crate::{ algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint}, objects::{Curve, Surface}, + partial::HasPartial, path::GlobalPath, stores::Stores, }; diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs index a8c6e99f3..8d2ad127d 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_edge.rs @@ -76,6 +76,7 @@ mod tests { use crate::{ objects::{Curve, HalfEdge, Surface}, + partial::HasPartial, stores::Stores, }; diff --git a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs index b0b461a2f..1cb6391b1 100644 --- a/crates/fj-kernel/src/algorithms/intersect/curve_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/curve_face.rs @@ -157,6 +157,7 @@ where mod tests { use crate::{ objects::{Curve, Face, Surface}, + partial::HasPartial, stores::Stores, }; diff --git a/crates/fj-kernel/src/algorithms/intersect/face_face.rs b/crates/fj-kernel/src/algorithms/intersect/face_face.rs index a0708fee9..08e3a064c 100644 --- a/crates/fj-kernel/src/algorithms/intersect/face_face.rs +++ b/crates/fj-kernel/src/algorithms/intersect/face_face.rs @@ -65,6 +65,7 @@ mod tests { use crate::{ algorithms::intersect::CurveFaceIntersection, objects::{Curve, Face, Surface}, + partial::HasPartial, stores::Stores, }; diff --git a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs index 895168a8d..7de1f3f3a 100644 --- a/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs +++ b/crates/fj-kernel/src/algorithms/intersect/surface_surface.rs @@ -159,6 +159,7 @@ mod tests { use crate::{ algorithms::transform::TransformObject, objects::{Curve, Surface}, + partial::HasPartial, stores::Stores, }; diff --git a/crates/fj-kernel/src/algorithms/sweep/edge.rs b/crates/fj-kernel/src/algorithms/sweep/edge.rs index 420883466..f774d1add 100644 --- a/crates/fj-kernel/src/algorithms/sweep/edge.rs +++ b/crates/fj-kernel/src/algorithms/sweep/edge.rs @@ -184,6 +184,7 @@ mod tests { use crate::{ algorithms::{reverse::Reverse, sweep::Sweep}, objects::{Cycle, Face, HalfEdge, Surface}, + partial::HasPartial, stores::Stores, }; diff --git a/crates/fj-kernel/src/algorithms/sweep/face.rs b/crates/fj-kernel/src/algorithms/sweep/face.rs index 68a9300c5..951cd5067 100644 --- a/crates/fj-kernel/src/algorithms/sweep/face.rs +++ b/crates/fj-kernel/src/algorithms/sweep/face.rs @@ -75,6 +75,7 @@ mod tests { use crate::{ algorithms::{reverse::Reverse, transform::TransformObject}, objects::{Face, HalfEdge, Sketch, Surface}, + partial::HasPartial, stores::Stores, }; diff --git a/crates/fj-kernel/src/algorithms/sweep/vertex.rs b/crates/fj-kernel/src/algorithms/sweep/vertex.rs index 81b12cc8d..3fbdb8fff 100644 --- a/crates/fj-kernel/src/algorithms/sweep/vertex.rs +++ b/crates/fj-kernel/src/algorithms/sweep/vertex.rs @@ -5,8 +5,9 @@ use crate::{ Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface, SurfaceVertex, Vertex, }, + partial::HasPartial, path::SurfacePath, - stores::Stores, + stores::{Handle, Stores}, }; use super::Sweep; @@ -137,7 +138,7 @@ impl Sweep for GlobalVertex { let a = self; let b = GlobalVertex::from_position(self.position() + path.into()); - let curve = GlobalCurve::partial() + let curve = Handle::::partial() .as_line_from_points([a.position(), b.position()]) .build(stores); @@ -153,7 +154,8 @@ mod tests { Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface, Vertex, }, - stores::Stores, + partial::HasPartial, + stores::{Handle, Stores}, }; #[test] @@ -186,7 +188,7 @@ mod tests { .sweep([0., 0., 1.], &stores); let expected_edge = GlobalEdge::new( - GlobalCurve::partial().as_z_axis().build(&stores), + Handle::::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/algorithms/validate/mod.rs b/crates/fj-kernel/src/algorithms/validate/mod.rs index 6966f8683..f00aeba12 100644 --- a/crates/fj-kernel/src/algorithms/validate/mod.rs +++ b/crates/fj-kernel/src/algorithms/validate/mod.rs @@ -164,6 +164,7 @@ mod tests { Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface, SurfaceVertex, Vertex, }, + partial::HasPartial, path::{GlobalPath, SurfacePath}, stores::Stores, }; diff --git a/crates/fj-kernel/src/builder/cycle.rs b/crates/fj-kernel/src/builder/cycle.rs index d3e3a95c6..765db1b8e 100644 --- a/crates/fj-kernel/src/builder/cycle.rs +++ b/crates/fj-kernel/src/builder/cycle.rs @@ -2,6 +2,7 @@ use fj_math::Point; use crate::{ objects::{Curve, Cycle, HalfEdge, Surface, SurfaceVertex, Vertex}, + partial::HasPartial, stores::Stores, }; diff --git a/crates/fj-kernel/src/builder/shell.rs b/crates/fj-kernel/src/builder/shell.rs index a5aa0345c..389660843 100644 --- a/crates/fj-kernel/src/builder/shell.rs +++ b/crates/fj-kernel/src/builder/shell.rs @@ -5,6 +5,7 @@ use crate::{ objects::{ Curve, Cycle, Face, HalfEdge, Shell, Surface, SurfaceVertex, Vertex, }, + partial::HasPartial, stores::Stores, }; diff --git a/crates/fj-kernel/src/iter.rs b/crates/fj-kernel/src/iter.rs index 7d4d35518..93e456bac 100644 --- a/crates/fj-kernel/src/iter.rs +++ b/crates/fj-kernel/src/iter.rs @@ -364,7 +364,8 @@ mod tests { Curve, Cycle, Face, GlobalCurve, GlobalVertex, HalfEdge, Shell, Sketch, Solid, Surface, SurfaceVertex, Vertex, }, - stores::Stores, + partial::HasPartial, + stores::{Handle, Stores}, }; use super::ObjectIters as _; @@ -440,7 +441,8 @@ mod tests { fn global_curve() { let stores = Stores::new(); - let object = GlobalCurve::partial().as_x_axis().build(&stores); + let object = + Handle::::partial().as_x_axis().build(&stores); assert_eq!(0, object.curve_iter().count()); assert_eq!(0, object.cycle_iter().count()); diff --git a/crates/fj-kernel/src/objects/curve.rs b/crates/fj-kernel/src/objects/curve.rs index d65b1702c..8eb2c1fdf 100644 --- a/crates/fj-kernel/src/objects/curve.rs +++ b/crates/fj-kernel/src/objects/curve.rs @@ -1,5 +1,4 @@ use crate::{ - partial::{PartialCurve, PartialGlobalCurve}, path::{GlobalPath, SurfacePath}, stores::Handle, }; @@ -15,14 +14,6 @@ pub struct Curve { } impl Curve { - /// 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` pub fn new( surface: Surface, @@ -59,14 +50,6 @@ pub struct GlobalCurve { } impl GlobalCurve { - /// 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 pub fn from_path(path: GlobalPath) -> Self { Self { path } diff --git a/crates/fj-kernel/src/objects/edge.rs b/crates/fj-kernel/src/objects/edge.rs index e411585fc..4aebce070 100644 --- a/crates/fj-kernel/src/objects/edge.rs +++ b/crates/fj-kernel/src/objects/edge.rs @@ -1,9 +1,6 @@ use std::fmt; -use crate::{ - partial::{PartialGlobalEdge, PartialHalfEdge}, - stores::Handle, -}; +use crate::stores::Handle; use super::{Curve, GlobalCurve, GlobalVertex, Vertex}; @@ -16,14 +13,6 @@ pub struct HalfEdge { } impl HalfEdge { - /// Create a [`PartialHalfEdge`] - /// - /// This function exists just for convenience, and will just return a - /// default [`PartialHalfEdge`]. - pub fn partial() -> PartialHalfEdge { - PartialHalfEdge::default() - } - /// Create a new instance of `HalfEdge` /// /// # Panics @@ -124,14 +113,6 @@ pub struct GlobalEdge { } impl GlobalEdge { - /// Create a [`PartialGlobalEdge`] - /// - /// This function exists just for convenience, and will just return a - /// default [`PartialGlobalEdge`]. - pub fn partial() -> PartialGlobalEdge { - PartialGlobalEdge::default() - } - /// Create a new instance pub fn new( curve: Handle, diff --git a/crates/fj-kernel/src/objects/vertex.rs b/crates/fj-kernel/src/objects/vertex.rs index 19710976f..2ef712af5 100644 --- a/crates/fj-kernel/src/objects/vertex.rs +++ b/crates/fj-kernel/src/objects/vertex.rs @@ -1,10 +1,6 @@ use fj_math::Point; use pretty_assertions::assert_eq; -use crate::partial::{ - PartialGlobalVertex, PartialSurfaceVertex, PartialVertex, -}; - use super::{Curve, Surface}; /// A vertex @@ -21,14 +17,6 @@ pub struct Vertex { } impl Vertex { - /// Create a [`PartialVertex`] - /// - /// This function exists just for convenience, and will just return a - /// default [`PartialVertex`]. - pub fn partial() -> PartialVertex { - PartialVertex::default() - } - /// Construct an instance of `Vertex` /// /// Panics, if `curve` and `surface_form` are not defined on the same @@ -85,14 +73,6 @@ pub struct SurfaceVertex { } impl SurfaceVertex { - /// Create a [`PartialSurfaceVertex`] - /// - /// This function exists just for convenience, and will just return a - /// default [`PartialSurfaceVertex`]. - pub fn partial() -> PartialSurfaceVertex { - PartialSurfaceVertex::default() - } - /// Construct a new instance of `SurfaceVertex` pub fn new( position: impl Into>, @@ -147,14 +127,6 @@ pub struct GlobalVertex { } impl GlobalVertex { - /// Create a [`PartialGlobalVertex`] - /// - /// This function exists just for convenience, and will just return a - /// default [`PartialGlobalVertex`]. - pub fn partial() -> PartialGlobalVertex { - PartialGlobalVertex::default() - } - /// Construct a `GlobalVertex` from a position pub fn from_position(position: impl Into>) -> Self { let position = position.into(); diff --git a/crates/fj-kernel/src/partial/maybe_partial.rs b/crates/fj-kernel/src/partial/maybe_partial.rs index 4be91ebbc..d75c47a2c 100644 --- a/crates/fj-kernel/src/partial/maybe_partial.rs +++ b/crates/fj-kernel/src/partial/maybe_partial.rs @@ -5,23 +5,35 @@ use crate::{ stores::{Handle, Stores}, }; -use super::HasPartialForm; +use super::{HasPartial, Partial}; -/// Either a partial object or a full one +/// Can be used everywhere either a partial or full objects are accepted +/// +/// Some convenience methods are available for specific instances of +/// `MaybePartial` (like, `MaybePartial`, or `MaybePartial`). +/// +/// # Implementation Note +/// +/// The set of available convenience methods is far from complete. Please feel +/// free to just add more, if you need them. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -pub enum MaybePartial { +pub enum MaybePartial { /// A full object Full(T), /// A partial object - Partial(T::PartialForm), + Partial(T::Partial), } -impl MaybePartial { +impl MaybePartial { /// If this is a partial object, update it + /// + /// This is useful whenever a partial object can infer something about its + /// parts from other parts, and wants to update what was inferred, in case + /// it *can* be updated. pub fn update_partial( self, - f: impl FnOnce(T::PartialForm) -> T::PartialForm, + f: impl FnOnce(T::Partial) -> T::Partial, ) -> Self { match self { Self::Partial(partial) => Self::Partial(f(partial)), @@ -29,23 +41,42 @@ impl MaybePartial { } } - /// Return the full object, either directly or by building it + /// Return or build a full object + /// + /// If this already is a full object, it is returned. If this is a partial + /// object, the full object is built from it, using [`Partial::build`]. pub fn into_full(self, stores: &Stores) -> T { match self { - Self::Partial(partial) => T::from_partial(partial, stores), + Self::Partial(partial) => partial.build(stores), Self::Full(full) => full, } } - /// Return the partial object, either directly or via conversion - pub fn into_partial(self) -> T::PartialForm { + /// Return or convert a partial object + /// + /// If this already is a partial object, is is returned. If this is a full + /// object, it is converted into a partial object using + /// [`HasPartial::to_partial`]. + pub fn into_partial(self) -> T::Partial { match self { Self::Partial(partial) => partial, - Self::Full(full) => full.into(), + Self::Full(full) => full.to_partial(), } } } +impl From for MaybePartial +where + T: HasPartial, +{ + fn from(full: T) -> Self { + Self::Full(full) + } +} + +// Unfortunately, we can't add a blanket implementation from `T::Partial` for +// `MaybePartial`, as that would conflict. + impl MaybePartial { /// Access the global form pub fn global_form(&self) -> Option>> { diff --git a/crates/fj-kernel/src/partial/mod.rs b/crates/fj-kernel/src/partial/mod.rs index 4bce8b36b..620341be4 100644 --- a/crates/fj-kernel/src/partial/mod.rs +++ b/crates/fj-kernel/src/partial/mod.rs @@ -1,102 +1,49 @@ -//! Partial objects +//! API for dealing with partially defined objects //! -//! This module contains type that represent partial objects. This is useful -//! when building objects, as it's often possible to provide just some the data -//! they own or objects they reference, while computing the rest. +//! This module contains types that represent objects that only have some of +//! their data and referenced objects defined. This is useful in the following +//! situations: //! -//! More generally speaking, there are situations where different parts of a new -//! objects are available at different times, and provided from different -//! places. Partial objects can be used to represent such partially constructed -//! objects whenever that is required. +//! - Sometimes parts of an object can be inferred. For example, when building a +//! half-edge that is a line segment, it is enough to provide only two partial +//! vertices with only their surface coordinates defined. The rest can be +//! inferred. +//! - Sometimes you need to build an object, but parts of it already exist. For +//! example, a new half-edge might share a vertex with an existing half-edge. +//! In such a case you can use the partial object to provide the existing +//! vertex, then provide or infer other parts as appropriate. +//! - When transforming an object, parts of it might already be transformed. For +//! example, when transforming a half-edge, each of its vertices references +//! the same curve as the half-edge does. The partial object API can be used +//! to avoid transforming the same object multiple times. //! -//! The API for partial objects follows a specific style: +//! This module contains two groups of types: //! -//! - Partial objects are structs with fields that mirror the fields of the full -//! object structs, but all fields are optional. -//! - Partial object structs implement [`Default`], but a `partial` method is -//! also available on the respective full object struct, as a perhaps more -//! convenient and readable way to construct a partial object. -//! - Partial object structs have `with_*` methods to provide values for each of -//! their fields. -//! - Partial object structs may have other methods with prefixes like `as_*`, -//! `from_*`, or similar, if one or more of their fields can be initialized by -//! providing alternative data. -//! - Partial object structs have a `build` method to build a full object. -//! - All `with_*`, `as_*`, and `build` methods can be chained, to provide a -//! convenient API. +//! - Structs that represent partial objects. For example [`PartialHalfEdge`] is +//! the partial variant of [`HalfEdge`]. +//! - Infrastructure for abstracting over partial objects. See [`Partial`], +//! [`HasPartial`], and [`MaybePartial`]. +//! +//! [`HalfEdge`]: crate::objects::HalfEdge +//! +//! # Implementation Note +//! +//! This API grew out of the [builder API][crate::builder] and is still +//! incomplete. Eventually, it should replace the builder API completely +//! ([#1147]). +//! +//! [#1147]: https://github.com/hannobraun/Fornjot/issues/1147 -mod curve; -mod edge; mod maybe_partial; -mod vertex; +mod objects; +mod traits; pub use self::{ - curve::{PartialCurve, PartialGlobalCurve}, - edge::{PartialGlobalEdge, PartialHalfEdge}, maybe_partial::MaybePartial, - vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex}, -}; - -use crate::{ objects::{ - Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, SurfaceVertex, - Vertex, + curve::{PartialCurve, PartialGlobalCurve}, + edge::{PartialGlobalEdge, PartialHalfEdge}, + vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex}, }, - stores::{Handle, Stores}, + traits::{HasPartial, Partial}, }; - -/// Implemented for types that are partial objects -/// -/// # Implementation Note -/// -/// It would be nicer to require a conversion from `&Self` into the partial -/// form, but I think we need a `where` clause on the associated type to specify -/// that, which is unstable. It should become stable soon though, together with -/// generic associated types: -/// -pub trait HasPartialForm: Into { - /// The full version of this partial object - type PartialForm; - - /// Build a full object from the partial object - fn from_partial(partial: Self::PartialForm, stores: &Stores) -> Self; -} - -macro_rules! impl_traits { - ($($full:ty, $partial:ty;)*) => { - $( - impl HasPartialForm for $full { - type PartialForm = $partial; - - fn from_partial(partial: Self::PartialForm, stores: &Stores) - -> Self - { - partial.build(stores) - } - } - - impl From<$full> for MaybePartial<$full> { - fn from(full: $full) -> Self { - Self::Full(full) - } - } - - impl From<$partial> for MaybePartial<$full> { - fn from(partial: $partial) -> Self { - Self::Partial(partial) - } - } - )* - }; -} - -impl_traits!( - Curve, PartialCurve; - GlobalEdge, PartialGlobalEdge; - GlobalVertex, PartialGlobalVertex; - HalfEdge, PartialHalfEdge; - SurfaceVertex, PartialSurfaceVertex; - Vertex, PartialVertex; - - Handle, PartialGlobalCurve; -); diff --git a/crates/fj-kernel/src/partial/curve.rs b/crates/fj-kernel/src/partial/objects/curve.rs similarity index 94% rename from crates/fj-kernel/src/partial/curve.rs rename to crates/fj-kernel/src/partial/objects/curve.rs index 40350fed3..767407344 100644 --- a/crates/fj-kernel/src/partial/curve.rs +++ b/crates/fj-kernel/src/partial/objects/curve.rs @@ -2,12 +2,11 @@ use fj_math::{Point, Scalar, Vector}; use crate::{ objects::{Curve, GlobalCurve, Surface}, + partial::{HasPartial, MaybePartial}, path::{GlobalPath, SurfacePath}, stores::{Handle, Stores}, }; -use super::MaybePartial; - /// A partial [`Curve`] /// /// See [`crate::partial`] for more information. @@ -98,7 +97,7 @@ impl PartialCurve { ), }; - GlobalCurve::partial().with_path(path).into() + Handle::::partial().with_path(path).into() }) .into_full(stores); @@ -106,8 +105,8 @@ impl PartialCurve { } } -impl From for PartialCurve { - fn from(curve: Curve) -> Self { +impl From<&Curve> for PartialCurve { + fn from(curve: &Curve) -> Self { Self { path: Some(curve.path()), surface: Some(*curve.surface()), @@ -170,8 +169,8 @@ impl PartialGlobalCurve { } } -impl From> for PartialGlobalCurve { - fn from(global_curve: Handle) -> Self { +impl From<&Handle> for PartialGlobalCurve { + fn from(global_curve: &Handle) -> Self { Self { path: Some(global_curve.path()), } diff --git a/crates/fj-kernel/src/partial/edge.rs b/crates/fj-kernel/src/partial/objects/edge.rs similarity index 97% rename from crates/fj-kernel/src/partial/edge.rs rename to crates/fj-kernel/src/partial/objects/edge.rs index f25dee173..68aec4aa9 100644 --- a/crates/fj-kernel/src/partial/edge.rs +++ b/crates/fj-kernel/src/partial/objects/edge.rs @@ -5,11 +5,10 @@ use crate::{ Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Surface, SurfaceVertex, Vertex, }, + partial::{HasPartial, MaybePartial, PartialCurve}, stores::{Handle, Stores}, }; -use super::{MaybePartial, PartialCurve}; - /// A partial [`HalfEdge`] /// /// See [`crate::partial`] for more information. @@ -188,8 +187,8 @@ impl PartialHalfEdge { } } -impl From for PartialHalfEdge { - fn from(half_edge: HalfEdge) -> Self { +impl From<&HalfEdge> for PartialHalfEdge { + fn from(half_edge: &HalfEdge) -> Self { Self { curve: Some(half_edge.curve().clone().into()), vertices: Some(half_edge.vertices().clone().map(Into::into)), @@ -241,8 +240,8 @@ impl PartialGlobalEdge { } } -impl From for PartialGlobalEdge { - fn from(global_edge: GlobalEdge) -> Self { +impl From<&GlobalEdge> for PartialGlobalEdge { + fn from(global_edge: &GlobalEdge) -> Self { Self { curve: Some(global_edge.curve().clone()), 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 new file mode 100644 index 000000000..94b9c0335 --- /dev/null +++ b/crates/fj-kernel/src/partial/objects/mod.rs @@ -0,0 +1,52 @@ +pub mod curve; +pub mod edge; +pub mod vertex; + +use crate::{ + objects::{ + Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, SurfaceVertex, + Vertex, + }, + stores::{Handle, Stores}, +}; + +use super::{ + HasPartial, MaybePartial, Partial, PartialCurve, PartialGlobalCurve, + PartialGlobalEdge, PartialGlobalVertex, PartialHalfEdge, + PartialSurfaceVertex, PartialVertex, +}; + +macro_rules! impl_traits { + ($($full:ty, $partial:ty;)*) => { + $( + impl HasPartial for $full { + type Partial = $partial; + } + + impl Partial for $partial { + type Full = $full; + + fn build(self, stores: &Stores) -> Self::Full { + self.build(stores) + } + } + + impl From<$partial> for MaybePartial<$full> { + fn from(partial: $partial) -> Self { + Self::Partial(partial) + } + } + )* + }; +} + +impl_traits!( + Curve, PartialCurve; + GlobalEdge, PartialGlobalEdge; + GlobalVertex, PartialGlobalVertex; + HalfEdge, PartialHalfEdge; + SurfaceVertex, PartialSurfaceVertex; + Vertex, PartialVertex; + + Handle, PartialGlobalCurve; +); diff --git a/crates/fj-kernel/src/partial/vertex.rs b/crates/fj-kernel/src/partial/objects/vertex.rs similarity index 95% rename from crates/fj-kernel/src/partial/vertex.rs rename to crates/fj-kernel/src/partial/objects/vertex.rs index a9d8ce648..b111e21cb 100644 --- a/crates/fj-kernel/src/partial/vertex.rs +++ b/crates/fj-kernel/src/partial/objects/vertex.rs @@ -2,11 +2,10 @@ use fj_math::Point; use crate::{ objects::{Curve, GlobalVertex, Surface, SurfaceVertex, Vertex}, + partial::{HasPartial, MaybePartial}, stores::Stores, }; -use super::MaybePartial; - /// A partial [`Vertex`] /// /// See [`crate::partial`] for more information. @@ -102,8 +101,8 @@ impl PartialVertex { } } -impl From for PartialVertex { - fn from(vertex: Vertex) -> Self { +impl From<&Vertex> for PartialVertex { + fn from(vertex: &Vertex) -> Self { Self { position: Some(vertex.position()), curve: Some(vertex.curve().clone().into()), @@ -185,8 +184,8 @@ impl PartialSurfaceVertex { } } -impl From for PartialSurfaceVertex { - fn from(surface_vertex: SurfaceVertex) -> Self { +impl From<&SurfaceVertex> for PartialSurfaceVertex { + fn from(surface_vertex: &SurfaceVertex) -> Self { Self { position: Some(surface_vertex.position()), surface: Some(*surface_vertex.surface()), @@ -252,8 +251,8 @@ impl PartialGlobalVertex { } } -impl From for PartialGlobalVertex { - fn from(global_vertex: GlobalVertex) -> Self { +impl From<&GlobalVertex> for PartialGlobalVertex { + fn from(global_vertex: &GlobalVertex) -> Self { Self { position: Some(global_vertex.position()), } diff --git a/crates/fj-kernel/src/partial/traits.rs b/crates/fj-kernel/src/partial/traits.rs new file mode 100644 index 000000000..9a8925205 --- /dev/null +++ b/crates/fj-kernel/src/partial/traits.rs @@ -0,0 +1,77 @@ +use crate::stores::Stores; + +/// Implemented for objects that a partial object type exists for +/// +/// # Implementation Note +/// +/// This trait is usually implemented for object types themselves, but types +/// that are already managed in the centralized object storage ([#1021]) +/// implement this trait for `Handle` instead. This is necessary, due to the +/// use of this type in [`MaybePartial`], but leads to some not so nice +/// inconsistencies. +/// +/// Once [#1021] is addressed and all types are managed in the centralized +/// object storage, this should be changed to all object types implement this +/// directly. +/// +/// [#1021]: https://github.com/hannobraun/Fornjot/issues/1021 +/// [`MaybePartial`]: super::MaybePartial +pub trait HasPartial { + /// The type representing the partial variant of this object + type Partial: Partial; + + /// Create an empty partial variant of this object + /// + /// This function exists just for convenience, and will just return a + /// [`Default`] version of the partial object. + fn partial() -> Self::Partial { + Self::Partial::default() + } + + /// Convert this object into its partial variant + /// + /// All fields of the partial variant are set from this object. This is + /// useful when creating a new object that needs to share parts of an + /// existing one. + fn to_partial(&self) -> Self::Partial { + self.into() + } +} + +/// Implemented for partial objects +/// +/// The API for partial objects follows a specific style: +/// +/// - Partial objects are structs with fields that mirror the fields of the full +/// object structs, but all fields are optional. +/// - Partial object structs have `with_*` methods to provide values for each of +/// their fields. +/// - Partial object structs may have other methods with prefixes like `as_*`, +/// `from_*`, or similar, if one or more of their fields can be initialized by +/// providing alternative data. +/// - Partial object structs have a `build` method to build a full object. +/// - All `with_*`, `as_*`, and `build` methods can be chained, to provide a +/// convenient API. +/// +/// # Implementation Note +/// +/// It would be nicer to require an [`Into`] bound instead of [`From`] (see +/// documentation of those types for more information). But I think we'd need a +/// `where` clause on the associated type to specify that, which is unstable. It +/// should become stable soon though, together with generic associated types: +/// +pub trait Partial: Default + for<'a> From<&'a Self::Full> { + /// The type representing the full variant of this object + type Full; + + /// Build a full object from this partial one + /// + /// Implementations of this method will typically try to infer any missing + /// parts of the partial object, but this is not possible in all cases. In + /// such cases, implementations of this method may panic. + /// + /// Calling `build` on a partial object that can't infer its missing parts + /// is considered a programmer error, hence why this method doesn't return a + /// [`Result`]. + fn build(self, stores: &Stores) -> Self::Full; +} diff --git a/crates/fj-operations/src/sketch.rs b/crates/fj-operations/src/sketch.rs index 1c174367a..831cb51b7 100644 --- a/crates/fj-operations/src/sketch.rs +++ b/crates/fj-operations/src/sketch.rs @@ -4,6 +4,7 @@ use fj_kernel::{ Validate, Validated, ValidationConfig, ValidationError, }, objects::{Cycle, Face, HalfEdge, Sketch, Surface}, + partial::HasPartial, stores::Stores, }; use fj_math::{Aabb, Point};