mirror of https://github.com/hannobraun/Fornjot
Merge pull request #1310 from hannobraun/partial
Continue cleanup of partial object API
This commit is contained in:
commit
edcd61ffaf
|
@ -92,8 +92,7 @@ mod tests {
|
|||
.update_as_u_axis()
|
||||
.build(&objects)?;
|
||||
let half_edge = HalfEdge::partial()
|
||||
.with_surface(Some(surface))
|
||||
.update_as_line_segment_from_points([[1., -1.], [1., 1.]])
|
||||
.update_as_line_segment_from_points(surface, [[1., -1.], [1., 1.]])
|
||||
.build(&objects)?;
|
||||
|
||||
let intersection = CurveEdgeIntersection::compute(&curve, &half_edge);
|
||||
|
@ -117,8 +116,10 @@ mod tests {
|
|||
.update_as_u_axis()
|
||||
.build(&objects)?;
|
||||
let half_edge = HalfEdge::partial()
|
||||
.with_surface(Some(surface))
|
||||
.update_as_line_segment_from_points([[-1., -1.], [-1., 1.]])
|
||||
.update_as_line_segment_from_points(
|
||||
surface,
|
||||
[[-1., -1.], [-1., 1.]],
|
||||
)
|
||||
.build(&objects)?;
|
||||
|
||||
let intersection = CurveEdgeIntersection::compute(&curve, &half_edge);
|
||||
|
@ -142,8 +143,10 @@ mod tests {
|
|||
.update_as_u_axis()
|
||||
.build(&objects)?;
|
||||
let half_edge = HalfEdge::partial()
|
||||
.with_surface(Some(surface))
|
||||
.update_as_line_segment_from_points([[-1., -1.], [1., -1.]])
|
||||
.update_as_line_segment_from_points(
|
||||
surface,
|
||||
[[-1., -1.], [1., -1.]],
|
||||
)
|
||||
.build(&objects)?;
|
||||
|
||||
let intersection = CurveEdgeIntersection::compute(&curve, &half_edge);
|
||||
|
@ -162,8 +165,7 @@ mod tests {
|
|||
.update_as_u_axis()
|
||||
.build(&objects)?;
|
||||
let half_edge = HalfEdge::partial()
|
||||
.with_surface(Some(surface))
|
||||
.update_as_line_segment_from_points([[-1., 0.], [1., 0.]])
|
||||
.update_as_line_segment_from_points(surface, [[-1., 0.], [1., 0.]])
|
||||
.build(&objects)?;
|
||||
|
||||
let intersection = CurveEdgeIntersection::compute(&curve, &half_edge);
|
||||
|
|
|
@ -199,8 +199,10 @@ mod tests {
|
|||
let objects = Objects::new();
|
||||
|
||||
let half_edge = HalfEdge::partial()
|
||||
.with_surface(Some(objects.surfaces.xy_plane()))
|
||||
.update_as_line_segment_from_points([[0., 0.], [1., 0.]])
|
||||
.update_as_line_segment_from_points(
|
||||
objects.surfaces.xy_plane(),
|
||||
[[0., 0.], [1., 0.]],
|
||||
)
|
||||
.build(&objects)?;
|
||||
|
||||
let face =
|
||||
|
@ -210,8 +212,10 @@ mod tests {
|
|||
let surface = objects.surfaces.xz_plane();
|
||||
|
||||
let bottom = HalfEdge::partial()
|
||||
.with_surface(Some(surface.clone()))
|
||||
.update_as_line_segment_from_points([[0., 0.], [1., 0.]])
|
||||
.update_as_line_segment_from_points(
|
||||
surface.clone(),
|
||||
[[0., 0.], [1., 0.]],
|
||||
)
|
||||
.build(&objects)?;
|
||||
let side_up = HalfEdge::partial()
|
||||
.with_surface(Some(surface.clone()))
|
||||
|
|
|
@ -125,8 +125,10 @@ mod tests {
|
|||
.array_windows_ext()
|
||||
.map(|&[a, b]| {
|
||||
let half_edge = HalfEdge::partial()
|
||||
.with_surface(Some(objects.surfaces.xy_plane()))
|
||||
.update_as_line_segment_from_points([a, b])
|
||||
.update_as_line_segment_from_points(
|
||||
objects.surfaces.xy_plane(),
|
||||
[a, b],
|
||||
)
|
||||
.build(&objects)?;
|
||||
(half_edge, Color::default()).sweep(UP, &objects)
|
||||
})
|
||||
|
@ -167,8 +169,10 @@ mod tests {
|
|||
.array_windows_ext()
|
||||
.map(|&[a, b]| {
|
||||
let half_edge = HalfEdge::partial()
|
||||
.with_surface(Some(objects.surfaces.xy_plane()))
|
||||
.update_as_line_segment_from_points([a, b])
|
||||
.update_as_line_segment_from_points(
|
||||
objects.surfaces.xy_plane(),
|
||||
[a, b],
|
||||
)
|
||||
.build(&objects)?
|
||||
.reverse(&objects)?;
|
||||
(half_edge, Color::default()).sweep(DOWN, &objects)
|
||||
|
|
|
@ -191,8 +191,7 @@ mod tests {
|
|||
(vertex, surface.clone()).sweep([0., 0., 1.], &objects)?;
|
||||
|
||||
let expected_half_edge = HalfEdge::partial()
|
||||
.with_surface(Some(surface))
|
||||
.update_as_line_segment_from_points([[0., 0.], [0., 1.]])
|
||||
.update_as_line_segment_from_points(surface, [[0., 0.], [0., 1.]])
|
||||
.build(&objects)?;
|
||||
assert_eq!(half_edge, expected_half_edge);
|
||||
Ok(())
|
||||
|
|
|
@ -12,22 +12,11 @@ impl TransformObject for PartialCycle {
|
|||
transform: &Transform,
|
||||
objects: &Objects,
|
||||
) -> Result<Self, ValidationError> {
|
||||
let surface = self
|
||||
.surface()
|
||||
.map(|surface| surface.transform(transform, objects))
|
||||
.transpose()?;
|
||||
let half_edges = self
|
||||
.half_edges()
|
||||
.map(|edge| {
|
||||
Ok(edge
|
||||
.into_partial()
|
||||
.transform(transform, objects)?
|
||||
.with_surface(surface.clone()))
|
||||
})
|
||||
.map(|edge| edge.into_partial().transform(transform, objects))
|
||||
.collect::<Result<Vec<_>, ValidationError>>()?;
|
||||
|
||||
Ok(Self::default()
|
||||
.with_surface(surface)
|
||||
.with_half_edges(half_edges))
|
||||
Ok(Self::default().with_half_edges(half_edges))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,15 +15,10 @@ impl TransformObject for PartialHalfEdge {
|
|||
transform: &Transform,
|
||||
objects: &Objects,
|
||||
) -> Result<Self, ValidationError> {
|
||||
let surface = self
|
||||
.surface()
|
||||
.map(|surface| surface.transform(transform, objects))
|
||||
.transpose()?;
|
||||
let curve: MaybePartial<_> = self
|
||||
.curve()
|
||||
.into_partial()
|
||||
.transform(transform, objects)?
|
||||
.with_surface(surface.clone())
|
||||
.into();
|
||||
let vertices = self.vertices().try_map_ext(
|
||||
|vertex| -> Result<_, ValidationError> {
|
||||
|
@ -41,7 +36,6 @@ impl TransformObject for PartialHalfEdge {
|
|||
.into();
|
||||
|
||||
Ok(Self::default()
|
||||
.with_surface(surface)
|
||||
.with_curve(Some(curve))
|
||||
.with_vertices(Some(vertices))
|
||||
.with_global_form(global_form))
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use fj_math::Point;
|
||||
|
||||
use crate::{
|
||||
objects::{Curve, HalfEdge, SurfaceVertex, Vertex},
|
||||
objects::{Curve, HalfEdge, Surface, SurfaceVertex, Vertex},
|
||||
partial::{HasPartial, MaybePartial, PartialCycle},
|
||||
storage::Handle,
|
||||
};
|
||||
|
||||
use super::{CurveBuilder, HalfEdgeBuilder};
|
||||
|
@ -18,6 +19,7 @@ pub trait CycleBuilder {
|
|||
/// Update the partial cycle with a polygonal chain from the provided points
|
||||
fn with_poly_chain_from_points(
|
||||
self,
|
||||
surface: Handle<Surface>,
|
||||
points: impl IntoIterator<Item = impl Into<Point<2>>>,
|
||||
) -> Self;
|
||||
|
||||
|
@ -49,9 +51,8 @@ impl CycleBuilder for PartialCycle {
|
|||
let mut half_edges = Vec::new();
|
||||
for vertex_next in iter {
|
||||
if let Some(vertex_prev) = previous {
|
||||
let surface = self
|
||||
let surface = vertex_prev
|
||||
.surface()
|
||||
.clone()
|
||||
.expect("Need surface to extend cycle with poly-chain");
|
||||
|
||||
let position_prev = vertex_prev
|
||||
|
@ -61,12 +62,8 @@ impl CycleBuilder for PartialCycle {
|
|||
.position()
|
||||
.expect("Need surface position to extend cycle");
|
||||
|
||||
let from = vertex_prev.update_partial(|partial| {
|
||||
partial.with_surface(Some(surface.clone()))
|
||||
});
|
||||
let to = vertex_next.update_partial(|partial| {
|
||||
partial.with_surface(Some(surface.clone()))
|
||||
});
|
||||
let from = vertex_prev;
|
||||
let to = vertex_next;
|
||||
|
||||
previous = Some(to.clone());
|
||||
|
||||
|
@ -99,10 +96,13 @@ impl CycleBuilder for PartialCycle {
|
|||
|
||||
fn with_poly_chain_from_points(
|
||||
self,
|
||||
surface: Handle<Surface>,
|
||||
points: impl IntoIterator<Item = impl Into<Point<2>>>,
|
||||
) -> Self {
|
||||
self.with_poly_chain(points.into_iter().map(|position| {
|
||||
SurfaceVertex::partial().with_position(Some(position))
|
||||
SurfaceVertex::partial()
|
||||
.with_surface(Some(surface.clone()))
|
||||
.with_position(Some(position))
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -127,8 +127,7 @@ impl CycleBuilder for PartialCycle {
|
|||
|
||||
self.with_half_edges(Some(
|
||||
HalfEdge::partial()
|
||||
.with_surface(Some(surface))
|
||||
.update_as_line_segment_from_points(vertices),
|
||||
.update_as_line_segment_from_points(surface, vertices),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use fj_math::{Point, Scalar};
|
|||
|
||||
use crate::{
|
||||
objects::{
|
||||
Curve, GlobalVertex, Objects, SurfaceVertex, Vertex,
|
||||
Curve, GlobalVertex, Objects, Surface, SurfaceVertex, Vertex,
|
||||
VerticesInNormalizedOrder,
|
||||
},
|
||||
partial::{HasPartial, PartialGlobalEdge, PartialHalfEdge},
|
||||
|
@ -31,6 +31,7 @@ pub trait HalfEdgeBuilder: Sized {
|
|||
/// Update partial half-edge as a line segment, from the given points
|
||||
fn update_as_line_segment_from_points(
|
||||
self,
|
||||
surface: Handle<Surface>,
|
||||
points: [impl Into<Point<2>>; 2],
|
||||
) -> Self;
|
||||
|
||||
|
@ -44,9 +45,10 @@ impl HalfEdgeBuilder for PartialHalfEdge {
|
|||
radius: impl Into<Scalar>,
|
||||
objects: &Objects,
|
||||
) -> Result<Self, ValidationError> {
|
||||
let curve = Curve::partial()
|
||||
let curve = self
|
||||
.curve()
|
||||
.into_partial()
|
||||
.with_global_form(Some(self.extract_global_curve()))
|
||||
.with_surface(self.surface())
|
||||
.update_as_circle_from_radius(radius);
|
||||
|
||||
let path = curve.path().expect("Expected path that was just created");
|
||||
|
@ -55,7 +57,8 @@ impl HalfEdgeBuilder for PartialHalfEdge {
|
|||
[Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord]));
|
||||
|
||||
let global_vertex = self
|
||||
.extract_global_vertices()
|
||||
.global_form()
|
||||
.vertices()
|
||||
.map(|[global_form, _]| global_form)
|
||||
.unwrap_or_else(|| {
|
||||
GlobalVertex::partial()
|
||||
|
@ -65,7 +68,7 @@ impl HalfEdgeBuilder for PartialHalfEdge {
|
|||
|
||||
let surface_vertex = SurfaceVertex::partial()
|
||||
.with_position(Some(path.point_from_path_coords(a_curve)))
|
||||
.with_surface(self.surface())
|
||||
.with_surface(curve.surface())
|
||||
.with_global_form(Some(global_vertex))
|
||||
.build(objects)?;
|
||||
|
||||
|
@ -83,18 +86,20 @@ impl HalfEdgeBuilder for PartialHalfEdge {
|
|||
|
||||
fn update_as_line_segment_from_points(
|
||||
self,
|
||||
surface: Handle<Surface>,
|
||||
points: [impl Into<Point<2>>; 2],
|
||||
) -> Self {
|
||||
let surface = self.surface();
|
||||
let vertices = points.map(|point| {
|
||||
let surface_form = SurfaceVertex::partial()
|
||||
.with_surface(surface.clone())
|
||||
.with_surface(Some(surface.clone()))
|
||||
.with_position(Some(point));
|
||||
|
||||
Vertex::partial().with_surface_form(Some(surface_form))
|
||||
});
|
||||
|
||||
self.with_vertices(Some(vertices)).update_as_line_segment()
|
||||
self.with_surface(Some(surface))
|
||||
.with_vertices(Some(vertices))
|
||||
.update_as_line_segment()
|
||||
}
|
||||
|
||||
fn update_as_line_segment(self) -> Self {
|
||||
|
@ -103,6 +108,7 @@ impl HalfEdgeBuilder for PartialHalfEdge {
|
|||
[&from, &to].map(|vertex| vertex.surface_form());
|
||||
|
||||
let surface = self
|
||||
.curve()
|
||||
.surface()
|
||||
.or_else(|| from_surface.surface())
|
||||
.or_else(|| to_surface.surface())
|
||||
|
@ -147,7 +153,8 @@ impl HalfEdgeBuilder for PartialHalfEdge {
|
|||
must_switch_order
|
||||
};
|
||||
|
||||
self.extract_global_vertices()
|
||||
self.global_form()
|
||||
.vertices()
|
||||
.map(
|
||||
|[a, b]| {
|
||||
if must_switch_order {
|
||||
|
|
|
@ -50,10 +50,14 @@ impl<'a> FaceBuilder<'a> {
|
|||
mut self,
|
||||
points: impl IntoIterator<Item = impl Into<Point<2>>>,
|
||||
) -> Self {
|
||||
let surface = self
|
||||
.surface
|
||||
.as_ref()
|
||||
.expect("Need surface to create polygon");
|
||||
|
||||
self.exterior = Some(
|
||||
Cycle::partial()
|
||||
.with_surface(self.surface.clone())
|
||||
.with_poly_chain_from_points(points)
|
||||
.with_poly_chain_from_points(surface.clone(), points)
|
||||
.close_with_line_segment()
|
||||
.build(self.objects)
|
||||
.unwrap(),
|
||||
|
@ -75,10 +79,14 @@ impl<'a> FaceBuilder<'a> {
|
|||
mut self,
|
||||
points: impl IntoIterator<Item = impl Into<Point<2>>>,
|
||||
) -> Self {
|
||||
let surface = self
|
||||
.surface
|
||||
.as_ref()
|
||||
.expect("Need surface to build polygon.");
|
||||
|
||||
self.interiors.push(
|
||||
Cycle::partial()
|
||||
.with_surface(self.surface.clone())
|
||||
.with_poly_chain_from_points(points)
|
||||
.with_poly_chain_from_points(surface.clone(), points)
|
||||
.close_with_line_segment()
|
||||
.build(self.objects)
|
||||
.unwrap(),
|
||||
|
|
|
@ -89,12 +89,11 @@ impl<'a> ShellBuilder<'a> {
|
|||
.zip(&surfaces)
|
||||
.map(|(half_edge, surface)| {
|
||||
HalfEdge::partial()
|
||||
.with_surface(Some(surface.clone()))
|
||||
.with_global_form(Some(half_edge.global_form().clone()))
|
||||
.update_as_line_segment_from_points([
|
||||
[Z, Z],
|
||||
[edge_length, Z],
|
||||
])
|
||||
.update_as_line_segment_from_points(
|
||||
surface.clone(),
|
||||
[[Z, Z], [edge_length, Z]],
|
||||
)
|
||||
.build(self.objects)
|
||||
.unwrap()
|
||||
})
|
||||
|
@ -188,10 +187,8 @@ impl<'a> ShellBuilder<'a> {
|
|||
.zip(sides_up)
|
||||
.zip(tops.clone())
|
||||
.zip(sides_down)
|
||||
.zip(surfaces)
|
||||
.map(|((((bottom, side_up), top), side_down), surface)| {
|
||||
.map(|(((bottom, side_up), top), side_down)| {
|
||||
let cycle = Cycle::partial()
|
||||
.with_surface(Some(surface))
|
||||
.with_half_edges([bottom, side_up, top, side_down])
|
||||
.build(self.objects)
|
||||
.unwrap();
|
||||
|
|
|
@ -399,8 +399,10 @@ mod tests {
|
|||
|
||||
let surface = objects.surfaces.xy_plane();
|
||||
let object = Cycle::partial()
|
||||
.with_surface(Some(surface))
|
||||
.with_poly_chain_from_points([[0., 0.], [1., 0.], [0., 1.]])
|
||||
.with_poly_chain_from_points(
|
||||
surface,
|
||||
[[0., 0.], [1., 0.], [0., 1.]],
|
||||
)
|
||||
.close_with_line_segment()
|
||||
.build(&objects);
|
||||
|
||||
|
@ -485,8 +487,10 @@ mod tests {
|
|||
let objects = Objects::new();
|
||||
|
||||
let object = HalfEdge::partial()
|
||||
.with_surface(Some(objects.surfaces.xy_plane()))
|
||||
.update_as_line_segment_from_points([[0., 0.], [1., 0.]])
|
||||
.update_as_line_segment_from_points(
|
||||
objects.surfaces.xy_plane(),
|
||||
[[0., 0.], [1., 0.]],
|
||||
)
|
||||
.build(&objects);
|
||||
|
||||
assert_eq!(1, object.curve_iter().count());
|
||||
|
|
|
@ -165,12 +165,10 @@ mod tests {
|
|||
let b = [1., 0.];
|
||||
|
||||
let a_to_b = HalfEdge::partial()
|
||||
.with_surface(Some(surface.clone()))
|
||||
.update_as_line_segment_from_points([a, b])
|
||||
.update_as_line_segment_from_points(surface.clone(), [a, b])
|
||||
.build(&objects)?;
|
||||
let b_to_a = HalfEdge::partial()
|
||||
.with_surface(Some(surface))
|
||||
.update_as_line_segment_from_points([b, a])
|
||||
.update_as_line_segment_from_points(surface, [b, a])
|
||||
.build(&objects)?;
|
||||
|
||||
assert_eq!(a_to_b.global_form(), b_to_a.global_form());
|
||||
|
|
|
@ -64,6 +64,20 @@ impl<T: HasPartial> MaybePartial<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Merge this `MaybePartial` with another of the same type
|
||||
pub fn merge_with(self, other: Self) -> Self {
|
||||
match (self, other) {
|
||||
(Self::Full(_), Self::Full(_)) => {
|
||||
panic!("Can't merge two full objects")
|
||||
}
|
||||
(Self::Full(full), Self::Partial(_))
|
||||
| (Self::Partial(_), Self::Full(full)) => Self::Full(full),
|
||||
(Self::Partial(a), Self::Partial(b)) => {
|
||||
Self::Partial(a.merge_with(b))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return or build a full object
|
||||
///
|
||||
/// If this already is a full object, it is returned. If this is a partial
|
||||
|
@ -160,6 +174,14 @@ impl MaybePartial<GlobalEdge> {
|
|||
}
|
||||
|
||||
impl MaybePartial<HalfEdge> {
|
||||
/// Access the curve
|
||||
pub fn curve(&self) -> MaybePartial<Curve> {
|
||||
match self {
|
||||
Self::Full(full) => full.curve().clone().into(),
|
||||
Self::Partial(partial) => partial.curve(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the front vertex
|
||||
pub fn front(&self) -> MaybePartial<Vertex> {
|
||||
match self {
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
mod maybe_partial;
|
||||
mod objects;
|
||||
mod traits;
|
||||
mod util;
|
||||
|
||||
pub use self::{
|
||||
maybe_partial::MaybePartial,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
objects::{Curve, GlobalCurve, Objects, Surface},
|
||||
partial::MaybePartial,
|
||||
partial::{util::merge_options, MaybePartial},
|
||||
path::SurfacePath,
|
||||
storage::Handle,
|
||||
validate::ValidationError,
|
||||
|
@ -59,6 +59,26 @@ impl PartialCurve {
|
|||
self
|
||||
}
|
||||
|
||||
/// Merge this partial object with another
|
||||
pub fn merge_with(self, other: Self) -> Self {
|
||||
// This is harder than it should be, as `global_form` uses the redundant
|
||||
// `Option<MaybePartial<_>>` representation. There's some code relying
|
||||
// on that though, so we have to live with it for now.
|
||||
let global_form = match (self.global_form, other.global_form) {
|
||||
(Some(a), Some(b)) => Some(a.merge_with(b)),
|
||||
(Some(global_form), None) | (None, Some(global_form)) => {
|
||||
Some(global_form)
|
||||
}
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
Self {
|
||||
path: merge_options(self.path, other.path),
|
||||
surface: merge_options(self.surface, other.surface),
|
||||
global_form,
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a full [`Curve`] from the partial curve
|
||||
pub fn build(
|
||||
self,
|
||||
|
@ -102,6 +122,11 @@ impl From<&Curve> for PartialCurve {
|
|||
pub struct PartialGlobalCurve;
|
||||
|
||||
impl PartialGlobalCurve {
|
||||
/// Merge this partial object with another
|
||||
pub fn merge_with(self, _: Self) -> Self {
|
||||
Self
|
||||
}
|
||||
|
||||
/// Build a full [`GlobalCurve`] from the partial global curve
|
||||
pub fn build(
|
||||
self,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
objects::{Cycle, HalfEdge, Objects, Surface},
|
||||
partial::MaybePartial,
|
||||
partial::{util::merge_options, MaybePartial},
|
||||
storage::Handle,
|
||||
validate::ValidationError,
|
||||
};
|
||||
|
@ -10,46 +10,80 @@ use crate::{
|
|||
/// See [`crate::partial`] for more information.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PartialCycle {
|
||||
surface: Option<Handle<Surface>>,
|
||||
half_edges: Vec<MaybePartial<HalfEdge>>,
|
||||
}
|
||||
|
||||
impl PartialCycle {
|
||||
/// Access the surface that the [`Cycle`] is defined in
|
||||
pub fn surface(&self) -> Option<Handle<Surface>> {
|
||||
self.surface.clone()
|
||||
}
|
||||
|
||||
/// Access the half-edges that make up the [`Cycle`]
|
||||
pub fn half_edges(&self) -> impl Iterator<Item = MaybePartial<HalfEdge>> {
|
||||
self.half_edges.clone().into_iter()
|
||||
}
|
||||
|
||||
/// Update the partial cycle with the given surface
|
||||
pub fn with_surface(mut self, surface: Option<Handle<Surface>>) -> Self {
|
||||
if let Some(surface) = surface {
|
||||
self.surface = Some(surface);
|
||||
}
|
||||
self
|
||||
/// Access the surface that the [`Cycle`]'s [`HalfEdge`]s are defined in
|
||||
pub fn surface(&self) -> Option<Handle<Surface>> {
|
||||
self.half_edges
|
||||
.first()
|
||||
.and_then(|half_edge| half_edge.curve().surface())
|
||||
}
|
||||
|
||||
/// Update the partial cycle with the given half-edges
|
||||
/// Add the provided half-edges to the partial cycle
|
||||
///
|
||||
/// This will merge all the surfaces of the added half-edges. All added
|
||||
/// half-edges will end up with the same merged surface.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics, if the surfaces can't be merged.
|
||||
pub fn with_half_edges(
|
||||
mut self,
|
||||
half_edges: impl IntoIterator<Item = impl Into<MaybePartial<HalfEdge>>>,
|
||||
) -> Self {
|
||||
self.half_edges
|
||||
.extend(half_edges.into_iter().map(Into::into));
|
||||
let half_edges = half_edges.into_iter().map(Into::into);
|
||||
|
||||
let mut surface = self.surface();
|
||||
for half_edge in half_edges {
|
||||
surface = merge_options(surface, half_edge.curve().surface());
|
||||
self.half_edges.push(half_edge);
|
||||
}
|
||||
|
||||
self.with_surface(surface)
|
||||
}
|
||||
|
||||
/// Update the partial cycle with the provided surface
|
||||
///
|
||||
/// All [`HalfEdge`]s will be updated with this surface.
|
||||
pub fn with_surface(mut self, surface: Option<Handle<Surface>>) -> Self {
|
||||
if let Some(surface) = surface {
|
||||
for half_edge in &mut self.half_edges {
|
||||
*half_edge = half_edge.clone().update_partial(|half_edge| {
|
||||
half_edge.with_surface(Some(surface.clone()))
|
||||
});
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Merge this partial object with another
|
||||
pub fn merge_with(self, other: Self) -> Self {
|
||||
let a_is_empty = self.half_edges.is_empty();
|
||||
let b_is_empty = other.half_edges.is_empty();
|
||||
let half_edges = match (a_is_empty, b_is_empty) {
|
||||
(true, true) => {
|
||||
panic!("Can't merge `PartialHalfEdge`, if both have half-edges")
|
||||
}
|
||||
(true, false) => self.half_edges,
|
||||
(false, true) => other.half_edges,
|
||||
(false, false) => self.half_edges, // doesn't matter which we use
|
||||
};
|
||||
|
||||
Self { half_edges }
|
||||
}
|
||||
|
||||
/// Build a full [`Cycle`] from the partial cycle
|
||||
pub fn build(
|
||||
mut self,
|
||||
objects: &Objects,
|
||||
) -> Result<Handle<Cycle>, ValidationError> {
|
||||
let surface = self.surface.expect("Need surface to build `Cycle`");
|
||||
let surface_for_edges = surface.clone();
|
||||
let half_edges = {
|
||||
let last_vertex = self
|
||||
.half_edges
|
||||
|
@ -65,11 +99,7 @@ impl PartialCycle {
|
|||
.map(|(half_edge, vertex, surface_vertex)|
|
||||
-> Result<_, ValidationError>
|
||||
{
|
||||
let surface_vertex = surface_vertex
|
||||
.update_partial(|surface_vertex| {
|
||||
surface_vertex.with_surface(Some(surface.clone()))
|
||||
})
|
||||
.into_full(objects)?;
|
||||
let surface_vertex = surface_vertex.into_full(objects)?;
|
||||
|
||||
*half_edge =
|
||||
half_edge.clone().update_partial(|half_edge| {
|
||||
|
@ -98,9 +128,7 @@ impl PartialCycle {
|
|||
partial.with_surface_form(previous_vertex)
|
||||
});
|
||||
|
||||
half_edge
|
||||
.with_surface(Some(surface_for_edges.clone()))
|
||||
.with_back_vertex(Some(back))
|
||||
half_edge.with_back_vertex(Some(back))
|
||||
})
|
||||
.into_full(objects)?;
|
||||
|
||||
|
@ -121,7 +149,6 @@ impl PartialCycle {
|
|||
impl From<&Cycle> for PartialCycle {
|
||||
fn from(cycle: &Cycle) -> Self {
|
||||
Self {
|
||||
surface: Some(cycle.surface().clone()),
|
||||
half_edges: cycle.half_edges().cloned().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
Curve, GlobalCurve, GlobalEdge, GlobalVertex, HalfEdge, Objects,
|
||||
Surface, Vertex,
|
||||
},
|
||||
partial::MaybePartial,
|
||||
partial::{util::merge_arrays, MaybePartial},
|
||||
storage::Handle,
|
||||
validate::ValidationError,
|
||||
};
|
||||
|
@ -16,18 +16,12 @@ use crate::{
|
|||
/// See [`crate::partial`] for more information.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct PartialHalfEdge {
|
||||
surface: Option<Handle<Surface>>,
|
||||
curve: MaybePartial<Curve>,
|
||||
vertices: [MaybePartial<Vertex>; 2],
|
||||
global_form: MaybePartial<GlobalEdge>,
|
||||
}
|
||||
|
||||
impl PartialHalfEdge {
|
||||
/// Access the surface that the [`HalfEdge`]'s [`Curve`] is defined in
|
||||
pub fn surface(&self) -> Option<Handle<Surface>> {
|
||||
self.surface.clone()
|
||||
}
|
||||
|
||||
/// Access the curve that the [`HalfEdge`] is defined in
|
||||
pub fn curve(&self) -> MaybePartial<Curve> {
|
||||
self.curve.clone()
|
||||
|
@ -52,17 +46,24 @@ impl PartialHalfEdge {
|
|||
.unwrap_or_else(|| self.global_form.curve())
|
||||
}
|
||||
|
||||
/// Access the vertices of the global form, if available
|
||||
pub fn extract_global_vertices(
|
||||
&self,
|
||||
) -> Option<[MaybePartial<GlobalVertex>; 2]> {
|
||||
self.global_form.vertices()
|
||||
}
|
||||
|
||||
/// Update the partial half-edge with the given surface
|
||||
pub fn with_surface(mut self, surface: Option<Handle<Surface>>) -> Self {
|
||||
if let Some(surface) = surface {
|
||||
self.surface = Some(surface);
|
||||
self.curve = self.curve.update_partial(|curve| {
|
||||
curve.with_surface(Some(surface.clone()))
|
||||
});
|
||||
|
||||
self.vertices = self.vertices.map(|vertex| {
|
||||
vertex.update_partial(|vertex| {
|
||||
let surface_form = vertex.surface_form().update_partial(
|
||||
|surface_vertex| {
|
||||
surface_vertex.with_surface(Some(surface.clone()))
|
||||
},
|
||||
);
|
||||
|
||||
vertex.with_surface_form(Some(surface_form))
|
||||
})
|
||||
});
|
||||
}
|
||||
self
|
||||
}
|
||||
|
@ -78,7 +79,7 @@ impl PartialHalfEdge {
|
|||
self
|
||||
}
|
||||
|
||||
/// Update the partial half-edge with the given from vertex
|
||||
/// Update the partial half-edge with the given back vertex
|
||||
pub fn with_back_vertex(
|
||||
mut self,
|
||||
vertex: Option<impl Into<MaybePartial<Vertex>>>,
|
||||
|
@ -90,7 +91,7 @@ impl PartialHalfEdge {
|
|||
self
|
||||
}
|
||||
|
||||
/// Update the partial half-edge with the given from vertex
|
||||
/// Update the partial half-edge with the given front vertex
|
||||
pub fn with_front_vertex(
|
||||
mut self,
|
||||
vertex: Option<impl Into<MaybePartial<Vertex>>>,
|
||||
|
@ -125,16 +126,21 @@ impl PartialHalfEdge {
|
|||
self
|
||||
}
|
||||
|
||||
/// Merge this partial object with another
|
||||
pub fn merge_with(self, other: Self) -> Self {
|
||||
Self {
|
||||
curve: self.curve.merge_with(other.curve),
|
||||
vertices: merge_arrays(self.vertices, other.vertices),
|
||||
global_form: self.global_form.merge_with(other.global_form),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a full [`HalfEdge`] from the partial half-edge
|
||||
pub fn build(
|
||||
self,
|
||||
objects: &Objects,
|
||||
) -> Result<Handle<HalfEdge>, ValidationError> {
|
||||
let surface = self.surface;
|
||||
let curve = self
|
||||
.curve
|
||||
.update_partial(|curve| curve.with_surface(surface))
|
||||
.into_full(objects)?;
|
||||
let curve = self.curve.into_full(objects)?;
|
||||
let vertices = self.vertices.try_map_ext(|vertex| {
|
||||
vertex
|
||||
.update_partial(|vertex| vertex.with_curve(Some(curve.clone())))
|
||||
|
@ -160,7 +166,6 @@ impl From<&HalfEdge> for PartialHalfEdge {
|
|||
half_edge.vertices().clone().map(Into::into);
|
||||
|
||||
Self {
|
||||
surface: Some(half_edge.curve().surface().clone()),
|
||||
curve: half_edge.curve().clone().into(),
|
||||
vertices: [back_vertex, front_vertex],
|
||||
global_form: half_edge.global_form().clone().into(),
|
||||
|
@ -210,6 +215,23 @@ impl PartialGlobalEdge {
|
|||
self
|
||||
}
|
||||
|
||||
/// Merge this partial object with another
|
||||
pub fn merge_with(self, other: Self) -> Self {
|
||||
// This is harder than it needs to be, because `vertices` uses the
|
||||
// redundant combination of `Option` and `MaybePartial`. There's some
|
||||
// code relying on that, however, so we have to live with it for now.
|
||||
let vertices = match (self.vertices, other.vertices) {
|
||||
(Some(a), Some(b)) => Some(merge_arrays(a, b)),
|
||||
(Some(vertices), None) | (None, Some(vertices)) => Some(vertices),
|
||||
(None, None) => None,
|
||||
};
|
||||
|
||||
Self {
|
||||
curve: self.curve.merge_with(other.curve),
|
||||
vertices,
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a full [`GlobalEdge`] from the partial global edge
|
||||
pub fn build(
|
||||
self,
|
||||
|
|
|
@ -27,6 +27,10 @@ macro_rules! impl_traits {
|
|||
impl Partial for $partial {
|
||||
type Full = $full;
|
||||
|
||||
fn merge_with(self, other: Self) -> Self {
|
||||
self.merge_with(other)
|
||||
}
|
||||
|
||||
fn build(self, objects: &Objects)
|
||||
-> Result<
|
||||
Handle<Self::Full>,
|
||||
|
|
|
@ -3,7 +3,7 @@ use fj_math::Point;
|
|||
use crate::{
|
||||
builder::GlobalVertexBuilder,
|
||||
objects::{Curve, GlobalVertex, Objects, Surface, SurfaceVertex, Vertex},
|
||||
partial::MaybePartial,
|
||||
partial::{util::merge_options, MaybePartial},
|
||||
storage::Handle,
|
||||
validate::ValidationError,
|
||||
};
|
||||
|
@ -67,6 +67,15 @@ impl PartialVertex {
|
|||
self
|
||||
}
|
||||
|
||||
/// Merge this partial object with another
|
||||
pub fn merge_with(self, other: Self) -> Self {
|
||||
Self {
|
||||
position: merge_options(self.position, other.position),
|
||||
curve: self.curve.merge_with(other.curve),
|
||||
surface_form: self.surface_form.merge_with(other.surface_form),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a full [`Vertex`] from the partial vertex
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -170,6 +179,15 @@ impl PartialSurfaceVertex {
|
|||
self
|
||||
}
|
||||
|
||||
/// Merge this partial object with another
|
||||
pub fn merge_with(self, other: Self) -> Self {
|
||||
Self {
|
||||
position: merge_options(self.position, other.position),
|
||||
surface: merge_options(self.surface, other.surface),
|
||||
global_form: self.global_form.merge_with(other.global_form),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a full [`SurfaceVertex`] from the partial surface vertex
|
||||
pub fn build(
|
||||
self,
|
||||
|
@ -232,6 +250,13 @@ impl PartialGlobalVertex {
|
|||
self
|
||||
}
|
||||
|
||||
/// Merge this partial object with another
|
||||
pub fn merge_with(self, other: Self) -> Self {
|
||||
Self {
|
||||
position: merge_options(self.position, other.position),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a full [`GlobalVertex`] from the partial global vertex
|
||||
pub fn build(
|
||||
self,
|
||||
|
|
|
@ -68,6 +68,9 @@ pub trait Partial: Default + for<'a> From<&'a Self::Full> {
|
|||
/// The type representing the full variant of this object
|
||||
type Full;
|
||||
|
||||
/// Merge another partial object of the same type into this one
|
||||
fn merge_with(self, other: Self) -> Self;
|
||||
|
||||
/// Build a full object from this partial one
|
||||
///
|
||||
/// Implementations of this method will typically try to infer any missing
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
use fj_interop::ext::ArrayExt;
|
||||
|
||||
use super::{HasPartial, MaybePartial};
|
||||
|
||||
pub fn merge_options<T>(a: Option<T>, b: Option<T>) -> Option<T>
|
||||
where
|
||||
T: Eq,
|
||||
{
|
||||
if a == b {
|
||||
return a;
|
||||
}
|
||||
|
||||
// We know that `a != b`, or we wouldn't have made it here.
|
||||
if a.is_some() && b.is_some() {
|
||||
panic!("Can't merge `Option`s if both are defined");
|
||||
}
|
||||
|
||||
a.xor(b)
|
||||
}
|
||||
|
||||
pub fn merge_arrays<T: HasPartial>(
|
||||
a: [MaybePartial<T>; 2],
|
||||
b: [MaybePartial<T>; 2],
|
||||
) -> [MaybePartial<T>; 2] {
|
||||
a.zip_ext(b).map(|(a, b)| a.merge_with(b))
|
||||
}
|
|
@ -207,8 +207,10 @@ mod tests {
|
|||
let objects = Objects::new();
|
||||
|
||||
let valid = HalfEdge::partial()
|
||||
.with_surface(Some(objects.surfaces.xy_plane()))
|
||||
.update_as_line_segment_from_points([[0., 0.], [1., 0.]])
|
||||
.update_as_line_segment_from_points(
|
||||
objects.surfaces.xy_plane(),
|
||||
[[0., 0.], [1., 0.]],
|
||||
)
|
||||
.build(&objects)?;
|
||||
let invalid = {
|
||||
let mut vertices = valid.vertices().clone();
|
||||
|
@ -232,8 +234,10 @@ mod tests {
|
|||
let objects = Objects::new();
|
||||
|
||||
let valid = HalfEdge::partial()
|
||||
.with_surface(Some(objects.surfaces.xy_plane()))
|
||||
.update_as_line_segment_from_points([[0., 0.], [1., 0.]])
|
||||
.update_as_line_segment_from_points(
|
||||
objects.surfaces.xy_plane(),
|
||||
[[0., 0.], [1., 0.]],
|
||||
)
|
||||
.build(&objects)?;
|
||||
let invalid = HalfEdge::new(
|
||||
valid.vertices().clone(),
|
||||
|
@ -255,8 +259,10 @@ mod tests {
|
|||
let objects = Objects::new();
|
||||
|
||||
let valid = HalfEdge::partial()
|
||||
.with_surface(Some(objects.surfaces.xy_plane()))
|
||||
.update_as_line_segment_from_points([[0., 0.], [1., 0.]])
|
||||
.update_as_line_segment_from_points(
|
||||
objects.surfaces.xy_plane(),
|
||||
[[0., 0.], [1., 0.]],
|
||||
)
|
||||
.build(&objects)?;
|
||||
let invalid = HalfEdge::new(
|
||||
valid.vertices().clone(),
|
||||
|
@ -285,8 +291,10 @@ mod tests {
|
|||
let objects = Objects::new();
|
||||
|
||||
let valid = HalfEdge::partial()
|
||||
.with_surface(Some(objects.surfaces.xy_plane()))
|
||||
.update_as_line_segment_from_points([[0., 0.], [1., 0.]])
|
||||
.update_as_line_segment_from_points(
|
||||
objects.surfaces.xy_plane(),
|
||||
[[0., 0.], [1., 0.]],
|
||||
)
|
||||
.build(&objects)?;
|
||||
let invalid = HalfEdge::new(
|
||||
valid.vertices().clone().try_map_ext(|vertex| {
|
||||
|
|
Loading…
Reference in New Issue