Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make partial object API more flexible #1140

Merged
merged 11 commits into from
Sep 26, 2022
40 changes: 23 additions & 17 deletions crates/fj-kernel/src/partial/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use crate::{
stores::{Handle, Stores},
};

use super::MaybePartial;

/// A partial [`Curve`]
///
/// See [`crate::partial`] for more information.
Expand All @@ -25,7 +27,7 @@ pub struct PartialCurve {
///
/// Will be computed from `path` and `surface` in [`PartialCurve::build`],
/// if not provided.
pub global_form: Option<Handle<GlobalCurve>>,
pub global_form: Option<MaybePartial<Handle<GlobalCurve>>>,
}

impl PartialCurve {
Expand All @@ -44,9 +46,9 @@ impl PartialCurve {
/// Provide a global form for the partial curve
pub fn with_global_form(
mut self,
global_form: Handle<GlobalCurve>,
global_form: impl Into<MaybePartial<Handle<GlobalCurve>>>,
) -> Self {
self.global_form = Some(global_form);
self.global_form = Some(global_form.into());
self
}

Expand Down Expand Up @@ -82,19 +84,23 @@ impl PartialCurve {
let surface =
self.surface.expect("Can't build `Curve` without surface");

let global_form = self.global_form.unwrap_or_else(|| {
let path = match path {
SurfacePath::Circle(circle) => {
GlobalPath::circle_from_radius(circle.radius())
}
SurfacePath::Line(line) => GlobalPath::line_from_points(
[line.origin(), line.origin() + line.direction()]
.map(|point| surface.point_from_surface_coords(point)),
),
};

GlobalCurve::partial().with_path(path).build(stores)
});
let global_form = self
.global_form
.unwrap_or_else(|| {
let path = match path {
SurfacePath::Circle(circle) => {
GlobalPath::circle_from_radius(circle.radius())
}
SurfacePath::Line(line) => GlobalPath::line_from_points(
[line.origin(), line.origin() + line.direction()].map(
|point| surface.point_from_surface_coords(point),
),
),
};

GlobalCurve::partial().with_path(path).into()
})
.into_full(stores);

Curve::new(surface, path, global_form)
}
Expand All @@ -105,7 +111,7 @@ impl From<Curve> for PartialCurve {
Self {
path: Some(curve.path()),
surface: Some(*curve.surface()),
global_form: Some(curve.global_form().clone()),
global_form: Some(curve.global_form().clone().into()),
}
}
}
Expand Down
114 changes: 73 additions & 41 deletions crates/fj-kernel/src/partial/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
stores::{Handle, Stores},
};

use super::MaybePartial;
use super::{MaybePartial, PartialCurve};

/// A partial [`HalfEdge`]
///
Expand All @@ -18,12 +18,12 @@ pub struct PartialHalfEdge {
pub curve: Option<MaybePartial<Curve>>,

/// The vertices that bound this [`HalfEdge`] in the [`Curve`]
pub vertices: [Option<MaybePartial<Vertex>>; 2],
pub vertices: Option<[MaybePartial<Vertex>; 2]>,

/// The global form of the [`HalfEdge`]
///
/// Can be computed by [`PartialHalfEdge::build`], if not available.
pub global_form: Option<GlobalEdge>,
pub global_form: Option<MaybePartial<GlobalEdge>>,
}

