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::{
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]]);

View File

@ -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))

View File

@ -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]

View File

@ -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 =

View File

@ -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!(

View File

@ -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.]))

View File

@ -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<GlobalCurve> {
impl TransformObject for PartialGlobalCurve {
fn transform(
self,
_: &Transform,
objects: &Objects,
_: &Objects,
) -> Result<Self, ValidationError> {
// `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<Self, ValidationError> {
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))
}
}

View File

@ -57,10 +57,7 @@ impl TransformObject for PartialGlobalEdge {
transform: &Transform,
objects: &Objects,
) -> Result<Self, ValidationError> {
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 })
}
}

View File

@ -14,20 +14,19 @@ impl TransformObject for PartialVertex {
transform: &Transform,
objects: &Objects,
) -> Result<Self, ValidationError> {
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<Self, ValidationError> {
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<Self, ValidationError> {
let position = self
.position
.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
// 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},
};

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)]
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

View File

@ -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<T: HasPartial> {
}
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
///
/// This is useful whenever a partial object can infer something about its
@ -95,25 +114,37 @@ where
// `MaybePartial<T>`, as that would conflict.
impl MaybePartial<Curve> {
/// Access the global form
pub fn global_form(&self) -> Option<Handle<GlobalCurve>> {
/// Access the path
pub fn path(&self) -> Option<SurfacePath> {
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<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> {
/// Access the curve
pub fn curve(&self) -> Option<&Handle<GlobalCurve>> {
pub fn curve(&self) -> MaybePartial<GlobalCurve> {
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<SurfaceVertex> {
pub fn position(&self) -> Option<Point<2>> {
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<Surface>> {
pub fn surface(&self) -> Option<Handle<Surface>> {
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<Vertex> {
pub fn surface_form(&self) -> MaybePartial<SurfaceVertex> {
match self {
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::{
maybe_partial::MaybePartial,
objects::{
curve::PartialCurve,
curve::{PartialCurve, PartialGlobalCurve},
cycle::PartialCycle,
edge::{PartialGlobalEdge, PartialHalfEdge},
vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex},

View File

@ -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<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>>,
path: Option<SurfacePath>,
surface: Option<Handle<Surface>>,
global_form: Option<MaybePartial<GlobalCurve>>,
}
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
pub fn with_path(mut self, path: Option<SurfacePath>) -> 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<impl Into<HandleWrapper<GlobalCurve>>>,
global_form: Option<impl Into<MaybePartial<GlobalCurve>>>,
) -> 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<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
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<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 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<Handle<Surface>>,
@ -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)| {

View File

@ -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<Handle<Surface>>,
@ -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<Handle<GlobalCurve>> {
pub fn extract_global_curve(&self) -> MaybePartial<GlobalCurve> {
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<Self, ValidationError> {
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<HandleWrapper<GlobalCurve>>,
pub curve: MaybePartial<GlobalCurve>,
/// 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<Handle<GlobalCurve>>) -> Self {
pub fn with_curve(
mut self,
curve: Option<impl Into<MaybePartial<GlobalCurve>>>,
) -> 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<Handle<GlobalEdge>, 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()

View File

@ -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;

View File

@ -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<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>,
position: Option<Point<1>>,
curve: MaybePartial<Curve>,
surface_form: MaybePartial<SurfaceVertex>,
}
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
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<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>,
position: Option<Point<2>>,
surface: Option<Handle<Surface>>,
global_form: MaybePartial<GlobalVertex>,
}
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
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<Point<3>>,
position: Option<Point<3>>,
}
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
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<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
pub fn build(
self,

View File

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

View File

@ -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(