use tracing::{error, instrument};
use crate::{MeshGraph, error_none};
use super::{FaceId, HalfedgeId, VertexId};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Halfedge {
pub end_vertex: VertexId,
pub face: Option<FaceId>,
pub twin: Option<HalfedgeId>,
pub next: Option<HalfedgeId>,
}
impl Halfedge {
#[instrument(skip(mesh_graph))]
pub fn start_vertex(&self, mesh_graph: &MeshGraph) -> Option<VertexId> {
mesh_graph
.halfedges
.get(self.twin.or_else(error_none!("No twin"))?)
.or_else(error_none!("Twin halfedge not found"))
.map(|t| t.end_vertex)
}
#[instrument(skip(mesh_graph))]
pub fn prev(&self, mesh_graph: &MeshGraph) -> Option<HalfedgeId> {
self.next.and_then(|next_id| {
mesh_graph
.halfedges
.get(next_id)
.or_else(error_none!("Next halfedge not found"))
.and_then(|h| h.next)
})
}
#[instrument(skip(mesh_graph))]
pub fn ccw_rotated_neighbour(&self, mesh_graph: &MeshGraph) -> Option<HalfedgeId> {
self.prev(mesh_graph).and_then(|prev| {
mesh_graph
.halfedges
.get(prev)
.or_else(error_none!("Previous halfedge not found"))
.and_then(|h| h.twin.or_else(error_none!("Twin missing")))
})
}
#[instrument(skip(mesh_graph))]
pub fn cw_rotated_neighbour(&self, mesh_graph: &MeshGraph) -> Option<HalfedgeId> {
mesh_graph
.halfedges
.get(self.twin.or_else(error_none!("Twin missing"))?)
.and_then(|h| h.next)
}
#[instrument(skip(mesh_graph))]
pub fn length_squared(&self, mesh_graph: &MeshGraph) -> f32 {
self.len_sqr_inner(mesh_graph).unwrap_or_else(|| {
error!("Halfedge invalid. Defaulting to zero length");
0.0
})
}
fn len_sqr_inner(&self, mesh_graph: &MeshGraph) -> Option<f32> {
let start = mesh_graph.positions.get(self.start_vertex(mesh_graph)?)?;
let end = mesh_graph.positions.get(self.end_vertex)?;
Some(start.distance_squared(*end))
}
#[inline(always)]
pub fn length(&self, mesh_graph: &MeshGraph) -> f32 {
self.length_squared(mesh_graph).sqrt()
}
#[inline]
pub fn is_boundary(&self) -> bool {
self.face.is_none()
}
#[instrument(skip(mesh_graph))]
pub fn is_adjacent_to_boundary(&self, mesh_graph: &MeshGraph) -> bool {
if let Some(start_vertex) = self.start_vertex(mesh_graph) {
mesh_graph
.vertices
.get(start_vertex)
.or_else(error_none!("Start vertex not found"))
.map(|v| v.is_boundary(mesh_graph))
.unwrap_or(false)
|| mesh_graph
.vertices
.get(self.end_vertex)
.or_else(error_none!("End vertex not found"))
.map(|v| v.is_boundary(mesh_graph))
.unwrap_or(false)
} else {
error!("Start vertex ID not found");
false
}
}
pub fn opposite_vertex(&self, mesh_graph: &MeshGraph) -> Option<VertexId> {
let next_he = mesh_graph
.halfedges
.get(self.next?)
.or_else(error_none!("Next halfedge not found"))?;
Some(next_he.end_vertex)
}
}