Merge pull request #1305 from hannobraun/partial

Start extracting new builder API from partial object API
This commit is contained in:
Hanno Braun 2022-11-03 16:14:43 +01:00 committed by GitHub
commit 412f1b26f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 374 additions and 239 deletions

View File

@ -197,6 +197,7 @@ mod tests {
use crate::{ use crate::{
algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint}, algorithms::approx::{path::RangeOnPath, Approx, ApproxPoint},
builder::CurveBuilder,
objects::{Curve, Objects, Surface}, objects::{Curve, Objects, Surface},
partial::HasPartial, partial::HasPartial,
path::GlobalPath, path::GlobalPath,
@ -213,7 +214,7 @@ mod tests {
.insert(Surface::new(GlobalPath::x_axis(), [0., 0., 1.]))?; .insert(Surface::new(GlobalPath::x_axis(), [0., 0., 1.]))?;
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface)) .with_surface(Some(surface))
.as_line_from_points([[1., 1.], [2., 1.]]) .update_as_line_from_points([[1., 1.], [2., 1.]])
.build(&objects)?; .build(&objects)?;
let range = RangeOnPath::from([[0.], [1.]]); let range = RangeOnPath::from([[0.], [1.]]);
@ -234,7 +235,7 @@ mod tests {
))?; ))?;
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface)) .with_surface(Some(surface))
.as_line_from_points([[1., 1.], [1., 2.]]) .update_as_line_from_points([[1., 1.], [1., 2.]])
.build(&objects)?; .build(&objects)?;
let range = RangeOnPath::from([[0.], [1.]]); let range = RangeOnPath::from([[0.], [1.]]);
@ -253,7 +254,7 @@ mod tests {
objects.surfaces.insert(Surface::new(path, [0., 0., 1.]))?; objects.surfaces.insert(Surface::new(path, [0., 0., 1.]))?;
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface.clone())) .with_surface(Some(surface.clone()))
.as_line_from_points([[0., 1.], [1., 1.]]) .update_as_line_from_points([[0., 1.], [1., 1.]])
.build(&objects)?; .build(&objects)?;
let range = RangeOnPath::from([[0.], [TAU]]); let range = RangeOnPath::from([[0.], [TAU]]);
@ -285,7 +286,7 @@ mod tests {
.insert(Surface::new(GlobalPath::x_axis(), [0., 0., 1.]))?; .insert(Surface::new(GlobalPath::x_axis(), [0., 0., 1.]))?;
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface)) .with_surface(Some(surface))
.as_circle_from_radius(1.) .update_as_circle_from_radius(1.)
.build(&objects)?; .build(&objects)?;
let range = RangeOnPath::from([[0.], [TAU]]); let range = RangeOnPath::from([[0.], [TAU]]);

View File

@ -75,6 +75,7 @@ mod tests {
use fj_math::Point; use fj_math::Point;
use crate::{ use crate::{
builder::CurveBuilder,
objects::{Curve, HalfEdge, Objects}, objects::{Curve, HalfEdge, Objects},
partial::HasPartial, partial::HasPartial,
}; };
@ -88,7 +89,7 @@ mod tests {
let surface = objects.surfaces.xy_plane(); let surface = objects.surfaces.xy_plane();
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface.clone())) .with_surface(Some(surface.clone()))
.as_u_axis() .update_as_u_axis()
.build(&objects)?; .build(&objects)?;
let half_edge = HalfEdge::partial() let half_edge = HalfEdge::partial()
.with_surface(Some(surface)) .with_surface(Some(surface))
@ -113,7 +114,7 @@ mod tests {
let surface = objects.surfaces.xy_plane(); let surface = objects.surfaces.xy_plane();
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface.clone())) .with_surface(Some(surface.clone()))
.as_u_axis() .update_as_u_axis()
.build(&objects)?; .build(&objects)?;
let half_edge = HalfEdge::partial() let half_edge = HalfEdge::partial()
.with_surface(Some(surface)) .with_surface(Some(surface))
@ -138,7 +139,7 @@ mod tests {
let surface = objects.surfaces.xy_plane(); let surface = objects.surfaces.xy_plane();
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface.clone())) .with_surface(Some(surface.clone()))
.as_u_axis() .update_as_u_axis()
.build(&objects)?; .build(&objects)?;
let half_edge = HalfEdge::partial() let half_edge = HalfEdge::partial()
.with_surface(Some(surface)) .with_surface(Some(surface))
@ -158,7 +159,7 @@ mod tests {
let surface = objects.surfaces.xy_plane(); let surface = objects.surfaces.xy_plane();
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface.clone())) .with_surface(Some(surface.clone()))
.as_u_axis() .update_as_u_axis()
.build(&objects)?; .build(&objects)?;
let half_edge = HalfEdge::partial() let half_edge = HalfEdge::partial()
.with_surface(Some(surface)) .with_surface(Some(surface))

View File

