Merge pull request #2288 from hannobraun/geometry

Complete migration of `HalfEdge` boundary to geometry layer
This commit is contained in:
Hanno Braun 2024-03-23 01:46:39 +01:00 committed by GitHub
commit 5d3bab7c3a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 131 additions and 178 deletions

View File

@ -5,7 +5,10 @@ use std::collections::BTreeMap;
use fj_math::Point; use fj_math::Point;
use crate::{ use crate::{
geometry::{CurveBoundary, GlobalPath, SurfaceGeometry, SurfacePath}, geometry::{
CurveBoundary, GlobalPath, HalfEdgeGeometry, SurfaceGeometry,
SurfacePath,
},
objects::Curve, objects::Curve,
storage::Handle, storage::Handle,
Core, Core,
@ -13,14 +16,7 @@ use crate::{
use super::{Approx, ApproxPoint, Tolerance}; use super::{Approx, ApproxPoint, Tolerance};
impl Approx impl Approx for (&Handle<Curve>, &HalfEdgeGeometry, &SurfaceGeometry) {
for (
&Handle<Curve>,
SurfacePath,
&SurfaceGeometry,
CurveBoundary<Point<1>>,
)
{
type Approximation = CurveApprox; type Approximation = CurveApprox;
type Cache = CurveApproxCache; type Cache = CurveApproxCache;
@ -30,20 +26,20 @@ impl Approx
cache: &mut Self::Cache, cache: &mut Self::Cache,
core: &mut Core, core: &mut Core,
) -> Self::Approximation { ) -> Self::Approximation {
let (curve, surface_path, surface, boundary) = self; let (curve, half_edge, surface) = self;
match cache.get(curve, boundary) { match cache.get(curve, half_edge.boundary) {
Some(approx) => approx, Some(approx) => approx,
None => { None => {
let approx = approx_curve( let approx = approx_curve(
&surface_path, &half_edge.path,
surface, surface,
boundary, half_edge.boundary,
tolerance, tolerance,
core, core,
); );
cache.insert(curve.clone(), boundary, approx) cache.insert(curve.clone(), half_edge.boundary, approx)
} }
} }
} }
@ -187,7 +183,10 @@ mod tests {
use crate::{ use crate::{
algorithms::approx::{Approx, ApproxPoint}, algorithms::approx::{Approx, ApproxPoint},
geometry::{CurveBoundary, GlobalPath, SurfaceGeometry, SurfacePath}, geometry::{
CurveBoundary, GlobalPath, HalfEdgeGeometry, SurfaceGeometry,
SurfacePath,
},
objects::Curve, objects::Curve,
operations::insert::Insert, operations::insert::Insert,
Core, Core,
@ -198,14 +197,15 @@ mod tests {
let mut core = Core::new(); let mut core = Core::new();
let curve = Curve::new().insert(&mut core); let curve = Curve::new().insert(&mut core);
let (surface_path, boundary) = let (path, boundary) =
SurfacePath::line_from_points([[1., 1.], [2., 1.]]); SurfacePath::line_from_points([[1., 1.], [2., 1.]]);
let boundary = CurveBoundary::from(boundary); let boundary = CurveBoundary::from(boundary);
let half_edge = HalfEdgeGeometry { path, boundary };
let surface = core.layers.geometry.xz_plane(); let surface = core.layers.geometry.xz_plane();
let tolerance = 1.; let tolerance = 1.;
let approx = (&curve, surface_path, &surface, boundary) let approx =
.approx(tolerance, &mut core); (&curve, &half_edge, &surface).approx(tolerance, &mut core);
assert_eq!(approx.points, vec![]); assert_eq!(approx.points, vec![]);
} }
@ -215,17 +215,18 @@ mod tests {
let mut core = Core::new(); let mut core = Core::new();
let curve = Curve::new().insert(&mut core); let curve = Curve::new().insert(&mut core);
let (surface_path, boundary) = let (path, boundary) =
SurfacePath::line_from_points([[1., 1.], [2., 1.]]); SurfacePath::line_from_points([[1., 1.], [2., 1.]]);
let boundary = CurveBoundary::from(boundary); let boundary = CurveBoundary::from(boundary);
let half_edge = HalfEdgeGeometry { path, boundary };
let surface = SurfaceGeometry { let surface = SurfaceGeometry {
u: GlobalPath::circle_from_radius(1.), u: GlobalPath::circle_from_radius(1.),
v: [0., 0., 1.].into(), v: [0., 0., 1.].into(),
}; };
let tolerance = 1.; let tolerance = 1.;
let approx = (&curve, surface_path, &surface, boundary) let approx =
.approx(tolerance, &mut core); (&curve, &half_edge, &surface).approx(tolerance, &mut core);
assert_eq!(approx.points, vec![]); assert_eq!(approx.points, vec![]);
} }
@ -236,26 +237,26 @@ mod tests {
let global_path = GlobalPath::circle_from_radius(1.); let global_path = GlobalPath::circle_from_radius(1.);
let curve = Curve::new().insert(&mut core); let curve = Curve::new().insert(&mut core);
let surface_path = SurfacePath::line_from_points_with_coords([ let path = SurfacePath::line_from_points_with_coords([
([0.], [0., 1.]), ([0.], [0., 1.]),
([TAU], [TAU, 1.]), ([TAU], [TAU, 1.]),
]); ]);
let boundary = CurveBoundary::from([[0.], [TAU]]); let boundary = CurveBoundary::from([[0.], [TAU]]);
let half_edge = HalfEdgeGeometry { path, boundary };
let surface = SurfaceGeometry { let surface = SurfaceGeometry {
u: global_path, u: global_path,
v: [0., 0., 1.].into(), v: [0., 0., 1.].into(),
}; };
let tolerance = 1.; let tolerance = 1.;
let approx = (&curve, surface_path, &surface, boundary) let approx =
.approx(tolerance, &mut core); (&curve, &half_edge, &surface).approx(tolerance, &mut core);
let expected_approx = (global_path, boundary) let expected_approx = (global_path, boundary)
.approx(tolerance, &mut core) .approx(tolerance, &mut core)
.into_iter() .into_iter()
.map(|(point_local, _)| { .map(|(point_local, _)| {
let point_surface = let point_surface = path.point_from_path_coords(point_local);
surface_path.point_from_path_coords(point_local);
let point_global = let point_global =
surface.point_from_surface_coords(point_surface); surface.point_from_surface_coords(point_surface);
ApproxPoint::new(point_local, point_global) ApproxPoint::new(point_local, point_global)
@ -269,21 +270,20 @@ mod tests {
let mut core = Core::new(); let mut core = Core::new();
let curve = Curve::new().insert(&mut core); let curve = Curve::new().insert(&mut core);
let surface_path = let path = SurfacePath::circle_from_center_and_radius([0., 0.], 1.);
SurfacePath::circle_from_center_and_radius([0., 0.], 1.);
let boundary = CurveBoundary::from([[0.], [TAU]]); let boundary = CurveBoundary::from([[0.], [TAU]]);
let half_edge = HalfEdgeGeometry { path, boundary };
let surface = core.layers.geometry.xz_plane(); let surface = core.layers.geometry.xz_plane();
let tolerance = 1.; let tolerance = 1.;
let approx = (&curve, surface_path, &surface, boundary) let approx =
.approx(tolerance, &mut core); (&curve, &half_edge, &surface).approx(tolerance, &mut core);
let expected_approx = (&surface_path, boundary) let expected_approx = (&path, boundary)
.approx(tolerance, &mut core) .approx(tolerance, &mut core)
.into_iter() .into_iter()
.map(|(point_local, _)| { .map(|(point_local, _)| {
let point_surface = let point_surface = path.point_from_path_coords(point_local);
surface_path.point_from_path_coords(point_local);
let point_global = let point_global =
surface.point_from_surface_coords(point_surface); surface.point_from_surface_coords(point_surface);
ApproxPoint::new(point_local, point_global) ApproxPoint::new(point_local, point_global)

View File

@ -50,9 +50,8 @@ impl Approx for (&Handle<HalfEdge>, &SurfaceGeometry) {
let rest = { let rest = {
let approx = ( let approx = (
half_edge.curve(), half_edge.curve(),
core.layers.geometry.of_half_edge(half_edge).path, &core.layers.geometry.of_half_edge(half_edge),
surface, surface,
half_edge.boundary(),
) )
.approx_with_cache( .approx_with_cache(
tolerance, tolerance,

View File

@ -22,11 +22,10 @@ impl super::BoundingVolume<2> for Handle<HalfEdge> {
}) })
} }
SurfacePath::Line(_) => { SurfacePath::Line(_) => {
let points = self.boundary().inner.map(|point_curve| { let geometry = geometry.of_half_edge(self);
geometry
.of_half_edge(self) let points = geometry.boundary.inner.map(|point_curve| {
.path geometry.path.point_from_path_coords(point_curve)
.point_from_path_coords(point_curve)
}); });
Some(Aabb::<2>::from_points(points)) Some(Aabb::<2>::from_points(points))

View File

@ -40,10 +40,12 @@ impl Cycle {
.next() .next()
.expect("Invalid cycle: expected at least one edge"); .expect("Invalid cycle: expected at least one edge");
let [a, b] = first.boundary().inner; let geometry = geometry.of_half_edge(first);
let [a, b] = geometry.boundary.inner;
let edge_direction_positive = a < b; let edge_direction_positive = a < b;
let circle = match geometry.of_half_edge(first).path { let circle = match geometry.path {
SurfacePath::Circle(circle) => circle, SurfacePath::Circle(circle) => circle,
SurfacePath::Line(_) => unreachable!( SurfacePath::Line(_) => unreachable!(
"Invalid cycle: less than 3 edges, but not all are circles" "Invalid cycle: less than 3 edges, but not all are circles"

View File

@ -1,7 +1,4 @@
use fj_math::Point;
use crate::{ use crate::{
geometry::CurveBoundary,
objects::{Curve, Vertex}, objects::{Curve, Vertex},
storage::Handle, storage::Handle,
}; };
@ -35,30 +32,19 @@ use crate::{
/// [`Shell`]: crate::objects::Shell /// [`Shell`]: crate::objects::Shell
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct HalfEdge { pub struct HalfEdge {
boundary: CurveBoundary<Point<1>>,
curve: Handle<Curve>, curve: Handle<Curve>,
start_vertex: Handle<Vertex>, start_vertex: Handle<Vertex>,
} }
impl HalfEdge { impl HalfEdge {
/// Create an instance of `Edge` /// Create an instance of `Edge`
pub fn new( pub fn new(curve: Handle<Curve>, start_vertex: Handle<Vertex>) -> Self {
boundary: impl Into<CurveBoundary<Point<1>>>,
curve: Handle<Curve>,
start_vertex: Handle<Vertex>,
) -> Self {
Self { Self {
boundary: boundary.into(),
curve, curve,
start_vertex, start_vertex,
} }
} }
/// Access the boundary points of the edge on the curve
pub fn boundary(&self) -> CurveBoundary<Point<1>> {
self.boundary
}
/// Access the curve of the edge /// Access the curve of the edge
pub fn curve(&self) -> &Handle<Curve> { pub fn curve(&self) -> &Handle<Curve> {
&self.curve &self.curve

View File

@ -2,7 +2,7 @@ use fj_interop::ext::ArrayExt;
use fj_math::{Arc, Point, Scalar}; use fj_math::{Arc, Point, Scalar};
use crate::{ use crate::{
geometry::{CurveBoundary, HalfEdgeGeometry, SurfacePath}, geometry::{HalfEdgeGeometry, SurfacePath},
objects::{Curve, HalfEdge, Vertex}, objects::{Curve, HalfEdge, Vertex},
operations::{geometry::UpdateHalfEdgeGeometry, insert::Insert}, operations::{geometry::UpdateHalfEdgeGeometry, insert::Insert},
storage::Handle, storage::Handle,
@ -16,14 +16,11 @@ use crate::{
/// [module-level documentation]: super /// [module-level documentation]: super
pub trait BuildHalfEdge { pub trait BuildHalfEdge {
/// Create a half-edge that is not joined to a sibling /// Create a half-edge that is not joined to a sibling
fn unjoined( fn unjoined(core: &mut Core) -> HalfEdge {
boundary: impl Into<CurveBoundary<Point<1>>>,
core: &mut Core,
) -> HalfEdge {
let curve = Curve::new().insert(core); let curve = Curve::new().insert(core);
let start_vertex = Vertex::new().insert(core); let start_vertex = Vertex::new().insert(core);
HalfEdge::new(boundary, curve, start_vertex) HalfEdge::new(curve, start_vertex)
} }
/// Create a half-edge from its sibling /// Create a half-edge from its sibling
@ -35,13 +32,9 @@ pub trait BuildHalfEdge {
let mut geometry = core.layers.geometry.of_half_edge(sibling); let mut geometry = core.layers.geometry.of_half_edge(sibling);
geometry.boundary = geometry.boundary.reverse(); geometry.boundary = geometry.boundary.reverse();
HalfEdge::new( HalfEdge::new(sibling.curve().clone(), start_vertex)
sibling.boundary().reverse(), .insert(core)
sibling.curve().clone(), .set_geometry(geometry, &mut core.layers.geometry)
start_vertex,
)
.insert(core)
.set_geometry(geometry, &mut core.layers.geometry)
} }
/// Create an arc /// Create an arc
@ -67,7 +60,7 @@ pub trait BuildHalfEdge {
let boundary = let boundary =
[arc.start_angle, arc.end_angle].map(|coord| Point::from([coord])); [arc.start_angle, arc.end_angle].map(|coord| Point::from([coord]));
let half_edge = HalfEdge::unjoined(boundary, core).insert(core); let half_edge = HalfEdge::unjoined(core).insert(core);
core.layers.geometry.define_half_edge( core.layers.geometry.define_half_edge(
half_edge.clone(), half_edge.clone(),
HalfEdgeGeometry { HalfEdgeGeometry {
@ -89,7 +82,7 @@ pub trait BuildHalfEdge {
let boundary = let boundary =
[Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord])); [Scalar::ZERO, Scalar::TAU].map(|coord| Point::from([coord]));
let half_edge = HalfEdge::unjoined(boundary, core).insert(core); let half_edge = HalfEdge::unjoined(core).insert(core);
core.layers.geometry.define_half_edge( core.layers.geometry.define_half_edge(
half_edge.clone(), half_edge.clone(),
HalfEdgeGeometry { HalfEdgeGeometry {
@ -113,15 +106,13 @@ pub trait BuildHalfEdge {
boundary.zip_ext(points_surface), boundary.zip_ext(points_surface),
); );
HalfEdge::unjoined(boundary, core) HalfEdge::unjoined(core).insert(core).set_geometry(
.insert(core) HalfEdgeGeometry {
.set_geometry( path,
HalfEdgeGeometry { boundary: boundary.into(),
path, },
boundary: boundary.into(), &mut core.layers.geometry,
}, )
&mut core.layers.geometry,
)
} }
} }

View File

@ -68,11 +68,7 @@ impl AddHole for Shell {
[Cycle::empty().add_joined_edges( [Cycle::empty().add_joined_edges(
[( [(
entry.clone(), entry.clone(),
core.layers core.layers.geometry.of_half_edge(&entry),
.geometry
.of_half_edge(&entry)
.path,
entry.boundary(),
)], )],
core, core,
)], )],
@ -142,11 +138,7 @@ impl AddHole for Shell {
[Cycle::empty().add_joined_edges( [Cycle::empty().add_joined_edges(
[( [(
entry.clone(), entry.clone(),
core.layers core.layers.geometry.of_half_edge(&entry),
.geometry
.of_half_edge(&entry)
.path,
entry.boundary(),
)], )],
core, core,
)], )],
@ -167,11 +159,7 @@ impl AddHole for Shell {
[Cycle::empty().add_joined_edges( [Cycle::empty().add_joined_edges(
[( [(
exit.clone(), exit.clone(),
core.layers core.layers.geometry.of_half_edge(exit),
.geometry
.of_half_edge(exit)
.path,
exit.boundary(),
)], )],
core, core,
)], )],

View File

@ -1,10 +1,9 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use fj_math::Point;
use itertools::Itertools; use itertools::Itertools;
use crate::{ use crate::{
geometry::{CurveBoundary, HalfEdgeGeometry, SurfacePath}, geometry::HalfEdgeGeometry,
objects::{Cycle, HalfEdge}, objects::{Cycle, HalfEdge},
operations::{ operations::{
build::BuildHalfEdge, build::BuildHalfEdge,
@ -22,9 +21,7 @@ pub trait JoinCycle {
#[must_use] #[must_use]
fn add_joined_edges<Es>(&self, edges: Es, core: &mut Core) -> Self fn add_joined_edges<Es>(&self, edges: Es, core: &mut Core) -> Self
where where
Es: IntoIterator< Es: IntoIterator<Item = (Handle<HalfEdge>, HalfEdgeGeometry)>,
Item = (Handle<HalfEdge>, SurfacePath, CurveBoundary<Point<1>>),
>,
Es::IntoIter: Clone + ExactSizeIterator; Es::IntoIter: Clone + ExactSizeIterator;
/// Join the cycle to another /// Join the cycle to another
@ -81,26 +78,21 @@ pub trait JoinCycle {
impl JoinCycle for Cycle { impl JoinCycle for Cycle {
fn add_joined_edges<Es>(&self, edges: Es, core: &mut Core) -> Self fn add_joined_edges<Es>(&self, edges: Es, core: &mut Core) -> Self
where where
Es: IntoIterator< Es: IntoIterator<Item = (Handle<HalfEdge>, HalfEdgeGeometry)>,
Item = (Handle<HalfEdge>, SurfacePath, CurveBoundary<Point<1>>),
>,
Es::IntoIter: Clone + ExactSizeIterator, Es::IntoIter: Clone + ExactSizeIterator,
{ {
let half_edges = edges let half_edges = edges
.into_iter() .into_iter()
.circular_tuple_windows() .circular_tuple_windows()
.map(|((prev_half_edge, _, _), (half_edge, path, boundary))| { .map(|((prev_half_edge, _), (half_edge, geometry))| {
HalfEdge::unjoined(boundary, core) HalfEdge::unjoined(core)
.update_curve(|_, _| half_edge.curve().clone(), core) .update_curve(|_, _| half_edge.curve().clone(), core)
.update_start_vertex( .update_start_vertex(
|_, _| prev_half_edge.start_vertex().clone(), |_, _| prev_half_edge.start_vertex().clone(),
core, core,
) )
.insert(core) .insert(core)
.set_geometry( .set_geometry(geometry, &mut core.layers.geometry)
HalfEdgeGeometry { path, boundary },
&mut core.layers.geometry,
)
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
self.add_half_edges(half_edges, core) self.add_half_edges(half_edges, core)

View File

@ -18,7 +18,6 @@ impl Reverse for Cycle {
geometry.boundary = geometry.boundary.reverse(); geometry.boundary = geometry.boundary.reverse();
HalfEdge::new( HalfEdge::new(
current.boundary().reverse(),
current.curve().clone(), current.curve().clone(),
next.start_vertex().clone(), next.start_vertex().clone(),
) )

View File

@ -13,13 +13,10 @@ impl ReverseCurveCoordinateSystems for Handle<HalfEdge> {
geometry.path = geometry.path.reverse(); geometry.path = geometry.path.reverse();
geometry.boundary = geometry.boundary.reverse(); geometry.boundary = geometry.boundary.reverse();
let half_edge = HalfEdge::new( let half_edge =
geometry.boundary, HalfEdge::new(self.curve().clone(), self.start_vertex().clone())
self.curve().clone(), .insert(core)
self.start_vertex().clone(), .derive_from(self, core);
)
.insert(core)
.derive_from(self, core);
core.layers core.layers
.geometry .geometry

View File

@ -37,7 +37,7 @@ impl SplitEdge for Shell {
let point = point.into(); let point = point.into();
let sibling = self let sibling = self
.get_sibling_of(half_edge) .get_sibling_of(half_edge, &core.layers.geometry)
.expect("Expected half-edge and its sibling to be part of shell"); .expect("Expected half-edge and its sibling to be part of shell");
let [half_edge_a, half_edge_b] = half_edge.split_half_edge(point, core); let [half_edge_a, half_edge_b] = half_edge.split_half_edge(point, core);

View File

@ -42,36 +42,24 @@ impl SplitHalfEdge for Handle<HalfEdge> {
) -> [Handle<HalfEdge>; 2] { ) -> [Handle<HalfEdge>; 2] {
let point = point.into(); let point = point.into();
let [start, end] = self.boundary().inner; let geometry = core.layers.geometry.of_half_edge(self);
let [start, end] = geometry.boundary.inner;
let a = HalfEdge::new( let a =
[start, point], HalfEdge::new(self.curve().clone(), self.start_vertex().clone())
self.curve().clone(), .insert(core)
self.start_vertex().clone(), .derive_from(self, core)
) .set_geometry(
.insert(core) geometry.with_boundary([start, point]),
.derive_from(self, core) &mut core.layers.geometry,
.set_geometry( );
core.layers let b = HalfEdge::new(self.curve().clone(), Vertex::new().insert(core))
.geometry .insert(core)
.of_half_edge(self) .derive_from(self, core)
.with_boundary([start, point]), .set_geometry(
&mut core.layers.geometry, geometry.with_boundary([point, end]),
); &mut core.layers.geometry,
let b = HalfEdge::new( );
[point, end],
self.curve().clone(),
Vertex::new().insert(core),
)
.insert(core)
.derive_from(self, core)
.set_geometry(
core.layers
.geometry
.of_half_edge(self)
.with_boundary([point, end]),
&mut core.layers.geometry,
);
[a, b] [a, b]
} }

View File

@ -77,8 +77,7 @@ impl SweepCycle for Cycle {
top_edges.push(( top_edges.push((
top_edge, top_edge,
core.layers.geometry.of_half_edge(bottom_half_edge).path, core.layers.geometry.of_half_edge(bottom_half_edge),
bottom_half_edge.boundary(),
)); ));
} }

View File

@ -59,12 +59,8 @@ impl SweepHalfEdge for Handle<HalfEdge> {
) -> (Face, Handle<HalfEdge>) { ) -> (Face, Handle<HalfEdge>) {
let path = path.into(); let path = path.into();
let surface = core let geometry = core.layers.geometry.of_half_edge(self);
.layers let surface = geometry.path.sweep_surface_path(surface, path, core);
.geometry
.of_half_edge(self)
.path
.sweep_surface_path(surface, path, core);
// Next, we need to define the boundaries of the face. Let's start with // Next, we need to define the boundaries of the face. Let's start with
// the global vertices and edges. // the global vertices and edges.
@ -86,7 +82,7 @@ impl SweepHalfEdge for Handle<HalfEdge> {
// Let's figure out the surface coordinates of the edge vertices. // Let's figure out the surface coordinates of the edge vertices.
let surface_points = { let surface_points = {
let [a, b] = self.boundary().inner; let [a, b] = geometry.boundary.inner;
[ [
[a.t, Scalar::ZERO], [a.t, Scalar::ZERO],
@ -104,7 +100,7 @@ impl SweepHalfEdge for Handle<HalfEdge> {
// Now, the boundaries of each edge. // Now, the boundaries of each edge.
let boundaries = { let boundaries = {
let [a, b] = self.boundary().inner; let [a, b] = geometry.boundary.inner;
let [c, d] = [0., 1.].map(|coord| Point::from([coord])); let [c, d] = [0., 1.].map(|coord| Point::from([coord]));
[[a, b], [c, d], [b, a], [d, c]] [[a, b], [c, d], [b, a], [d, c]]

View File

@ -13,7 +13,6 @@ impl TransformObject for Handle<HalfEdge> {
core: &mut Core, core: &mut Core,
cache: &mut TransformCache, cache: &mut TransformCache,
) -> Self { ) -> Self {
let boundary = self.boundary();
let curve = self let curve = self
.curve() .curve()
.clone() .clone()
@ -23,8 +22,7 @@ impl TransformObject for Handle<HalfEdge> {
.clone() .clone()
.transform_with_cache(transform, core, cache); .transform_with_cache(transform, core, cache);
let half_edge = let half_edge = HalfEdge::new(curve, start_vertex).insert(core);
HalfEdge::new(boundary, curve, start_vertex).insert(core);
core.layers.geometry.define_half_edge( core.layers.geometry.define_half_edge(
half_edge.clone(), half_edge.clone(),

View File

@ -38,7 +38,6 @@ impl UpdateHalfEdge for HalfEdge {
T: Insert<Inserted = Handle<Curve>>, T: Insert<Inserted = Handle<Curve>>,
{ {
HalfEdge::new( HalfEdge::new(
self.boundary(),
update(self.curve(), core) update(self.curve(), core)
.insert(core) .insert(core)
.derive_from(self.curve(), core), .derive_from(self.curve(), core),
@ -55,7 +54,6 @@ impl UpdateHalfEdge for HalfEdge {
T: Insert<Inserted = Handle<Vertex>>, T: Insert<Inserted = Handle<Vertex>>,
{ {
HalfEdge::new( HalfEdge::new(
self.boundary(),
self.curve().clone(), self.curve().clone(),
update(self.start_vertex(), core) update(self.start_vertex(), core)
.insert(core) .insert(core)

View File

@ -1,4 +1,5 @@
use crate::{ use crate::{
geometry::Geometry,
objects::{HalfEdge, Shell}, objects::{HalfEdge, Shell},
storage::Handle, storage::Handle,
}; };
@ -8,7 +9,12 @@ use super::BoundingVerticesOfHalfEdge;
/// Queries related to the sibling of a [`HalfEdge`] /// Queries related to the sibling of a [`HalfEdge`]
pub trait SiblingOfHalfEdge { pub trait SiblingOfHalfEdge {
/// Indicate whether the provided half-edges are siblings /// Indicate whether the provided half-edges are siblings
fn are_siblings(&self, a: &Handle<HalfEdge>, b: &Handle<HalfEdge>) -> bool; fn are_siblings(
&self,
a: &Handle<HalfEdge>,
b: &Handle<HalfEdge>,
geometry: &Geometry,
) -> bool;
/// Retrieve the sibling of this half-edge /// Retrieve the sibling of this half-edge
/// ///
@ -18,13 +24,20 @@ pub trait SiblingOfHalfEdge {
fn get_sibling_of( fn get_sibling_of(
&self, &self,
half_edge: &Handle<HalfEdge>, half_edge: &Handle<HalfEdge>,
geometry: &Geometry,
) -> Option<Handle<HalfEdge>>; ) -> Option<Handle<HalfEdge>>;
} }
impl SiblingOfHalfEdge for Shell { impl SiblingOfHalfEdge for Shell {
fn are_siblings(&self, a: &Handle<HalfEdge>, b: &Handle<HalfEdge>) -> bool { fn are_siblings(
&self,
a: &Handle<HalfEdge>,
b: &Handle<HalfEdge>,
geometry: &Geometry,
) -> bool {
let same_curve = a.curve().id() == b.curve().id(); let same_curve = a.curve().id() == b.curve().id();
let same_boundary = a.boundary() == b.boundary().reverse(); let same_boundary = geometry.of_half_edge(a).boundary
== geometry.of_half_edge(b).boundary.reverse();
let same_vertices = { let same_vertices = {
let Some(a_vertices) = self.bounding_vertices_of_half_edge(a) let Some(a_vertices) = self.bounding_vertices_of_half_edge(a)
else { else {
@ -44,11 +57,12 @@ impl SiblingOfHalfEdge for Shell {
fn get_sibling_of( fn get_sibling_of(
&self, &self,
half_edge: &Handle<HalfEdge>, half_edge: &Handle<HalfEdge>,
geometry: &Geometry,
) -> Option<Handle<HalfEdge>> { ) -> Option<Handle<HalfEdge>> {
for face in self.faces() { for face in self.faces() {
for cycle in face.region().all_cycles() { for cycle in face.region().all_cycles() {
for h in cycle.half_edges() { for h in cycle.half_edges() {
if self.are_siblings(half_edge, h) { if self.are_siblings(half_edge, h, geometry) {
return Some(h.clone()); return Some(h.clone());
} }
} }

View File

@ -23,7 +23,7 @@ impl Validate for Shell {
ShellValidationError::check_curve_coordinates( ShellValidationError::check_curve_coordinates(
self, geometry, config, errors, self, geometry, config, errors,
); );
ShellValidationError::check_half_edge_pairs(self, errors); ShellValidationError::check_half_edge_pairs(self, geometry, errors);
ShellValidationError::check_half_edge_coincidence( ShellValidationError::check_half_edge_coincidence(
self, geometry, config, errors, self, geometry, config, errors,
); );
@ -112,7 +112,7 @@ impl ShellValidationError {
// we have right now are circles, 3 would be enough to check // we have right now are circles, 3 would be enough to check
// for coincidence. But the first and last might be // for coincidence. But the first and last might be
// identical, so let's add an extra one. // identical, so let's add an extra one.
let [a, d] = edge_a.boundary().inner; let [a, d] = geometry.of_half_edge(edge_a).boundary.inner;
let b = a + (d - a) * 1. / 3.; let b = a + (d - a) * 1. / 3.;
let c = a + (d - a) * 2. / 3.; let c = a + (d - a) * 2. / 3.;
@ -177,14 +177,18 @@ impl ShellValidationError {
} }
/// Check that each half-edge is part of a pair /// Check that each half-edge is part of a pair
fn check_half_edge_pairs(shell: &Shell, errors: &mut Vec<ValidationError>) { fn check_half_edge_pairs(
shell: &Shell,
geometry: &Geometry,
errors: &mut Vec<ValidationError>,
) {
let mut unmatched_half_edges = BTreeMap::new(); let mut unmatched_half_edges = BTreeMap::new();
for face in shell.faces() { for face in shell.faces() {
for cycle in face.region().all_cycles() { for cycle in face.region().all_cycles() {
for half_edge in cycle.half_edges() { for half_edge in cycle.half_edges() {
let curve = half_edge.curve().clone(); let curve = half_edge.curve().clone();
let boundary = half_edge.boundary(); let boundary = geometry.of_half_edge(half_edge).boundary;
let vertices = let vertices =
cycle.bounding_vertices_of_half_edge(half_edge).expect( cycle.bounding_vertices_of_half_edge(half_edge).expect(
"`half_edge` came from `cycle`, must exist there", "`half_edge` came from `cycle`, must exist there",
@ -200,7 +204,8 @@ impl ShellValidationError {
// currently looking at. Let's make sure the logic // currently looking at. Let's make sure the logic
// we use here to determine that matches the // we use here to determine that matches the
// "official" definition. // "official" definition.
assert!(shell.are_siblings(half_edge, sibling)); assert!(shell
.are_siblings(half_edge, sibling, geometry));
} }
None => { None => {
// If this half-edge has a sibling, we haven't seen // If this half-edge has a sibling, we haven't seen
@ -238,7 +243,7 @@ impl ShellValidationError {
continue; continue;
} }
if shell.are_siblings(half_edge_a, half_edge_b) { if shell.are_siblings(half_edge_a, half_edge_b, geometry) {
// If the half-edges are siblings, they are allowed to be // If the half-edges are siblings, they are allowed to be
// coincident. Must be, in fact. There's another validation // coincident. Must be, in fact. There's another validation
// check that takes care of that. // check that takes care of that.
@ -257,8 +262,11 @@ impl ShellValidationError {
.all(|d| d < config.distinct_min_distance) .all(|d| d < config.distinct_min_distance)
{ {
let boundaries = Box::new(CoincidentHalfEdgeBoundaries { let boundaries = Box::new(CoincidentHalfEdgeBoundaries {
boundaries: [half_edge_a, half_edge_b] boundaries: [half_edge_a, half_edge_b].map(
.map(|half_edge| half_edge.boundary()), |half_edge| {
geometry.of_half_edge(half_edge).boundary
},
),
}); });
let curves = Box::new(CoincidentHalfEdgeCurves { let curves = Box::new(CoincidentHalfEdgeCurves {
curves: [half_edge_a, half_edge_b] curves: [half_edge_a, half_edge_b]
@ -384,7 +392,7 @@ fn distances(
(edge, surface): (&Handle<HalfEdge>, &SurfaceGeometry), (edge, surface): (&Handle<HalfEdge>, &SurfaceGeometry),
geometry: &Geometry, geometry: &Geometry,
) -> Point<3> { ) -> Point<3> {
let [start, end] = edge.boundary().inner; let [start, end] = geometry.of_half_edge(edge).boundary.inner;
let path_coords = start + (end - start) * percent; let path_coords = start + (end - start) * percent;
let surface_coords = geometry let surface_coords = geometry
.of_half_edge(edge) .of_half_edge(edge)
@ -454,7 +462,6 @@ mod tests {
geometry.boundary.reverse(); geometry.boundary.reverse();
[HalfEdge::new( [HalfEdge::new(
half_edge.boundary().reverse(),
half_edge.curve().clone(), half_edge.curve().clone(),
half_edge.start_vertex().clone(), half_edge.start_vertex().clone(),
) )

View File

@ -46,7 +46,7 @@ impl ValidationCheck<Cycle> for AdjacentHalfEdgesNotConnected {
) -> impl Iterator<Item = Self> { ) -> impl Iterator<Item = Self> {
object.half_edges().pairs().filter_map(|(first, second)| { object.half_edges().pairs().filter_map(|(first, second)| {
let end_pos_of_first_half_edge = { let end_pos_of_first_half_edge = {
let [_, end] = first.boundary().inner; let [_, end] = geometry.of_half_edge(first).boundary.inner;
geometry geometry
.of_half_edge(first) .of_half_edge(first)
.path .path