impl PartialHalfEdge {
Expand All @@ -38,31 +38,16 @@ impl PartialHalfEdge {
mut self,
vertices: [impl Into<MaybePartial<Vertex>>; 2],
) -> Self {
self.vertices = vertices.map(Into::into).map(Some);
self.vertices = Some(vertices.map(Into::into));
self
}

/// Update the partial half-edge, starting it from the given vertex
pub fn with_from_vertex(
mut self,
vertex: impl Into<MaybePartial<Vertex>>,
) -> Self {
self.vertices[0] = Some(vertex.into());
self
}

/// Update the partial half-edge with the given end vertex
pub fn with_to_vertex(
/// Update the partial half-edge with the given global form
pub fn with_global_form(
mut self,
vertex: impl Into<MaybePartial<Vertex>>,
global_form: impl Into<MaybePartial<GlobalEdge>>,
) -> Self {
self.vertices[1] = Some(vertex.into());
self
}

/// Update the partial half-edge with the given global form
pub fn with_global_form(mut self, global_form: GlobalEdge) -> Self {
self.global_form = Some(global_form);
self.global_form = Some(global_form.into());
self
}

Expand Down Expand Up @@ -92,7 +77,7 @@ impl PartialHalfEdge {
};

self.curve = Some(curve.into());
self.vertices = vertices.map(Into::into).map(Some);
self.vertices = Some(vertices.map(Into::into));

self
}
Expand All @@ -103,9 +88,12 @@ impl PartialHalfEdge {
surface: Surface,
points: [impl Into<Point<2>>; 2],
) -> Self {
let curve = Curve::partial()
.with_surface(surface)
.as_line_from_points(points);
let curve = PartialCurve {
global_form: self.extract_global_curve(),
..PartialCurve::default()
}
.with_surface(surface)
.as_line_from_points(points);

let vertices = [0., 1.].map(|position| {
Vertex::partial()
Expand All @@ -114,7 +102,42 @@ impl PartialHalfEdge {
});

self.curve = Some(curve.into());
self.vertices = vertices.map(Into::into).map(Some);
self.vertices = Some(vertices.map(Into::into));

self
}

/// Update partial half-edge as a line segment, reusing existing vertices
pub fn as_line_segment(mut self) -> Self {
let [from, to] = self
.vertices
.clone()
.expect("Can't infer line segment without vertices")
.map(|vertex| {
vertex.surface_form().expect(
"Can't infer line segment without two surface vertices",
)
});

let surface = from
.surface()
.copied()
.or_else(|| to.surface().copied())
.expect("Can't infer line segment without a surface");
let points = [from, to].map(|vertex| {
vertex
.position()
.expect("Can't infer line segment without surface position")
});

let curve = PartialCurve {
global_form: self.extract_global_curve(),
..PartialCurve::default()
}
.with_surface(surface)
.as_line_from_points(points);

self.curve = Some(curve.into());

self
}
Expand All @@ -125,28 +148,37 @@ impl PartialHalfEdge {
.curve
.expect("Can't build `HalfEdge` without curve")
.into_full(stores);
let vertices = self.vertices.map(|vertex| {
vertex
.expect("Can't build `HalfEdge` without vertices")
.into_full(stores)
});

let global_form = self.global_form.unwrap_or_else(|| {
GlobalEdge::partial()
.from_curve_and_vertices(&curve, &vertices)
.build(stores)
});
let vertices = self
.vertices
.expect("Can't build `HalfEdge` without vertices")
.map(|vertex| vertex.into_full(stores));

let global_form = self
.global_form
.unwrap_or_else(|| {
GlobalEdge::partial()
.from_curve_and_vertices(&curve, &vertices)
.into()
})
.into_full(stores);

HalfEdge::new(curve, vertices, global_form)
}

fn extract_global_curve(
&self,
) -> Option<MaybePartial<Handle<GlobalCurve>>> {
let global_curve = self.global_form.as_ref()?.curve()?.clone();
Some(global_curve.into())
}
}

impl From<HalfEdge> for PartialHalfEdge {
fn from(half_edge: HalfEdge) -> Self {
Self {
curve: Some(half_edge.curve().clone().into()),
vertices: half_edge.vertices().clone().map(Into::into).map(Some),
global_form: Some(half_edge.global_form().clone()),
vertices: Some(half_edge.vertices().clone().map(Into::into)),
global_form: Some(half_edge.global_form().clone().into()),
}
}
}
Expand Down
74 changes: 74 additions & 0 deletions crates/fj-kernel/src/partial/maybe_partial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use fj_math::Point;

use crate::{
objects::{GlobalCurve, GlobalEdge, Surface, SurfaceVertex, Vertex},
stores::{Handle, Stores},
};

use super::HasPartialForm;

/// Either a partial object or a full one
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum MaybePartial<T: HasPartialForm> {
/// A full object
Full(T),

/// A partial object
Partial(T::PartialForm),
}

impl<T: HasPartialForm> MaybePartial<T> {
/// Return the full object, either directly or by building it
pub fn into_full(self, stores: &Stores) -> T {
match self {
Self::Partial(partial) => T::from_partial(partial, stores),
Self::Full(full) => full,
}
}

/// Return the partial object, either directly or via conversion
pub fn into_partial(self) -> T::PartialForm {
match self {
Self::Partial(partial) => partial,
Self::Full(full) => full.into(),
}
}
}

impl MaybePartial<GlobalEdge> {
/// Access the curve
pub fn curve(&self) -> Option<&Handle<GlobalCurve>> {
match self {
Self::Full(full) => Some(full.curve()),
Self::Partial(partial) => partial.curve.as_ref(),
}
}
}

impl MaybePartial<SurfaceVertex> {
/// Access the position
pub fn position(&self) -> Option<Point<2>> {
match self {
Self::Full(full) => Some(full.position()),
Self::Partial(partial) => partial.position,
}
}

/// Access the surface
pub fn surface(&self) -> Option<&Surface> {
match self {
Self::Full(full) => Some(full.surface()),
Self::Partial(partial) => partial.surface.as_ref(),
}
}
}

impl MaybePartial<Vertex> {
/// Access the surface form
pub fn surface_form(&self) -> Option<MaybePartial<SurfaceVertex>> {
match self {
Self::Full(full) => Some((*full.surface_form()).into()),
Self::Partial(partial) => partial.surface_form.clone(),
}
}
}
30 changes: 2 additions & 28 deletions crates/fj-kernel/src/partial/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@

mod curve;
mod edge;
mod maybe_partial;
mod vertex;

pub use self::{
curve::{PartialCurve, PartialGlobalCurve},
edge::{PartialGlobalEdge, PartialHalfEdge},
maybe_partial::MaybePartial,
vertex::{PartialGlobalVertex, PartialSurfaceVertex, PartialVertex},
};

Expand All @@ -43,34 +45,6 @@ use crate::{
stores::{Handle, Stores},
};

/// Either a partial object or a full one
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub enum MaybePartial<T: HasPartialForm> {
/// A full object
Full(T),

/// A partial object
Partial(T::PartialForm),
}

impl<T: HasPartialForm> MaybePartial<T> {
/// Return the full object, either directly or by building it
pub fn into_full(self, stores: &Stores) -> T {
match self {
Self::Partial(partial) => T::from_partial(partial, stores),
Self::Full(full) => full,
}
}

/// Return the partial object, either directly or via conversion
pub fn into_partial(self) -> T::PartialForm {
match self {
Self::Partial(partial) => partial,
Self::Full(full) => full.into(),
}
}
}

/// Implemented for types that are partial objects
///
/// # Implementation Note
Expand Down
Loading