@ -150,6 +150,7 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
builder::CurveBuilder,
objects::{Curve, Face, Objects}, objects::{Curve, Face, Objects},
partial::HasPartial, partial::HasPartial,
}; };
@ -164,7 +165,7 @@ mod tests {
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface.clone())) .with_surface(Some(surface.clone()))
.as_line_from_points([[-3., 0.], [-2., 0.]]) .update_as_line_from_points([[-3., 0.], [-2., 0.]])
.build(&objects)?; .build(&objects)?;
#[rustfmt::skip] #[rustfmt::skip]

View File

@ -67,6 +67,7 @@ mod tests {
use crate::{ use crate::{
algorithms::intersect::CurveFaceIntersection, algorithms::intersect::CurveFaceIntersection,
builder::CurveBuilder,
objects::{Curve, Face, Objects}, objects::{Curve, Face, Objects},
partial::HasPartial, partial::HasPartial,
}; };
@ -124,7 +125,7 @@ mod tests {
let expected_curves = surfaces.try_map_ext(|surface| { let expected_curves = surfaces.try_map_ext(|surface| {
Curve::partial() Curve::partial()
.with_surface(Some(surface)) .with_surface(Some(surface))
.as_line_from_points([[0., 0.], [1., 0.]]) .update_as_line_from_points([[0., 0.], [1., 0.]])
.build(&objects) .build(&objects)
})?; })?;
let expected_intervals = let expected_intervals =

View File

@ -92,6 +92,7 @@ mod tests {
use crate::{ use crate::{
algorithms::transform::TransformObject, algorithms::transform::TransformObject,
builder::CurveBuilder,
objects::{Curve, Objects}, objects::{Curve, Objects},
partial::HasPartial, partial::HasPartial,
}; };
@ -122,11 +123,11 @@ mod tests {
let expected_xy = Curve::partial() let expected_xy = Curve::partial()
.with_surface(Some(xy.clone())) .with_surface(Some(xy.clone()))
.as_u_axis() .update_as_u_axis()
.build(&objects)?; .build(&objects)?;
let expected_xz = Curve::partial() let expected_xz = Curve::partial()
.with_surface(Some(xz.clone())) .with_surface(Some(xz.clone()))
.as_u_axis() .update_as_u_axis()
.build(&objects)?; .build(&objects)?;
assert_eq!( assert_eq!(

View File

@ -168,6 +168,7 @@ mod tests {
use crate::{ use crate::{
algorithms::sweep::Sweep, algorithms::sweep::Sweep,
builder::CurveBuilder,
objects::{Curve, HalfEdge, Objects, Vertex}, objects::{Curve, HalfEdge, Objects, Vertex},
partial::HasPartial, partial::HasPartial,
}; };
@ -179,7 +180,7 @@ mod tests {
let surface = objects.surfaces.xz_plane(); let surface = objects.surfaces.xz_plane();
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface.clone())) .with_surface(Some(surface.clone()))
.as_u_axis() .update_as_u_axis()
.build(&objects)?; .build(&objects)?;
let vertex = Vertex::partial() let vertex = Vertex::partial()
.with_position(Some([0.])) .with_position(Some([0.]))

View File

@ -1,29 +1,24 @@
use fj_math::Transform; use fj_math::Transform;
use crate::{ use crate::{
objects::{GlobalCurve, Objects}, objects::Objects,
partial::PartialCurve, partial::{PartialCurve, PartialGlobalCurve},
storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };
use super::TransformObject; use super::TransformObject;
impl TransformObject for Handle<GlobalCurve> { impl TransformObject for PartialGlobalCurve {
fn transform( fn transform(
self, self,
_: &Transform, _: &Transform,
objects: &Objects, _: &Objects,
) -> Result<Self, ValidationError> { ) -> Result<Self, ValidationError> {
// `GlobalCurve` doesn't contain any internal geometry. If it did, that // `GlobalCurve` doesn't contain any internal geometry. If it did, that
// would just be redundant with the geometry of other objects, and this // would just be redundant with the geometry of other objects, and this
// other geometry is already being transformed by other implementations // other geometry is already being transformed by other implementations
// of this trait. // of this trait.
// Ok(self)
// 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)?)
} }
} }
@ -34,20 +29,19 @@ impl TransformObject for PartialCurve {
objects: &Objects, objects: &Objects,
) -> Result<Self, ValidationError> { ) -> Result<Self, ValidationError> {
let surface = self let surface = self
.surface .surface()
.map(|surface| surface.transform(transform, objects)) .map(|surface| surface.transform(transform, objects))
.transpose()?; .transpose()?;
let global_form = self let global_form = self
.global_form .global_form()
.map(|global_form| global_form.0.transform(transform, objects)) .map(|global_form| global_form.transform(transform, objects))
.transpose()?; .transpose()?;
// Don't need to transform `self.path`, as that's defined in surface // Don't need to transform `self.path`, as that's defined in surface
// coordinates, and thus transforming `surface` takes care of it. // coordinates, and thus transforming `surface` takes care of it.
Ok(Self { Ok(Self::default()
surface, .with_surface(surface)
path: self.path, .with_path(self.path())
global_form: global_form.map(Into::into), .with_global_form(global_form))
})
} }
} }

View File

@ -57,10 +57,7 @@ impl TransformObject for PartialGlobalEdge {
transform: &Transform, transform: &Transform,
objects: &Objects, objects: &Objects,
) -> Result<Self, ValidationError> { ) -> Result<Self, ValidationError> {
let curve = self let curve = self.curve.transform(transform, objects)?;
.curve
.map(|curve| curve.0.transform(transform, objects))
.transpose()?;
let vertices = self let vertices = self
.vertices .vertices
.map(|vertices| { .map(|vertices| {
@ -70,9 +67,6 @@ impl TransformObject for PartialGlobalEdge {
}) })
.transpose()?; .transpose()?;
Ok(Self { Ok(Self { curve, vertices })
curve: curve.map(Into::into),
vertices,
})
} }
} }

View File

@ -14,20 +14,19 @@ impl TransformObject for PartialVertex {
transform: &Transform, transform: &Transform,
objects: &Objects, objects: &Objects,
) -> Result<Self, ValidationError> { ) -> Result<Self, ValidationError> {
let curve = self.curve.transform(transform, objects)?; let curve = self.curve().transform(transform, objects)?;
let surface_form = self let surface_form = self
.surface_form .surface_form()
.into_partial() .into_partial()
.transform(transform, objects)? .transform(transform, objects)?
.into(); .into();
// Don't need to transform `self.position`, as that is in curve // Don't need to transform `self.position`, as that is in curve
// coordinates and thus transforming the curve takes care of it. // coordinates and thus transforming the curve takes care of it.
Ok(Self { Ok(Self::default()
position: self.position, .with_position(self.position())
curve, .with_curve(Some(curve))
surface_form, .with_surface_form(surface_form))
})
} }
} }
@ -38,18 +37,17 @@ impl TransformObject for PartialSurfaceVertex {
objects: &Objects, objects: &Objects,
) -> Result<Self, ValidationError> { ) -> Result<Self, ValidationError> {
let surface = self let surface = self
.surface .surface()
.map(|surface| surface.transform(transform, objects)) .map(|surface| surface.transform(transform, objects))
.transpose()?; .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 // Don't need to transform `self.position`, as that is in surface
// coordinates and thus transforming the surface takes care of it. // coordinates and thus transforming the surface takes care of it.
Ok(Self { Ok(Self::default()
position: self.position, .with_position(self.position())
surface, .with_surface(surface)
global_form, .with_global_form(Some(global_form)))
})
} }
} }
@ -60,9 +58,9 @@ impl TransformObject for PartialGlobalVertex {
_: &Objects, _: &Objects,
) -> Result<Self, ValidationError> { ) -> Result<Self, ValidationError> {
let position = self let position = self
.position .position()
.map(|position| transform.transform_point(&position)); .map(|position| transform.transform_point(&position));
Ok(Self { position }) Ok(Self::default().with_position(position))
} }
} }

