diff --git a/crates/fj-core/src/operations/build/shell.rs b/crates/fj-core/src/operations/build/shell.rs index 2a6b9b2ab..57a5666f6 100644 --- a/crates/fj-core/src/operations/build/shell.rs +++ b/crates/fj-core/src/operations/build/shell.rs @@ -8,6 +8,7 @@ use crate::{ objects::{Curve, Face, HalfEdge, Shell, Surface, Vertex}, operations::{ build::{BuildFace, BuildHalfEdge, BuildSurface, Polygon}, + geometry::UpdateHalfEdgeGeometry, insert::{Insert, IsInserted, IsInsertedNo, IsInsertedYes}, join::JoinCycle, reverse::ReverseCurveCoordinateSystems, @@ -91,13 +92,22 @@ pub trait BuildShell { .zip_ext([[a, b], [b, c], [c, a]]) .zip_ext(curves_and_boundaries) .map(|((vertex, positions), (curve, boundary))| { - HalfEdge::line_segment( + let half_edge = HalfEdge::line_segment( positions, Some(boundary.reverse().inner), core, - ) - .update_start_vertex(|_, _| vertex, core) - .update_curve(|_, _| curve, core) + ); + half_edge + .update_start_vertex(|_, _| vertex, core) + .update_curve(|_, _| curve, core) + .insert(core) + .set_path( + core.layers + .geometry + .of_half_edge(&half_edge) + .path, + &mut core.layers.geometry, + ) }) }; diff --git a/crates/fj-core/src/operations/geometry/half_edge.rs b/crates/fj-core/src/operations/geometry/half_edge.rs index 8f05de05a..1ac2dd40b 100644 --- a/crates/fj-core/src/operations/geometry/half_edge.rs +++ b/crates/fj-core/src/operations/geometry/half_edge.rs @@ -1,7 +1,8 @@ use fj_math::Point; use crate::{ - geometry::{CurveBoundary, HalfEdgeGeometry, SurfacePath}, + geometry::{CurveBoundary, Geometry, HalfEdgeGeometry, SurfacePath}, + layers::Layer, objects::HalfEdge, operations::insert::Insert, storage::Handle, @@ -10,7 +11,14 @@ use crate::{ /// Update the geometry of a [`HalfEdge`] pub trait UpdateHalfEdgeGeometry { - /// Update the path of the edge + /// Set the path of the half-edge + fn set_path( + self, + path: SurfacePath, + geometry: &mut Layer, + ) -> Self; + + /// Update the path of the half-edge #[must_use] fn update_path( &self, @@ -18,7 +26,7 @@ pub trait UpdateHalfEdgeGeometry { core: &mut Core, ) -> Self; - /// Update the boundary of the edge + /// Update the boundary of the half-edge #[must_use] fn update_boundary( &self, @@ -28,6 +36,15 @@ pub trait UpdateHalfEdgeGeometry { } impl UpdateHalfEdgeGeometry for Handle { + fn set_path( + self, + path: SurfacePath, + geometry: &mut Layer, + ) -> Self { + geometry.define_half_edge(self.clone(), HalfEdgeGeometry { path }); + self + } + fn update_path( &self, update: impl FnOnce(SurfacePath) -> SurfacePath, diff --git a/crates/fj-core/src/operations/join/cycle.rs b/crates/fj-core/src/operations/join/cycle.rs index 91af735fa..563858971 100644 --- a/crates/fj-core/src/operations/join/cycle.rs +++ b/crates/fj-core/src/operations/join/cycle.rs @@ -8,6 +8,7 @@ use crate::{ objects::{Cycle, HalfEdge}, operations::{ build::BuildHalfEdge, + geometry::UpdateHalfEdgeGeometry, insert::Insert, update::{UpdateCycle, UpdateHalfEdge}, }, @@ -129,8 +130,8 @@ impl JoinCycle for Cycle { cycle .update_half_edge( self.half_edges().nth_circular(index), - |edge, core| { - [edge + |half_edge, core| { + [half_edge .update_curve( |_, _| edge_other.curve().clone(), core, @@ -144,17 +145,34 @@ impl JoinCycle for Cycle { .clone() }, core, + ) + .insert(core) + .set_path( + core.layers + .geometry + .of_half_edge(half_edge) + .path, + &mut core.layers.geometry, )] }, core, ) .update_half_edge( self.half_edges().nth_circular(index + 1), - |edge, core| { - [edge.update_start_vertex( - |_, _| edge_other.start_vertex().clone(), - core, - )] + |half_edge, core| { + [half_edge + .update_start_vertex( + |_, _| edge_other.start_vertex().clone(), + core, + ) + .insert(core) + .set_path( + core.layers + .geometry + .of_half_edge(half_edge) + .path, + &mut core.layers.geometry, + )] }, core, ) diff --git a/crates/fj-core/src/operations/split/edge.rs b/crates/fj-core/src/operations/split/edge.rs index fd476bcb5..fb0237a46 100644 --- a/crates/fj-core/src/operations/split/edge.rs +++ b/crates/fj-core/src/operations/split/edge.rs @@ -3,8 +3,8 @@ use fj_math::Point; use crate::{ objects::{HalfEdge, Shell}, operations::{ - derive::DeriveFrom, insert::Insert, replace::ReplaceHalfEdge, - split::SplitHalfEdge, update::UpdateHalfEdge, + derive::DeriveFrom, geometry::UpdateHalfEdgeGeometry, insert::Insert, + replace::ReplaceHalfEdge, split::SplitHalfEdge, update::UpdateHalfEdge, }, queries::SiblingOfHalfEdge, storage::Handle, @@ -55,10 +55,21 @@ impl SplitEdge for Shell { ) .insert(core); [sibling_a, sibling_b].map(|half_edge| { - half_edge.insert(core).derive_from(&sibling, core) + half_edge.insert(core).derive_from(&sibling, core).set_path( + core.layers.geometry.of_half_edge(&sibling).path, + &mut core.layers.geometry, + ) }) }; + let [half_edge_a, half_edge_b] = + [half_edge_a, half_edge_b].map(|half_edge_part| { + half_edge_part.set_path( + core.layers.geometry.of_half_edge(half_edge).path, + &mut core.layers.geometry, + ) + }); + let shell = self .replace_half_edge( half_edge, diff --git a/crates/fj-core/src/operations/split/face.rs b/crates/fj-core/src/operations/split/face.rs index 11a7bf513..0acf856c5 100644 --- a/crates/fj-core/src/operations/split/face.rs +++ b/crates/fj-core/src/operations/split/face.rs @@ -7,6 +7,7 @@ use crate::{ operations::{ build::{BuildCycle, BuildHalfEdge}, derive::DeriveFrom, + geometry::UpdateHalfEdgeGeometry, insert::Insert, split::SplitEdge, update::{ @@ -101,13 +102,20 @@ impl SplitFace for Shell { .expect("Updated shell must contain updated face"); // Build the edge that's going to divide the new faces. - let dividing_half_edge_a_to_d = HalfEdge::line_segment( - [b.start_position(), d.start_position()], - None, - core, - ) - .update_start_vertex(|_, _| b.start_vertex().clone(), core) - .insert(core); + let dividing_half_edge_a_to_d = { + let half_edge = HalfEdge::line_segment( + [b.start_position(), d.start_position()], + None, + core, + ); + half_edge + .update_start_vertex(|_, _| b.start_vertex().clone(), core) + .insert(core) + .set_path( + core.layers.geometry.of_half_edge(&half_edge).path, + &mut core.layers.geometry, + ) + }; let dividing_half_edge_c_to_b = HalfEdge::from_sibling( &dividing_half_edge_a_to_d, d.start_vertex().clone(), diff --git a/crates/fj-core/src/operations/sweep/half_edge.rs b/crates/fj-core/src/operations/sweep/half_edge.rs index 3d0ba3380..883e42525 100644 --- a/crates/fj-core/src/operations/sweep/half_edge.rs +++ b/crates/fj-core/src/operations/sweep/half_edge.rs @@ -6,6 +6,7 @@ use crate::{ objects::{Cycle, Face, HalfEdge, Region, Vertex}, operations::{ build::{BuildCycle, BuildHalfEdge}, + geometry::UpdateHalfEdgeGeometry, insert::Insert, presentation::SetColor, update::{UpdateCycle, UpdateHalfEdge}, @@ -113,26 +114,30 @@ impl SweepHalfEdge for HalfEdge { .zip_ext(vertices) .zip_ext(curves) .map(|((((boundary, start), end), start_vertex), curve)| { - let edge = { - let edge = HalfEdge::line_segment( + let half_edge = { + let line_segment = HalfEdge::line_segment( [start, end], Some(boundary), core, - ) - .update_start_vertex(|_, _| start_vertex, core); + ); + let half_edge = line_segment + .update_start_vertex(|_, _| start_vertex, core); - let edge = if let Some(curve) = curve { - edge.update_curve(|_, _| curve, core) + let half_edge = if let Some(curve) = curve { + half_edge.update_curve(|_, _| curve, core) } else { - edge + half_edge }; - edge.insert(core) + half_edge.insert(core).set_path( + core.layers.geometry.of_half_edge(&line_segment).path, + &mut core.layers.geometry, + ) }; - exterior = exterior.add_half_edges([edge.clone()], core); + exterior = exterior.add_half_edges([half_edge.clone()], core); - edge + half_edge }); let exterior = exterior.insert(core); diff --git a/crates/fj-core/src/validate/shell.rs b/crates/fj-core/src/validate/shell.rs index 48bf7a8cb..51e8d6dba 100644 --- a/crates/fj-core/src/validate/shell.rs +++ b/crates/fj-core/src/validate/shell.rs @@ -404,6 +404,7 @@ mod tests { operations::{ build::BuildShell, geometry::UpdateHalfEdgeGeometry, + insert::Insert, update::{ UpdateCycle, UpdateFace, UpdateHalfEdge, UpdateRegion, UpdateShell, @@ -430,8 +431,36 @@ mod tests { |cycle, core| { cycle.update_half_edge( cycle.half_edges().nth_circular(0), - |edge, core| { - [edge + |half_edge, core| { + // This is going to be weird: + // + // - That first call to `update_path` is + // going to reverse a path and insert + // a new object with the reversed + // path. + // - The next call to `update_boundary` + // relies on that, because it inserts + // an object too, and that would cause + // a validation failure without the + // first call. + // - But the new object created from + // those two operations doesn't + // actually have its geometry set in + // the geometry layer, because that + // happens in `update_path`, for an + // earlier version of the object. + // - So we need a last `set_path`, which + // sets the path again. + // + // This is very weird, but good new is, + // it's just an artifact of the + // transition from a unified object + // graph to separate topology and + // geometry layers. This should clear up + // again, once the separation is + // finished, and all APIs can adapt to + // the new reality. + [half_edge .update_path( |path| path.reverse(), core, @@ -439,6 +468,14 @@ mod tests { .update_boundary( |boundary| boundary.reverse(), core, + ) + .set_path( + core.layers + .geometry + .of_half_edge(half_edge) + .path + .reverse(), + &mut core.layers.geometry, )] }, core, @@ -508,11 +545,20 @@ mod tests { |cycle, core| { cycle.update_half_edge( cycle.half_edges().nth_circular(0), - |edge, core| { - [edge.update_curve( - |_, _| Curve::new(), - core, - )] + |half_edge, core| { + [half_edge + .update_curve( + |_, _| Curve::new(), + core, + ) + .insert(core) + .set_path( + core.layers + .geometry + .of_half_edge(half_edge) + .path, + &mut core.layers.geometry, + )] }, core, )