Merge pull request #2405 from hannobraun/geom

Define vertex geometry in more places
This commit is contained in:
Hanno Braun 2024-06-28 18:55:08 +02:00 committed by GitHub
commit f4676a283f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 407 additions and 166 deletions

View File

@ -2,7 +2,7 @@ use fj_math::{Point, Scalar, Vector};
use itertools::Itertools;
use crate::{
geometry::LocalVertexGeom,
geometry::{CurveBoundary, LocalVertexGeom},
operations::build::BuildHalfEdge,
storage::Handle,
topology::{Cycle, HalfEdge, Surface},
@ -20,6 +20,38 @@ pub trait BuildCycle {
Cycle::new([])
}
/// # Build a cycle from half-edges and associated curve boundaries
fn from_half_edges_and_boundaries<I>(
half_edges_and_boundaries: I,
core: &mut Core,
) -> Cycle
where
I: IntoIterator<Item = (Handle<HalfEdge>, CurveBoundary<Point<1>>)>,
I::IntoIter: Clone + ExactSizeIterator,
{
let half_edges = half_edges_and_boundaries
.into_iter()
.circular_tuple_windows()
.map(|((half_edge, boundary), (next_half_edge, _))| {
let [start, end] = boundary.inner;
core.layers.geometry.define_vertex(
half_edge.start_vertex().clone(),
half_edge.curve().clone(),
LocalVertexGeom { position: start },
);
core.layers.geometry.define_vertex(
next_half_edge.start_vertex().clone(),
half_edge.curve().clone(),
LocalVertexGeom { position: end },
);
half_edge
});
Cycle::new(half_edges)
}
/// # Build a circle
///
/// This circle is built out of 4 distinct arcs.
@ -55,14 +87,14 @@ pub trait BuildCycle {
let angle = Scalar::TAU / 4.;
let half_edges =
[[a, b], [b, c], [c, d], [d, a]]
.into_iter()
.map(|[start, end]| {
HalfEdge::arc(start, end, angle, surface.clone(), core)
});
let half_edges_and_boundaries = [[a, b], [b, c], [c, d], [d, a]]
.into_iter()
.map(|[start, end]| {
HalfEdge::arc(start, end, angle, surface.clone(), core)
})
.collect::<Vec<_>>();
Cycle::new(half_edges)
Self::from_half_edges_and_boundaries(half_edges_and_boundaries, core)
}
/// Build a polygon
@ -84,27 +116,8 @@ pub trait BuildCycle {
HalfEdge::line_segment([start, end], surface.clone(), core)
})
.collect::<Vec<_>>();
let half_edges = half_edges_and_boundaries
.into_iter()
.circular_tuple_windows()
.map(|((half_edge, boundary), (next_half_edge, _))| {
let [start, end] = boundary.inner;
core.layers.geometry.define_vertex(
half_edge.start_vertex().clone(),
half_edge.curve().clone(),
LocalVertexGeom { position: start },
);
core.layers.geometry.define_vertex(
next_half_edge.start_vertex().clone(),
half_edge.curve().clone(),
LocalVertexGeom { position: end },
);
half_edge
});
Cycle::new(half_edges)
Self::from_half_edges_and_boundaries(half_edges_and_boundaries, core)
}
}

View File