View File

@ -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<Scalar>) -> Self;
/// Update partial curve as a line, from the provided points
fn update_as_line_from_points(
self,
points: [impl Into<Point<2>>; 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<Scalar>) -> Self {
self.with_path(Some(SurfacePath::circle_from_radius(radius)))
}
fn update_as_line_from_points(
self,
points: [impl Into<Point<2>>; 2],
) -> Self {
self.with_path(Some(SurfacePath::line_from_points(points)))
}
}

View File

@ -1,11 +1,22 @@
//! API for building objects //! 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 face;
mod shell; mod shell;
mod sketch; mod sketch;
mod solid; mod solid;
// These are new-style builders that build on top of the partial object API.
mod curve;
mod vertex;
pub use self::{ pub use self::{
face::FaceBuilder, shell::ShellBuilder, sketch::SketchBuilder, curve::CurveBuilder,
face::FaceBuilder,
shell::ShellBuilder,
sketch::SketchBuilder,
solid::SolidBuilder, solid::SolidBuilder,
vertex::{GlobalVertexBuilder, SurfaceVertexBuilder, VertexBuilder},
}; };

View File

@ -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<MaybePartial<Curve>>,
position: impl Into<Point<1>>,
) -> 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<Point<2>>,
) -> Self;
}
impl GlobalVertexBuilder for PartialGlobalVertex {
fn update_from_curve_and_position(
self,
curve: impl Into<MaybePartial<Curve>>,
position: impl Into<Point<1>>,
) -> 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<Point<2>>,
) -> Self {
self.with_position(Some(surface.point_from_surface_coords(position)))
}
}

View File

