use crate::ids::{FaceId, HalfEdgeId, VertexId};
use crate::storage::MeshStorage;
#[must_use]
pub struct MeshQuery<T> {
#[allow(clippy::type_complexity)]
f: Box<dyn FnOnce(&MeshStorage) -> Option<T>>,
}
impl<T: 'static> MeshQuery<T> {
pub fn new<F>(f: F) -> Self
where
F: FnOnce(&MeshStorage) -> Option<T> + 'static,
{
Self { f: Box::new(f) }
}
pub fn run(self, mesh: &MeshStorage) -> Option<T> {
(self.f)(mesh)
}
fn then<F, U>(self, f: F) -> MeshQuery<U>
where
F: FnOnce(&MeshStorage, T) -> Option<U> + 'static,
U: 'static,
{
let prev = self.f;
MeshQuery::new(move |mesh| {
let val = prev(mesh)?;
f(mesh, val)
})
}
}
fn he_twin(mesh: &MeshStorage, he: HalfEdgeId) -> Option<HalfEdgeId> {
mesh.get_halfedge(he).and_then(|h| h.twin)
}
fn he_next(mesh: &MeshStorage, he: HalfEdgeId) -> Option<HalfEdgeId> {
mesh.get_halfedge(he).and_then(|h| h.next)
}
fn he_prev(mesh: &MeshStorage, he: HalfEdgeId) -> Option<HalfEdgeId> {
mesh.get_halfedge(he).and_then(|h| h.prev)
}
fn he_face(mesh: &MeshStorage, he: HalfEdgeId) -> Option<FaceId> {
mesh.get_halfedge(he).and_then(|h| h.face)
}
fn he_src_vert(mesh: &MeshStorage, he: HalfEdgeId) -> Option<VertexId> {
mesh.get_halfedge(he)
.and_then(|h| h.twin)
.and_then(|t| mesh.get_halfedge(t))
.map(|t| t.vertex)
}
fn he_dst_vert(mesh: &MeshStorage, he: HalfEdgeId) -> Option<VertexId> {
mesh.get_halfedge(he).map(|h| h.vertex)
}
fn he_cw_rotated(mesh: &MeshStorage, he: HalfEdgeId) -> Option<HalfEdgeId> {
mesh.get_halfedge(he)
.and_then(|h| h.twin)
.and_then(|t| mesh.get_halfedge(t))
.and_then(|h| h.next)
}
fn he_ccw_rotated(mesh: &MeshStorage, he: HalfEdgeId) -> Option<HalfEdgeId> {
mesh.get_halfedge(he)
.and_then(|h| h.prev)
.and_then(|p| mesh.get_halfedge(p))
.and_then(|h| h.twin)
}
fn vertex_halfedge(mesh: &MeshStorage, v: VertexId) -> Option<HalfEdgeId> {
mesh.get_vertex(v).and_then(|vt| vt.halfedge)
}
fn vertex_halfedge_to(mesh: &MeshStorage, v: VertexId, target: VertexId) -> Option<HalfEdgeId> {
crate::traversal::VertexRingLazy::new(mesh, v).find(|&he| {
mesh.get_halfedge(he)
.map(|h| h.vertex == target)
.unwrap_or(false)
})
}
impl HalfEdgeId {
pub fn twin(self) -> MeshQuery<HalfEdgeId> {
MeshQuery::new(move |mesh| he_twin(mesh, self))
}
pub fn next(self) -> MeshQuery<HalfEdgeId> {
MeshQuery::new(move |mesh| he_next(mesh, self))
}
pub fn prev(self) -> MeshQuery<HalfEdgeId> {
MeshQuery::new(move |mesh| he_prev(mesh, self))
}
pub fn face(self) -> MeshQuery<FaceId> {
MeshQuery::new(move |mesh| he_face(mesh, self))
}
pub fn src_vert(self) -> MeshQuery<VertexId> {
MeshQuery::new(move |mesh| he_src_vert(mesh, self))
}
pub fn dst_vert(self) -> MeshQuery<VertexId> {
MeshQuery::new(move |mesh| he_dst_vert(mesh, self))
}
pub fn cw_rotated(self) -> MeshQuery<HalfEdgeId> {
MeshQuery::new(move |mesh| he_cw_rotated(mesh, self))
}
pub fn ccw_rotated(self) -> MeshQuery<HalfEdgeId> {
MeshQuery::new(move |mesh| he_ccw_rotated(mesh, self))
}
}
impl VertexId {
pub fn halfedge(self) -> MeshQuery<HalfEdgeId> {
MeshQuery::new(move |mesh| vertex_halfedge(mesh, self))
}
pub fn halfedge_to(self, target: VertexId) -> MeshQuery<HalfEdgeId> {
MeshQuery::new(move |mesh| vertex_halfedge_to(mesh, self, target))
}
}
fn face_halfedge(mesh: &MeshStorage, f: FaceId) -> Option<HalfEdgeId> {
mesh.get_face(f).and_then(|ft| ft.halfedge)
}
impl FaceId {
pub fn halfedge(self) -> MeshQuery<HalfEdgeId> {
MeshQuery::new(move |mesh| face_halfedge(mesh, self))
}
}
impl MeshQuery<HalfEdgeId> {
pub fn twin(self) -> MeshQuery<HalfEdgeId> {
self.then(he_twin)
}
pub fn next(self) -> MeshQuery<HalfEdgeId> {
self.then(he_next)
}
pub fn prev(self) -> MeshQuery<HalfEdgeId> {
self.then(he_prev)
}
pub fn face(self) -> MeshQuery<FaceId> {
self.then(he_face)
}
pub fn src_vert(self) -> MeshQuery<VertexId> {
self.then(he_src_vert)
}
pub fn dst_vert(self) -> MeshQuery<VertexId> {
self.then(he_dst_vert)
}
pub fn cw_rotated(self) -> MeshQuery<HalfEdgeId> {
self.then(he_cw_rotated)
}
pub fn ccw_rotated(self) -> MeshQuery<HalfEdgeId> {
self.then(he_ccw_rotated)
}
}
impl MeshQuery<VertexId> {
pub fn halfedge(self) -> MeshQuery<HalfEdgeId> {
self.then(vertex_halfedge)
}
pub fn halfedge_to(self, target: VertexId) -> MeshQuery<HalfEdgeId> {
self.then(move |mesh, v| vertex_halfedge_to(mesh, v, target))
}
}
impl MeshQuery<FaceId> {
pub fn halfedge(self) -> MeshQuery<HalfEdgeId> {
self.then(face_halfedge)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::{Face, HalfEdge, MeshStorage, Vertex};
fn build_triangle() -> (MeshStorage, [VertexId; 3], [HalfEdgeId; 6], FaceId) {
let mut mesh = MeshStorage::new();
let v0 = mesh.add_vertex(Vertex::new([0.0, 0.0, 0.0]));
let v1 = mesh.add_vertex(Vertex::new([1.0, 0.0, 0.0]));
let v2 = mesh.add_vertex(Vertex::new([0.0, 1.0, 0.0]));
let h0 = mesh.add_halfedge(HalfEdge::new(v1)); let h1 = mesh.add_halfedge(HalfEdge::new(v2)); let h2 = mesh.add_halfedge(HalfEdge::new(v0)); let t0 = mesh.add_halfedge(HalfEdge::new(v0)); let t1 = mesh.add_halfedge(HalfEdge::new(v1)); let t2 = mesh.add_halfedge(HalfEdge::new(v2));
let f = mesh.add_face(Face::new());
for (he, twin, next, prev) in [(h0, t0, h1, h2), (h1, t1, h2, h0), (h2, t2, h0, h1)] {
let h = mesh.get_halfedge_mut(he).unwrap();
h.twin = Some(twin);
h.next = Some(next);
h.prev = Some(prev);
h.face = Some(f);
}
for (t, he) in [(t0, h0), (t1, h1), (t2, h2)] {
mesh.get_halfedge_mut(t).unwrap().twin = Some(he);
}
mesh.get_vertex_mut(v0).unwrap().halfedge = Some(h0);
mesh.get_vertex_mut(v1).unwrap().halfedge = Some(h1);
mesh.get_vertex_mut(v2).unwrap().halfedge = Some(h2);
mesh.get_face_mut(f).unwrap().halfedge = Some(h0);
(mesh, [v0, v1, v2], [h0, h1, h2, t0, t1, t2], f)
}
fn build_closed_fan() -> (
MeshStorage,
VertexId, // center
[VertexId; 3], // outer verts
[HalfEdgeId; 9], // [a1,a2,a3, b1,b2,b3, c1,c2,c3]
[FaceId; 3],
) {
let mut mesh = MeshStorage::new();
let c = mesh.add_vertex(Vertex::new([0.5, 0.5, 0.0]));
let v0 = mesh.add_vertex(Vertex::new([0.0, 0.0, 0.0]));
let v1 = mesh.add_vertex(Vertex::new([1.0, 0.0, 0.0]));
let v2 = mesh.add_vertex(Vertex::new([0.5, 1.0, 0.0]));
let a1 = mesh.add_halfedge(HalfEdge::new(v0)); let b1 = mesh.add_halfedge(HalfEdge::new(v1)); let c1 = mesh.add_halfedge(HalfEdge::new(c)); let a2 = mesh.add_halfedge(HalfEdge::new(v1)); let b2 = mesh.add_halfedge(HalfEdge::new(v2)); let c2 = mesh.add_halfedge(HalfEdge::new(c)); let a3 = mesh.add_halfedge(HalfEdge::new(v2)); let b3 = mesh.add_halfedge(HalfEdge::new(v0)); let c3 = mesh.add_halfedge(HalfEdge::new(c)); let t1 = mesh.add_halfedge(HalfEdge::new(v0)); let t2 = mesh.add_halfedge(HalfEdge::new(v1)); let t3 = mesh.add_halfedge(HalfEdge::new(v2));
let f1 = mesh.add_face(Face::new());
let f2 = mesh.add_face(Face::new());
let f3 = mesh.add_face(Face::new());
for (he, twin, next, prev, face) in [
(a1, c3, b1, c1, f1),
(b1, t1, c1, a1, f1),
(c1, a2, a1, b1, f1),
(a2, c1, b2, c2, f2),
(b2, t2, c2, a2, f2),
(c2, a3, a2, b2, f2),
(a3, c2, b3, c3, f3),
(b3, t3, c3, a3, f3),
(c3, a1, a3, b3, f3),
] {
let h = mesh.get_halfedge_mut(he).unwrap();
h.twin = Some(twin);
h.next = Some(next);
h.prev = Some(prev);
h.face = Some(face);
}
for (t, he) in [(t1, b1), (t2, b2), (t3, b3)] {
mesh.get_halfedge_mut(t).unwrap().twin = Some(he);
}
mesh.get_vertex_mut(c).unwrap().halfedge = Some(a1);
mesh.get_vertex_mut(v0).unwrap().halfedge = Some(b1);
mesh.get_vertex_mut(v1).unwrap().halfedge = Some(b2);
mesh.get_vertex_mut(v2).unwrap().halfedge = Some(b3);
mesh.get_face_mut(f1).unwrap().halfedge = Some(a1);
mesh.get_face_mut(f2).unwrap().halfedge = Some(a2);
mesh.get_face_mut(f3).unwrap().halfedge = Some(a3);
(
mesh,
c,
[v0, v1, v2],
[a1, a2, a3, b1, b2, b3, c1, c2, c3],
[f1, f2, f3],
)
}
fn build_two_triangles() -> (
MeshStorage,
[VertexId; 4],
[HalfEdgeId; 10], // [h0,h1,h2, g0,g1,g2, t1,t2,t_g1,t_g2]
[FaceId; 2],
) {
let mut mesh = MeshStorage::new();
let v0 = mesh.add_vertex(Vertex::new([0.0, 0.0, 0.0]));
let v1 = mesh.add_vertex(Vertex::new([1.0, 0.0, 0.0]));
let v2 = mesh.add_vertex(Vertex::new([0.0, 1.0, 0.0]));
let v3 = mesh.add_vertex(Vertex::new([1.0, -1.0, 0.0]));
let h0 = mesh.add_halfedge(HalfEdge::new(v1)); let h1 = mesh.add_halfedge(HalfEdge::new(v2)); let h2 = mesh.add_halfedge(HalfEdge::new(v0)); let g0 = mesh.add_halfedge(HalfEdge::new(v0)); let g1 = mesh.add_halfedge(HalfEdge::new(v3)); let g2 = mesh.add_halfedge(HalfEdge::new(v1)); let t1 = mesh.add_halfedge(HalfEdge::new(v1)); let t2 = mesh.add_halfedge(HalfEdge::new(v2)); let t_g1 = mesh.add_halfedge(HalfEdge::new(v0)); let t_g2 = mesh.add_halfedge(HalfEdge::new(v3));
let f1 = mesh.add_face(Face::new());
let f2 = mesh.add_face(Face::new());
for (he, twin, next, prev) in [(h0, g0, h1, h2), (h1, t1, h2, h0), (h2, t2, h0, h1)] {
let h = mesh.get_halfedge_mut(he).unwrap();
h.twin = Some(twin);
h.next = Some(next);
h.prev = Some(prev);
h.face = Some(f1);
}
for (he, twin, next, prev) in [(g0, h0, g1, g2), (g1, t_g1, g2, g0), (g2, t_g2, g0, g1)] {
let h = mesh.get_halfedge_mut(he).unwrap();
h.twin = Some(twin);
h.next = Some(next);
h.prev = Some(prev);
h.face = Some(f2);
}
for (t, he) in [(t1, h1), (t2, h2), (t_g1, g1), (t_g2, g2)] {
mesh.get_halfedge_mut(t).unwrap().twin = Some(he);
}
mesh.get_vertex_mut(v0).unwrap().halfedge = Some(h0);
mesh.get_vertex_mut(v1).unwrap().halfedge = Some(g0);
mesh.get_vertex_mut(v2).unwrap().halfedge = Some(h1);
mesh.get_vertex_mut(v3).unwrap().halfedge = Some(g1);
mesh.get_face_mut(f1).unwrap().halfedge = Some(h0);
mesh.get_face_mut(f2).unwrap().halfedge = Some(g0);
(
mesh,
[v0, v1, v2, v3],
[h0, h1, h2, g0, g1, g2, t1, t2, t_g1, t_g2],
[f1, f2],
)
}
#[test]
fn vertex_halfedge_query() {
let (mesh, v, he, _f) = build_triangle();
let [v0, _v1, _v2] = v;
let [h0, _h1, _h2, _t0, _t1, _t2] = he;
assert_eq!(v0.halfedge().run(&mesh), Some(h0));
}
#[test]
fn vertex_halfedge_to_finds_correct_edge() {
let (mesh, v, he, _f) = build_triangle();
let [v0, v1, v2] = v;
let [h0, _h1, _h2, _t0, _t1, t2] = he;
assert_eq!(v0.halfedge_to(v1).run(&mesh), Some(h0));
assert_eq!(v0.halfedge_to(v2).run(&mesh), Some(t2));
let fake_v = VertexId::default();
assert_eq!(v0.halfedge_to(fake_v).run(&mesh), None);
}
#[test]
fn halfedge_twin_query() {
let (mesh, _v, he, _f) = build_triangle();
let [h0, h1, h2, t0, t1, t2] = he;
assert_eq!(h0.twin().run(&mesh), Some(t0));
assert_eq!(t0.twin().run(&mesh), Some(h0));
assert_eq!(h1.twin().run(&mesh), Some(t1));
assert_eq!(h2.twin().run(&mesh), Some(t2));
}
#[test]
fn halfedge_next_prev_query() {
let (mesh, _v, he, _f) = build_triangle();
let [h0, h1, h2, _t0, _t1, _t2] = he;
assert_eq!(h0.next().run(&mesh), Some(h1));
assert_eq!(h1.next().run(&mesh), Some(h2));
assert_eq!(h2.next().run(&mesh), Some(h0));
assert_eq!(h0.prev().run(&mesh), Some(h2));
assert_eq!(h1.prev().run(&mesh), Some(h0));
assert_eq!(h2.prev().run(&mesh), Some(h1));
}
#[test]
fn halfedge_face_query() {
let (mesh, _v, he, f) = build_triangle();
let [h0, h1, h2, t0, t1, t2] = he;
assert_eq!(h0.face().run(&mesh), Some(f));
assert_eq!(h1.face().run(&mesh), Some(f));
assert_eq!(h2.face().run(&mesh), Some(f));
assert_eq!(t0.face().run(&mesh), None);
assert_eq!(t1.face().run(&mesh), None);
assert_eq!(t2.face().run(&mesh), None);
}
#[test]
fn halfedge_src_dst_vert_query() {
let (mesh, v, he, _f) = build_triangle();
let [v0, v1, v2] = v;
let [h0, h1, _h2, t0, _t1, _t2] = he;
assert_eq!(h0.src_vert().run(&mesh), Some(v0));
assert_eq!(h0.dst_vert().run(&mesh), Some(v1));
assert_eq!(t0.src_vert().run(&mesh), Some(v1));
assert_eq!(t0.dst_vert().run(&mesh), Some(v0));
assert_eq!(h1.src_vert().run(&mesh), Some(v1));
assert_eq!(h1.dst_vert().run(&mesh), Some(v2));
}
#[test]
fn halfedge_rotation_on_closed_fan() {
let (mesh, c, outer, he, _faces) = build_closed_fan();
let [_v0, v1, v2] = outer;
let [a1, a2, a3, _b1, _b2, _b3, _c1, _c2, _c3] = he;
assert_eq!(a1.cw_rotated().run(&mesh), Some(a3));
assert_eq!(a1.ccw_rotated().run(&mesh), Some(a2));
assert_eq!(a1.cw_rotated().dst_vert().run(&mesh), Some(v2));
assert_eq!(a1.ccw_rotated().dst_vert().run(&mesh), Some(v1));
assert_eq!(
a1.cw_rotated().cw_rotated().cw_rotated().run(&mesh),
Some(a1)
);
assert_eq!(
a1.ccw_rotated().ccw_rotated().ccw_rotated().run(&mesh),
Some(a1)
);
assert_eq!(c.halfedge().run(&mesh), Some(a1));
}
#[test]
fn halfedge_cw_rotated_boundary_returns_none() {
let (mesh, _v, he, _f) = build_triangle();
let [h0, _h1, _h2, _t0, _t1, _t2] = he;
assert_eq!(h0.cw_rotated().run(&mesh), None);
}
#[test]
fn chain_user_example_on_two_triangles() {
let (mesh, v, he, _f) = build_two_triangles();
let [v0, v1, _v2, v3] = v;
let [h0, _h1, _h2, _g0, _g1, _g2, _t1, _t2, _t_g1, _t_g2] = he;
assert_eq!(v0.halfedge_to(v1).run(&mesh), Some(h0));
let result = v0.halfedge_to(v1).cw_rotated().dst_vert().run(&mesh);
assert_eq!(result, Some(v3));
}
#[test]
fn chain_closed_fan_rotations() {
let (mesh, c, outer, _he, _faces) = build_closed_fan();
let [v0, v1, v2] = outer;
assert_eq!(
c.halfedge_to(v0).ccw_rotated().dst_vert().run(&mesh),
Some(v1)
);
assert_eq!(
c.halfedge_to(v0).cw_rotated().dst_vert().run(&mesh),
Some(v2)
);
}
#[test]
fn chain_traverses_face_boundary() {
let (mesh, v, he, _f) = build_triangle();
let [v0, _v1, v2] = v;
let [h0, _h1, _h2, _t0, _t1, _t2] = he;
assert_eq!(h0.next().next().next().run(&mesh), Some(h0));
assert_eq!(h0.next().dst_vert().run(&mesh), Some(v2));
assert_eq!(h0.next().next().dst_vert().run(&mesh), Some(v0));
}
#[test]
fn chain_src_dst_roundtrip() {
let (mesh, v, he, _f) = build_triangle();
let [_v0, v1, _v2] = v;
let [h0, _h1, _h2, _t0, _t1, _t2] = he;
assert_eq!(h0.src_vert().halfedge_to(v1).run(&mesh), Some(h0));
let result = h0.dst_vert().halfedge().run(&mesh);
assert!(result.is_some(), "dst_vert().halfedge() 应返回 Some");
}
#[test]
fn chain_short_circuits_on_none() {
let (mesh, _v, he, _f) = build_triangle();
let [h0, _h1, _h2, _t0, _t1, _t2] = he;
assert_eq!(h0.cw_rotated().cw_rotated().dst_vert().run(&mesh), None);
}
#[test]
fn invalid_ids_return_none() {
let mut mesh = MeshStorage::new();
let v = mesh.add_vertex(Vertex::new([0.0; 3])); let he = mesh.add_halfedge(HalfEdge::new(v));
assert_eq!(v.halfedge().run(&mesh), None);
assert_eq!(v.halfedge_to(v).run(&mesh), None);
assert_eq!(he.twin().run(&mesh), None);
assert_eq!(he.next().run(&mesh), None);
assert_eq!(he.prev().run(&mesh), None);
assert_eq!(he.face().run(&mesh), None);
assert_eq!(he.src_vert().run(&mesh), None);
assert_eq!(he.dst_vert().run(&mesh), Some(v));
assert_eq!(he.cw_rotated().run(&mesh), None);
assert_eq!(he.ccw_rotated().run(&mesh), None);
let removed_v = mesh.add_vertex(Vertex::new([1.0; 3]));
mesh.remove_vertex(removed_v);
assert_eq!(removed_v.halfedge().run(&mesh), None);
assert_eq!(removed_v.halfedge_to(removed_v).run(&mesh), None);
let removed_he = mesh.add_halfedge(HalfEdge::new(v));
mesh.remove_halfedge(removed_he);
assert_eq!(removed_he.twin().run(&mesh), None);
assert_eq!(removed_he.next().run(&mesh), None);
assert_eq!(removed_he.dst_vert().run(&mesh), None);
let default_v = VertexId::default();
let default_he = HalfEdgeId::default();
assert_eq!(default_v.halfedge().run(&mesh), None);
assert_eq!(default_he.twin().run(&mesh), None);
}
#[test]
fn chain_on_invalid_ids_short_circuits() {
let mesh = MeshStorage::new();
let default_v = VertexId::default();
assert_eq!(
default_v
.halfedge_to(default_v)
.cw_rotated()
.dst_vert()
.run(&mesh),
None
);
}
#[test]
fn face_halfedge_query() {
let (mesh, _v, _he, f) = build_triangle();
assert!(f.halfedge().run(&mesh).is_some());
assert!(mesh.contains_halfedge(f.halfedge().run(&mesh).unwrap()));
}
#[test]
fn face_chained_query() {
let (mesh, v, _he, f) = build_triangle();
let [_v0, v1, v2] = v;
assert_eq!(f.halfedge().dst_vert().run(&mesh), Some(v1));
assert_eq!(f.halfedge().next().dst_vert().run(&mesh), Some(v2));
}
#[test]
fn face_query_on_invalid_face_returns_none() {
let mesh = MeshStorage::new();
let f = FaceId::default();
assert_eq!(f.halfedge().run(&mesh), None);
assert_eq!(f.halfedge().dst_vert().run(&mesh), None);
}
#[test]
fn meshquery_face_chain_halfedge() {
let (mesh, _v, _he, f) = build_triangle();
let q: MeshQuery<FaceId> =
MeshQuery::new(move |m| if m.contains_face(f) { Some(f) } else { None });
assert!(q.halfedge().run(&mesh).is_some());
}
}