@ -50,7 +50,7 @@ pub trait BuildHalfEdge {
angle_rad: impl Into<Scalar>,
surface: Handle<Surface>,
core: &mut Core,
) -> Handle<HalfEdge> {
) -> (Handle<HalfEdge>, CurveBoundary<Point<1>>) {
let angle_rad = angle_rad.into();
if angle_rad <= -Scalar::TAU || angle_rad >= Scalar::TAU {
panic!("arc angle must be in the range (-2pi, 2pi) radians");
@ -60,8 +60,10 @@ pub trait BuildHalfEdge {
let path =
SurfacePath::circle_from_center_and_radius(arc.center, arc.radius);
let boundary =
[arc.start_angle, arc.end_angle].map(|coord| Point::from([coord]));
let boundary = CurveBoundary {
inner: [arc.start_angle, arc.end_angle]
.map(|coord| Point::from([coord])),
};
let half_edge = HalfEdge::unjoined(core).insert(core);
@ -70,14 +72,11 @@ pub trait BuildHalfEdge {
surface,
LocalCurveGeom { path },
);
core.layers.geometry.define_half_edge(
half_edge.clone(),
HalfEdgeGeom {
boundary: boundary.into(),
},
);
core.layers
.geometry
.define_half_edge(half_edge.clone(), HalfEdgeGeom { boundary });
half_edge
(half_edge, boundary)
}
/// Create a line segment

View File

@ -182,8 +182,15 @@ pub trait BuildShell {
cycle
.update_half_edge(
cycle.half_edges().nth_circular(0),
|edge, core| {
[(edge, bad.face.surface())
|half_edge, core| {
[(
half_edge,
cycle
.half_edges()
.nth_circular(1)
.start_vertex(),
bad.face.surface(),
)
.reverse_curve_coordinate_systems(
core,
)]
@ -204,111 +211,154 @@ pub trait BuildShell {
core,
)
};
let dac =
{
let dac = Face::triangle([d, a, c], core);
dac.update_region(
|region, core| {
region.update_exterior(
|cycle, core| {
cycle
.update_half_edge(
cycle.half_edges().nth_circular(1),
|edge, core| {
[(edge, dac.face.surface())
.reverse_curve_coordinate_systems(core)]
},
core,
)
.join_to(
abc.face.region().exterior(),
1..=1,
2..=2,
dac.face.surface().clone(),
core,
)
.update_half_edge(
cycle.half_edges().nth_circular(0),
|edge, core| {
[(edge, dac.face.surface())
.reverse_curve_coordinate_systems(core)]
},
core,
)
.join_to(
bad.face.region().exterior(),
0..=0,
1..=1,
dac.face.surface().clone(),
core,
)
},
core,
)
},
core,
)
};
let cbd =
{
let cbd = Face::triangle([c, b, d], core);
cbd.update_region(
|region, core| {
region.update_exterior(
|cycle, core| {
cycle
.update_half_edge(
cycle.half_edges().nth_circular(0),
|edge, core| {
[(edge, cbd.face.surface())
.reverse_curve_coordinate_systems(core)]
},
core,
)
.update_half_edge(
cycle.half_edges().nth_circular(1),
|edge, core| {
[(edge, cbd.face.surface())
.reverse_curve_coordinate_systems(core)]
},
core,
)
.update_half_edge(
cycle.half_edges().nth_circular(2),
|edge, core| {
[(edge, cbd.face.surface())
.reverse_curve_coordinate_systems(core)]
},
core,
)
.join_to(
abc.face.region().exterior(),
0..=0,
1..=1,
cbd.face.surface().clone(),
core,
)
.join_to(
bad.face.region().exterior(),
1..=1,
2..=2,
cbd.face.surface().clone(),
core,
)
.join_to(
dac.face.region().exterior(),
2..=2,
2..=2,
cbd.face.surface().clone(),
core,
)
},
core,
)
},
core,
)
};
let dac = {
let dac = Face::triangle([d, a, c], core);
dac.update_region(
|region, core| {
region.update_exterior(
|cycle, core| {
cycle
.update_half_edge(
cycle.half_edges().nth_circular(1),
|half_edge, core| {
[(
half_edge,
cycle
.half_edges()
.nth_circular(2)
.start_vertex(),
dac.face.surface(),
)
.reverse_curve_coordinate_systems(
core,
)]
},
core,
)
.join_to(
abc.face.region().exterior(),
1..=1,
2..=2,
dac.face.surface().clone(),
core,
)
.update_half_edge(
cycle.half_edges().nth_circular(0),
|half_edge, core| {
[(
half_edge,
cycle
.half_edges()
.nth_circular(1)
.start_vertex(),
dac.face.surface(),
)
.reverse_curve_coordinate_systems(
core,
)]
},
core,
)
.join_to(
bad.face.region().exterior(),
0..=0,
1..=1,
dac.face.surface().clone(),
core,
)
},
core,
)
},
core,
)
};
let cbd = {
let cbd = Face::triangle([c, b, d], core);
cbd.update_region(
|region, core| {
region.update_exterior(
|cycle, core| {
cycle
.update_half_edge(
cycle.half_edges().nth_circular(0),
|half_edge, core| {
[(
half_edge,
cycle
.half_edges()
.nth_circular(1)
.start_vertex(),
cbd.face.surface(),
)
.reverse_curve_coordinate_systems(
core,
)]
},
core,
)
.update_half_edge(
cycle.half_edges().nth_circular(1),
|half_edge, core| {
[(
half_edge,
cycle
.half_edges()
.nth_circular(2)
.start_vertex(),
cbd.face.surface(),
)
.reverse_curve_coordinate_systems(
core,
)]
},
core,
)
.update_half_edge(
cycle.half_edges().nth_circular(2),
|half_edge, core| {
[(
half_edge,
cycle
.half_edges()
.nth_circular(3)
.start_vertex(),
cbd.face.surface(),
)
.reverse_curve_coordinate_systems(
core,
)]
},
core,
)
.join_to(
abc.face.region().exterior(),
0..=0,
1..=1,
cbd.face.surface().clone(),
core,
)
.join_to(
bad.face.region().exterior(),
1..=1,
2..=2,
cbd.face.surface().clone(),
core,
)
.join_to(
dac.face.region().exterior(),
2..=2,
2..=2,
cbd.face.surface().clone(),
core,
)
},
core,
)
},
core,
)
};
let triangles =
[abc, bad, dac, cbd].map(|triangle| triangle.insert(core));

View File

@ -154,11 +154,16 @@ impl JoinCycle for Cycle {
range.zip(range_other).fold(
self.clone(),
|cycle, (index, index_other)| {
let edge_other = other.half_edges().nth_circular(index_other);
let half_edge = self.half_edges().nth_circular(index);
let half_edge_next = self.half_edges().nth_circular(index + 1);
let half_edge_other =
other.half_edges().nth_circular(index_other);
let half_edge_other_next =
other.half_edges().nth_circular(index_other + 1);
cycle
.update_half_edge(
self.half_edges().nth_circular(index),
half_edge,
|half_edge, core| {
// The curve of the other half-edge we're joining
// this one to already has a curve geometry,
@ -178,22 +183,78 @@ impl JoinCycle for Cycle {
});
if let Some(curve_geom) = curve_geom {
core.layers.geometry.define_curve(
edge_other.curve().clone(),
half_edge_other.curve().clone(),
surface_self.clone(),
curve_geom.clone(),
);
}
// The same goes for vertices. We have to move over
// any local definitions we have to the other
// vertices.
let vertex_geom_prev_end = core
.layers
.geometry
.of_vertex(half_edge.start_vertex())
.and_then(|vertex_geom| {
vertex_geom.local_on(
self.half_edges()
.before(half_edge)
.unwrap()
.curve(),
)
})
.cloned();
let vertex_geom_start = core
.layers
.geometry
.of_vertex(half_edge.start_vertex())
.and_then(|vertex_geom| {
vertex_geom.local_on(half_edge.curve())
})
.cloned();
let vertex_geom_end = core
.layers
.geometry
.of_vertex(half_edge_next.start_vertex())
.and_then(|vertex_geom| {
vertex_geom.local_on(half_edge.curve())
})
.cloned();
if let Some(vertex_geom) = vertex_geom_prev_end {
core.layers.geometry.define_vertex(
half_edge_other_next.start_vertex().clone(),
self.half_edges()
.before(half_edge)
.unwrap()
.curve()
.clone(),
vertex_geom,
);
}
if let Some(vertex_geom_start) = vertex_geom_start {
core.layers.geometry.define_vertex(
half_edge_other_next.start_vertex().clone(),
half_edge_other.curve().clone(),
vertex_geom_start,
);
}
if let Some(vertex_geom_end) = vertex_geom_end {
core.layers.geometry.define_vertex(
half_edge_other.start_vertex().clone(),
half_edge_other.curve().clone(),
vertex_geom_end,
);
}
[half_edge
.update_curve(
|_, _| edge_other.curve().clone(),
|_, _| half_edge_other.curve().clone(),
core,
)
.update_start_vertex(
|_, _| {
other
.half_edges()
.nth_circular(index_other + 1)
half_edge_other_next
.start_vertex()
.clone()
},
@ -211,11 +272,31 @@ impl JoinCycle for Cycle {
core,
)
.update_half_edge(
self.half_edges().nth_circular(index + 1),
half_edge_next,
|half_edge, core| {
// And we need to move over the geometry for this
// vertex too.
let vertex_geom = core
.layers
.geometry
.of_vertex(half_edge_next.start_vertex())
.and_then(|vertex_geom| {
vertex_geom.local_on(half_edge_next.curve())
})
.cloned();
if let Some(vertex_geom) = vertex_geom {
core.layers.geometry.define_vertex(
half_edge_other.start_vertex().clone(),
half_edge.curve().clone(),
vertex_geom,
);
}
[half_edge
.update_start_vertex(
|_, _| edge_other.start_vertex().clone(),
|_, _| {
half_edge_other.start_vertex().clone()
},
core,
)
.insert(core)

View File

@ -14,8 +14,9 @@ impl Reverse for Cycle {
.half_edges()
.pairs()
.map(|(current, next)| {
let mut geometry = *core.layers.geometry.of_half_edge(current);
geometry.boundary = geometry.boundary.reverse();
let mut half_edge_geom =
*core.layers.geometry.of_half_edge(current);
half_edge_geom.boundary = half_edge_geom.boundary.reverse();
HalfEdge::new(
current.curve().clone(),
@ -23,7 +24,7 @@ impl Reverse for Cycle {
)
.insert(core)
.derive_from(current, core)
.set_geometry(geometry, &mut core.layers.geometry)
.set_geometry(half_edge_geom, &mut core.layers.geometry)
})
.collect::<Vec<_>>();

View File

@ -1,20 +1,39 @@
use crate::{
operations::{derive::DeriveFrom, insert::Insert},
storage::Handle,
topology::{HalfEdge, Surface},
topology::{HalfEdge, Surface, Vertex},
Core,
};
use super::ReverseCurveCoordinateSystems;
impl ReverseCurveCoordinateSystems for (&Handle<HalfEdge>, &Handle<Surface>) {
impl ReverseCurveCoordinateSystems
for (&Handle<HalfEdge>, &Handle<Vertex>, &Handle<Surface>)
{
type Reversed = Handle<HalfEdge>;
fn reverse_curve_coordinate_systems(
self,
core: &mut Core,
) -> Self::Reversed {
let (half_edge, surface) = self;
let (half_edge, end_vertex, surface) = self;
let vertex_geom_start = core
.layers
.geometry
.of_vertex(half_edge.start_vertex())
.unwrap()
.local_on(half_edge.curve())
.unwrap()
.clone();
let vertex_geom_end = core
.layers
.geometry
.of_vertex(end_vertex)
.unwrap()
.local_on(half_edge.curve())
.unwrap()
.clone();
let mut half_edge_geom = *core.layers.geometry.of_half_edge(half_edge);
half_edge_geom.boundary = half_edge_geom.boundary.reverse();
@ -26,6 +45,17 @@ impl ReverseCurveCoordinateSystems for (&Handle<HalfEdge>, &Handle<Surface>) {
.insert(core)
.derive_from(half_edge, core);
core.layers.geometry.define_vertex(
half_edge.start_vertex().clone(),
half_edge.curve().clone(),
vertex_geom_end,
);
core.layers.geometry.define_vertex(
end_vertex.clone(),
half_edge.curve().clone(),
vertex_geom_start,
);
core.layers
.geometry
.define_half_edge(half_edge.clone(), half_edge_geom);

View File

@ -266,6 +266,37 @@ mod tests {
&mut core.layers.geometry,
);
let start_vertex =
half_edge.start_vertex();
let end_vertex = cycle
.half_edges()
.after(half_edge)
.unwrap()
.start_vertex();
core.layers.geometry.define_vertex(
start_vertex.clone(),
curve.clone(),
core.layers
.geometry
.of_vertex(start_vertex)
.unwrap()
.local_on(half_edge.curve())
.unwrap()
.clone(),
);
core.layers.geometry.define_vertex(
end_vertex.clone(),
curve.clone(),
core.layers
.geometry
.of_vertex(end_vertex)
.unwrap()
.local_on(half_edge.curve())
.unwrap()
.clone(),
);
[half_edge
.update_curve(|_, _| curve, core)
.insert(core)

View File

@ -142,6 +142,7 @@ fn check_cycle<'r>(
mod tests {
use crate::{
geometry::LocalVertexGeom,
operations::{
build::{BuildFace, BuildHalfEdge},
update::{UpdateCycle, UpdateFace, UpdateRegion},
@ -178,12 +179,47 @@ mod tests {
cycle.update_half_edge(
cycle.half_edges().first(),
|_, core| {
[HalfEdge::line_segment(
[[0., 0.], [2., 0.]],
surface,
core,
)
.0]
let (half_edge, boundary) =
HalfEdge::line_segment(
[[0., 0.], [2., 0.]],
surface,
core,
);
let half_edge_prev =
cycle.half_edges().nth(2).unwrap();
let half_edge_next = cycle
.half_edges()
.nth(1)
.unwrap()
.start_vertex()
.clone();
core.layers.geometry.define_vertex(
half_edge.start_vertex().clone(),
half_edge_prev.curve().clone(),
core.layers
.geometry
.of_vertex(
cycle
.half_edges()
.first()
.start_vertex(),
)
.unwrap()
.local_on(half_edge_prev.curve())
.unwrap()
.clone(),
);
core.layers.geometry.define_vertex(
half_edge_next,
half_edge.curve().clone(),
LocalVertexGeom {
position: boundary.inner[1],
},
);
[half_edge]
},
core,
)