@ -360,6 +360,7 @@ impl<T> Iterator for Iter<T> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
builder::CurveBuilder,
objects::{ objects::{
Curve, Cycle, Face, GlobalCurve, GlobalVertex, HalfEdge, Objects, Curve, Cycle, Face, GlobalCurve, GlobalVertex, HalfEdge, Objects,
Shell, Sketch, Solid, SurfaceVertex, Vertex, Shell, Sketch, Solid, SurfaceVertex, Vertex,
@ -376,7 +377,7 @@ mod tests {
let surface = objects.surfaces.xy_plane(); let surface = objects.surfaces.xy_plane();
let object = Curve::partial() let object = Curve::partial()
.with_surface(Some(surface)) .with_surface(Some(surface))
.as_u_axis() .update_as_u_axis()
.build(&objects); .build(&objects);
assert_eq!(1, object.curve_iter().count()); assert_eq!(1, object.curve_iter().count());
@ -593,7 +594,7 @@ mod tests {
let surface = objects.surfaces.xy_plane(); let surface = objects.surfaces.xy_plane();
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface.clone())) .with_surface(Some(surface.clone()))
.as_u_axis() .update_as_u_axis()
.build(&objects)?; .build(&objects)?;
let global_vertex = objects let global_vertex = objects
.global_vertices .global_vertices

View File

@ -5,6 +5,7 @@ use crate::{
Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects, Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
Surface, SurfaceVertex, Vertex, Surface, SurfaceVertex, Vertex,
}, },
path::SurfacePath,
storage::Handle, storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };
@ -30,6 +31,24 @@ pub enum MaybePartial<T: HasPartial> {
} }
impl<T: HasPartial> MaybePartial<T> { impl<T: HasPartial> MaybePartial<T> {
/// 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 /// If this is a partial object, update it
/// ///
/// This is useful whenever a partial object can infer something about its /// This is useful whenever a partial object can infer something about its
@ -95,25 +114,37 @@ where
// `MaybePartial<T>`, as that would conflict. // `MaybePartial<T>`, as that would conflict.
impl MaybePartial<Curve> { impl MaybePartial<Curve> {
/// Access the global form /// Access the path
pub fn global_form(&self) -> Option<Handle<GlobalCurve>> { pub fn path(&self) -> Option<SurfacePath> {
match self { match self {
Self::Full(full) => Some(full.global_form().clone()), MaybePartial::Full(full) => Some(full.path()),
Self::Partial(partial) => { MaybePartial::Partial(partial) => partial.path(),
partial.global_form.clone().map(Into::into) }
} }
/// Access the surface
pub fn surface(&self) -> Option<Handle<Surface>> {
match self {
MaybePartial::Full(full) => Some(full.surface().clone()),
MaybePartial::Partial(partial) => partial.surface(),
}
}
/// Access the global form
pub fn global_form(&self) -> Option<MaybePartial<GlobalCurve>> {
match self {
Self::Full(full) => Some(full.global_form().clone().into()),
Self::Partial(partial) => partial.global_form(),
} }
} }
} }
impl MaybePartial<GlobalEdge> { impl MaybePartial<GlobalEdge> {
/// Access the curve /// Access the curve
pub fn curve(&self) -> Option<&Handle<GlobalCurve>> { pub fn curve(&self) -> MaybePartial<GlobalCurve> {
match self { match self {
Self::Full(full) => Some(full.curve()), Self::Full(full) => full.curve().clone().into(),
Self::Partial(partial) => { Self::Partial(partial) => partial.curve.clone(),
partial.curve.as_ref().map(|wrapper| &wrapper.0)
}
} }
} }
@ -154,15 +185,15 @@ impl MaybePartial<SurfaceVertex> {
pub fn position(&self) -> Option<Point<2>> { pub fn position(&self) -> Option<Point<2>> {
match self { match self {
Self::Full(full) => Some(full.position()), Self::Full(full) => Some(full.position()),
Self::Partial(partial) => partial.position, Self::Partial(partial) => partial.position(),
} }
} }
/// Access the surface /// Access the surface
pub fn surface(&self) -> Option<&Handle<Surface>> { pub fn surface(&self) -> Option<Handle<Surface>> {
match self { match self {
Self::Full(full) => Some(full.surface()), Self::Full(full) => Some(full.surface().clone()),
Self::Partial(partial) => partial.surface.as_ref(), Self::Partial(partial) => partial.surface(),
} }
} }
} }
@ -172,7 +203,7 @@ impl MaybePartial<Vertex> {
pub fn surface_form(&self) -> MaybePartial<SurfaceVertex> { pub fn surface_form(&self) -> MaybePartial<SurfaceVertex> {
match self { match self {
Self::Full(full) => full.surface_form().clone().into(), Self::Full(full) => full.surface_form().clone().into(),
Self::Partial(partial) => partial.surface_form.clone(), Self::Partial(partial) => partial.surface_form(),
} }
} }
} }

View File

@ -41,7 +41,7 @@ mod traits;
pub use self::{ pub use self::{
maybe_partial::MaybePartial, maybe_partial::MaybePartial,
objects::{ objects::{
curve::PartialCurve, curve::{PartialCurve, PartialGlobalCurve},
cycle::PartialCycle, cycle::PartialCycle,
edge::{PartialGlobalEdge, PartialHalfEdge}, edge::{PartialGlobalEdge, PartialHalfEdge},
vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex}, vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex},

View File

@ -1,35 +1,37 @@
use fj_math::{Point, Scalar, Vector};
use crate::{ use crate::{
objects::{Curve, GlobalCurve, Objects, Surface}, objects::{Curve, GlobalCurve, Objects, Surface},
partial::MaybePartial,
path::SurfacePath, path::SurfacePath,
storage::{Handle, HandleWrapper}, storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };
/// A partial [`Curve`] /// A partial [`Curve`]
/// ///
/// See [`crate::partial`] for more information. /// See [`crate::partial`] for more information.
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Clone, Debug, Default)]
pub struct PartialCurve { pub struct PartialCurve {
/// The path that defines the [`Curve`] path: Option<SurfacePath>,
/// surface: Option<Handle<Surface>>,
/// Must be provided before calling [`PartialCurve::build`]. global_form: Option<MaybePartial<GlobalCurve>>,
pub path: Option<SurfacePath>,
/// The surface that the [`Curve`] is defined in
///
/// Must be provided before calling [`PartialCurve::build`].
pub surface: Option<Handle<Surface>>,
/// The global form of the [`Curve`]
///
/// Will be computed from `path` and `surface` in [`PartialCurve::build`],
/// if not provided.
pub global_form: Option<HandleWrapper<GlobalCurve>>,
} }
impl PartialCurve { impl PartialCurve {
/// Access the path that defines the [`Curve`]
pub fn path(&self) -> Option<SurfacePath> {
self.path
}
/// Access the surface that the [`Curve`] is defined in
pub fn surface(&self) -> Option<Handle<Surface>> {
self.surface.clone()
}
/// Access the global form of the [`Curve`]
pub fn global_form(&self) -> Option<MaybePartial<GlobalCurve>> {
self.global_form.clone()
}
/// Provide a path for the partial curve /// Provide a path for the partial curve
pub fn with_path(mut self, path: Option<SurfacePath>) -> Self { pub fn with_path(mut self, path: Option<SurfacePath>) -> Self {
if let Some(path) = path { if let Some(path) = path {
@ -49,7 +51,7 @@ impl PartialCurve {
/// Provide a global form for the partial curve /// Provide a global form for the partial curve
pub fn with_global_form( pub fn with_global_form(
mut self, mut self,
global_form: Option<impl Into<HandleWrapper<GlobalCurve>>>, global_form: Option<impl Into<MaybePartial<GlobalCurve>>>,
) -> Self { ) -> Self {
if let Some(global_form) = global_form { if let Some(global_form) = global_form {
self.global_form = Some(global_form.into()); self.global_form = Some(global_form.into());
@ -57,32 +59,6 @@ impl PartialCurve {
self 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<Scalar>) -> 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<Point<2>>; 2]) -> Self {
self.with_path(Some(SurfacePath::line_from_points(points)))
}
/// Build a full [`Curve`] from the partial curve /// Build a full [`Curve`] from the partial curve
pub fn build( pub fn build(
self, self,
@ -95,7 +71,8 @@ impl PartialCurve {
let global_form = match self.global_form { let global_form = match self.global_form {
Some(global_form) => global_form, Some(global_form) => global_form,
None => objects.global_curves.insert(GlobalCurve)?.into(), None => objects.global_curves.insert(GlobalCurve)?.into(),
}; }
.into_full(objects)?;
Ok(objects Ok(objects
.curves .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<Handle<GlobalCurve>, ValidationError> {
let global_curve = objects.global_curves.insert(GlobalCurve)?;
Ok(global_curve)
}
}
impl From<&GlobalCurve> for PartialGlobalCurve {
fn from(_: &GlobalCurve) -> Self {
Self
}
}

View File

@ -1,6 +1,7 @@
use fj_math::Point; use fj_math::Point;
use crate::{ use crate::{
builder::CurveBuilder,
objects::{ objects::{
Curve, Cycle, HalfEdge, Objects, Surface, SurfaceVertex, Vertex, Curve, Cycle, HalfEdge, Objects, Surface, SurfaceVertex, Vertex,
}, },
@ -12,7 +13,7 @@ use crate::{
/// A partial [`Cycle`] /// A partial [`Cycle`]
/// ///
/// See [`crate::partial`] for more information. /// See [`crate::partial`] for more information.
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Clone, Debug, Default)]
pub struct PartialCycle { pub struct PartialCycle {
/// The surface that the [`Cycle`] is defined in /// The surface that the [`Cycle`] is defined in
pub surface: Option<Handle<Surface>>, pub surface: Option<Handle<Surface>>,
@ -82,7 +83,7 @@ impl PartialCycle {
let curve = Curve::partial() let curve = Curve::partial()
.with_surface(Some(surface.clone())) .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] = let [from, to] =
[(0., from), (1., to)].map(|(position, surface_form)| { [(0., from), (1., to)].map(|(position, surface_form)| {

View File

@ -2,19 +2,20 @@ use fj_interop::ext::ArrayExt;
use fj_math::{Point, Scalar}; use fj_math::{Point, Scalar};
use crate::{ use crate::{
builder::{CurveBuilder, GlobalVertexBuilder},
objects::{ objects::{
Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects, Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
Surface, SurfaceVertex, Vertex, VerticesInNormalizedOrder, Surface, SurfaceVertex, Vertex, VerticesInNormalizedOrder,
}, },
partial::{HasPartial, MaybePartial}, partial::{HasPartial, MaybePartial},
storage::{Handle, HandleWrapper}, storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };
/// A partial [`HalfEdge`] /// A partial [`HalfEdge`]
/// ///
/// See [`crate::partial`] for more information. /// See [`crate::partial`] for more information.
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Clone, Debug, Default)]
pub struct PartialHalfEdge { pub struct PartialHalfEdge {
/// The surface that the [`HalfEdge`]'s [`Curve`] is defined in /// The surface that the [`HalfEdge`]'s [`Curve`] is defined in
pub surface: Option<Handle<Surface>>, pub surface: Option<Handle<Surface>>,
@ -35,10 +36,10 @@ impl PartialHalfEdge {
/// Extract the global curve from either the curve or global form /// Extract the global curve from either the curve or global form
/// ///
/// If a global curve is available through both, the curve is preferred. /// If a global curve is available through both, the curve is preferred.
pub fn extract_global_curve(&self) -> Option<Handle<GlobalCurve>> { pub fn extract_global_curve(&self) -> MaybePartial<GlobalCurve> {
self.curve self.curve
.global_form() .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 /// Access the vertices of the global form, if available
@ -127,11 +128,11 @@ impl PartialHalfEdge {
objects: &Objects, objects: &Objects,
) -> Result<Self, ValidationError> { ) -> Result<Self, ValidationError> {
let curve = Curve::partial() let curve = Curve::partial()
.with_global_form(self.extract_global_curve()) .with_global_form(Some(self.extract_global_curve()))
.with_surface(self.surface.clone()) .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] = let [a_curve, b_curve] =
[Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord]));
@ -141,7 +142,7 @@ impl PartialHalfEdge {
.map(|[global_form, _]| global_form) .map(|[global_form, _]| global_form)
.unwrap_or_else(|| { .unwrap_or_else(|| {
GlobalVertex::partial() GlobalVertex::partial()
.from_curve_and_position(curve.clone(), a_curve) .update_from_curve_and_position(curve.clone(), a_curve)
.into() .into()
}); });
@ -190,11 +191,10 @@ impl PartialHalfEdge {
let surface = self let surface = self
.surface .surface
.as_ref() .clone()
.or_else(|| from_surface.surface()) .or_else(|| from_surface.surface())
.or_else(|| to_surface.surface()) .or_else(|| to_surface.surface())
.expect("Can't infer line segment without a surface") .expect("Can't infer line segment without a surface");
.clone();
let points = [&from_surface, &to_surface].map(|vertex| { let points = [&from_surface, &to_surface].map(|vertex| {
vertex vertex
.position() .position()
@ -202,9 +202,9 @@ impl PartialHalfEdge {
}); });
let curve = Curve::partial() let curve = Curve::partial()
.with_global_form(self.extract_global_curve()) .with_global_form(Some(self.extract_global_curve()))
.with_surface(Some(surface)) .with_surface(Some(surface))
.as_line_from_points(points); .update_as_line_from_points(points);
let [back, front] = { let [back, front] = {
let vertices = [(from, 0.), (to, 1.)].map(|(vertex, position)| { let vertices = [(from, 0.), (to, 1.)].map(|(vertex, position)| {
@ -252,9 +252,11 @@ impl PartialHalfEdge {
vertices.zip_ext(global_forms).map(|(vertex, global_form)| { vertices.zip_ext(global_forms).map(|(vertex, global_form)| {
vertex.update_partial(|vertex| { vertex.update_partial(|vertex| {
vertex.clone().with_surface_form(Some( vertex.clone().with_surface_form(Some(
vertex.surface_form.update_partial(|surface_vertex| { vertex.surface_form().update_partial(
surface_vertex.with_global_form(global_form) |surface_vertex| {
}), surface_vertex.with_global_form(global_form)
},
),
)) ))
}) })
}) })
@ -312,12 +314,12 @@ impl From<&HalfEdge> for PartialHalfEdge {
/// A partial [`GlobalEdge`] /// A partial [`GlobalEdge`]
/// ///
/// See [`crate::partial`] for more information. /// See [`crate::partial`] for more information.
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Clone, Debug, Default)]
pub struct PartialGlobalEdge { pub struct PartialGlobalEdge {
/// The curve that the [`GlobalEdge`] is defined in /// The curve that the [`GlobalEdge`] is defined in
/// ///
/// Must be provided before [`PartialGlobalEdge::build`] is called. /// Must be provided before [`PartialGlobalEdge::build`] is called.
pub curve: Option<HandleWrapper<GlobalCurve>>, pub curve: MaybePartial<GlobalCurve>,
/// The vertices that bound the [`GlobalEdge`] in the curve /// The vertices that bound the [`GlobalEdge`] in the curve
/// ///
@ -327,9 +329,12 @@ pub struct PartialGlobalEdge {
impl PartialGlobalEdge { impl PartialGlobalEdge {
/// Update the partial global edge with the given curve /// Update the partial global edge with the given curve
pub fn with_curve(mut self, curve: Option<Handle<GlobalCurve>>) -> Self { pub fn with_curve(
mut self,
curve: Option<impl Into<MaybePartial<GlobalCurve>>>,
) -> Self {
if let Some(curve) = curve { if let Some(curve) = curve {
self.curve = Some(curve.into()); self.curve = curve.into();
} }
self self
} }
@ -362,9 +367,7 @@ impl PartialGlobalEdge {
self, self,
objects: &Objects, objects: &Objects,
) -> Result<Handle<GlobalEdge>, ValidationError> { ) -> Result<Handle<GlobalEdge>, ValidationError> {
let curve = self let curve = self.curve.into_full(objects)?;
.curve
.expect("Can't build `GlobalEdge` without `GlobalCurve`");
let vertices = self let vertices = self
.vertices .vertices
.expect("Can't build `GlobalEdge` without vertices") .expect("Can't build `GlobalEdge` without vertices")
@ -379,7 +382,7 @@ impl PartialGlobalEdge {
impl From<&GlobalEdge> for PartialGlobalEdge { impl From<&GlobalEdge> for PartialGlobalEdge {
fn from(global_edge: &GlobalEdge) -> Self { fn from(global_edge: &GlobalEdge) -> Self {
Self { Self {
curve: Some(global_edge.curve().clone().into()), curve: global_edge.curve().clone().into(),
vertices: Some( vertices: Some(
global_edge global_edge
.vertices() .vertices()

View File

@ -5,7 +5,7 @@ pub mod vertex;
use crate::{ use crate::{
objects::{ objects::{
Curve, Cycle, GlobalEdge, GlobalVertex, HalfEdge, Objects, Curve, Cycle, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
SurfaceVertex, Vertex, SurfaceVertex, Vertex,
}, },
storage::Handle, storage::Handle,
@ -13,8 +13,8 @@ use crate::{
use super::{ use super::{
HasPartial, MaybePartial, Partial, PartialCurve, PartialCycle, HasPartial, MaybePartial, Partial, PartialCurve, PartialCycle,
PartialGlobalEdge, PartialGlobalVertex, PartialHalfEdge, PartialGlobalCurve, PartialGlobalEdge, PartialGlobalVertex,
PartialSurfaceVertex, PartialVertex, PartialHalfEdge, PartialSurfaceVertex, PartialVertex,
}; };
macro_rules! impl_traits { macro_rules! impl_traits {
@ -49,6 +49,7 @@ macro_rules! impl_traits {
impl_traits!( impl_traits!(
Curve, PartialCurve; Curve, PartialCurve;
Cycle, PartialCycle; Cycle, PartialCycle;
GlobalCurve, PartialGlobalCurve;
GlobalEdge, PartialGlobalEdge; GlobalEdge, PartialGlobalEdge;
GlobalVertex, PartialGlobalVertex; GlobalVertex, PartialGlobalVertex;
HalfEdge, PartialHalfEdge; HalfEdge, PartialHalfEdge;

View File

@ -1,8 +1,9 @@
use fj_math::Point; use fj_math::Point;
use crate::{ use crate::{
builder::GlobalVertexBuilder,
objects::{Curve, GlobalVertex, Objects, Surface, SurfaceVertex, Vertex}, objects::{Curve, GlobalVertex, Objects, Surface, SurfaceVertex, Vertex},
partial::{HasPartial, MaybePartial}, partial::MaybePartial,
storage::Handle, storage::Handle,
validate::ValidationError, validate::ValidationError,
}; };
@ -10,26 +11,29 @@ use crate::{
/// A partial [`Vertex`] /// A partial [`Vertex`]
/// ///
/// See [`crate::partial`] for more information. /// See [`crate::partial`] for more information.
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Clone, Debug, Default)]
pub struct PartialVertex { pub struct PartialVertex {
/// The position of the [`Vertex`] on the [`Curve`] position: Option<Point<1>>,
/// curve: MaybePartial<Curve>,
/// Must be provided before [`PartialVertex::build`] is called. surface_form: MaybePartial<SurfaceVertex>,
pub position: Option<Point<1>>,
/// The curve that the [`Vertex`] is defined in
///
/// Must be provided before [`PartialVertex::build`] is called.
pub curve: MaybePartial<Curve>,
/// The surface form of the [`Vertex`]
///
/// Can be provided, if already available, or computed from the position on
/// the [`Curve`].
pub surface_form: MaybePartial<SurfaceVertex>,
} }
impl PartialVertex { impl PartialVertex {
/// Access the position of the [`Vertex`] on the curve
pub fn position(&self) -> Option<Point<1>> {
self.position
}
/// Access the curve that the [`Vertex`] is defined in
pub fn curve(&self) -> MaybePartial<Curve> {
self.curve.clone()
}
/// Access the surface form of the [`Vertex`]
pub fn surface_form(&self) -> MaybePartial<SurfaceVertex> {
self.surface_form.clone()
}
/// Provide a position for the partial vertex /// Provide a position for the partial vertex
pub fn with_position( pub fn with_position(
mut self, mut self,
@ -63,19 +67,13 @@ impl PartialVertex {
self 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 /// Build a full [`Vertex`] from the partial vertex
/// ///
/// # Panics /// # 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( pub fn build(
self, self,
objects: &Objects, objects: &Objects,
@ -121,24 +119,27 @@ impl From<&Vertex> for PartialVertex {
/// See [`crate::partial`] for more information. /// See [`crate::partial`] for more information.
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct PartialSurfaceVertex { pub struct PartialSurfaceVertex {
/// The position of the [`SurfaceVertex`] in the [`Surface`] position: Option<Point<2>>,
/// surface: Option<Handle<Surface>>,
/// Must be provided before [`PartialSurfaceVertex::build`] is called. global_form: MaybePartial<GlobalVertex>,
pub position: Option<Point<2>>,
/// The surface that the [`SurfaceVertex`] is defined in
///
/// Must be provided before [`PartialSurfaceVertex::build`] is called.
pub surface: Option<Handle<Surface>>,
/// The global form of the [`SurfaceVertex`]
///
/// Can be provided, if already available, or computed from the position on
/// the [`Surface`].
pub global_form: MaybePartial<GlobalVertex>,
} }
impl PartialSurfaceVertex { impl PartialSurfaceVertex {
/// Access the position of the [`SurfaceVertex`]
pub fn position(&self) -> Option<Point<2>> {
self.position
}
/// Access the surface that the [`SurfaceVertex`] is defined in
pub fn surface(&self) -> Option<Handle<Surface>> {
self.surface.clone()
}
/// Access the global form of the [`SurfaceVertex`]
pub fn global_form(&self) -> MaybePartial<GlobalVertex> {
self.global_form.clone()
}
/// Provide a position for the partial surface vertex /// Provide a position for the partial surface vertex
pub fn with_position( pub fn with_position(
mut self, mut self,
@ -169,19 +170,7 @@ impl PartialSurfaceVertex {
self 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 /// 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( pub fn build(
self, self,
objects: &Objects, objects: &Objects,
@ -196,7 +185,7 @@ impl PartialSurfaceVertex {
let global_form = self let global_form = self
.global_form .global_form
.update_partial(|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)?; .into_full(objects)?;
@ -223,13 +212,15 @@ impl From<&SurfaceVertex> for PartialSurfaceVertex {
/// See [`crate::partial`] for more information. /// See [`crate::partial`] for more information.
#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)] #[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct PartialGlobalVertex { pub struct PartialGlobalVertex {
/// The position of the [`GlobalVertex`] position: Option<Point<3>>,
///
/// Must be provided before [`PartialGlobalVertex::build`] is called.
pub position: Option<Point<3>>,
} }
impl PartialGlobalVertex { impl PartialGlobalVertex {
/// Access the position of the [`GlobalVertex`]
pub fn position(&self) -> Option<Point<3>> {
self.position
}
/// Provide a position for the partial global vertex /// Provide a position for the partial global vertex
pub fn with_position( pub fn with_position(
mut self, mut self,
@ -241,35 +232,6 @@ impl PartialGlobalVertex {
self self
} }
/// Update partial global vertex from the given curve and position on it
pub fn from_curve_and_position(
self,
curve: impl Into<MaybePartial<Curve>>,
position: impl Into<Point<1>>,
) -> 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<Point<2>>,
) -> Self {
self.position = Some(surface.point_from_surface_coords(position));
self
}
/// Build a full [`GlobalVertex`] from the partial global vertex /// Build a full [`GlobalVertex`] from the partial global vertex
pub fn build( pub fn build(
self, self,

View File

@ -196,6 +196,7 @@ mod tests {
use fj_interop::ext::ArrayExt; use fj_interop::ext::ArrayExt;
use crate::{ use crate::{
builder::VertexBuilder,
objects::{GlobalCurve, HalfEdge, Objects}, objects::{GlobalCurve, HalfEdge, Objects},
partial::HasPartial, partial::HasPartial,
validate::Validate2, validate::Validate2,

View File

@ -179,6 +179,7 @@ impl SurfaceVertexValidationError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
builder::{CurveBuilder, SurfaceVertexBuilder},
objects::{Curve, GlobalVertex, Objects, SurfaceVertex, Vertex}, objects::{Curve, GlobalVertex, Objects, SurfaceVertex, Vertex},
partial::HasPartial, partial::HasPartial,
validate::Validate2, validate::Validate2,
@ -193,7 +194,7 @@ mod tests {
.with_curve(Some( .with_curve(Some(
Curve::partial() Curve::partial()
.with_surface(Some(objects.surfaces.xy_plane())) .with_surface(Some(objects.surfaces.xy_plane()))
.as_u_axis(), .update_as_u_axis(),
)) ))
.build(&objects)?; .build(&objects)?;
let invalid = Vertex::new( let invalid = Vertex::new(
@ -221,7 +222,7 @@ mod tests {
.with_curve(Some( .with_curve(Some(
Curve::partial() Curve::partial()
.with_surface(Some(objects.surfaces.xy_plane())) .with_surface(Some(objects.surfaces.xy_plane()))
.as_u_axis(), .update_as_u_axis(),
)) ))
.build(&objects)?; .build(&objects)?;
let invalid = Vertex::new( let invalid = Vertex::new(