use alloc::vec;
use alloc::vec::Vec;
use crate::polygon::{EdgeId, VertexId};
use crate::Point;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NodeId(pub u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ArcId(pub u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NodeKind {
Boundary(VertexId),
EdgeEvent,
SplitEvent,
LimitReached,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Node {
pub position: Point,
pub exact: [f32; 2],
pub offset: f32,
pub kind: NodeKind,
pub sources: Vec<EdgeId>,
}
impl Node {
#[inline]
pub fn is_boundary(&self) -> bool {
matches!(self.kind, NodeKind::Boundary(_))
}
#[inline]
pub fn input_vertex(&self) -> Option<VertexId> {
match self.kind {
NodeKind::Boundary(v) => Some(v),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Arc {
pub nodes: [NodeId; 2],
pub sources: [EdgeId; 2],
}
impl Arc {
#[inline]
pub fn lower(&self) -> NodeId {
self.nodes[0]
}
#[inline]
pub fn upper(&self) -> NodeId {
self.nodes[1]
}
#[inline]
pub fn other(&self, n: NodeId) -> Option<NodeId> {
if self.nodes[0] == n {
Some(self.nodes[1])
} else if self.nodes[1] == n {
Some(self.nodes[0])
} else {
None
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ResidualLoop {
pub nodes: Vec<NodeId>,
pub edges: Vec<EdgeId>,
}
impl ResidualLoop {
#[inline]
pub fn len(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn segments(&self) -> impl Iterator<Item = (NodeId, NodeId, EdgeId)> + '_ {
(0..self.nodes.len()).map(move |i| {
(
self.nodes[i],
self.nodes[(i + 1) % self.nodes.len()],
self.edges[i],
)
})
}
}
#[derive(Clone, Debug, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Skeleton {
pub(crate) nodes: Vec<Node>,
pub(crate) arcs: Vec<Arc>,
pub(crate) node_arcs: Vec<Vec<ArcId>>,
pub(crate) edge_nodes: Vec<[NodeId; 2]>,
pub(crate) residual: Vec<ResidualLoop>,
}
impl Skeleton {
#[inline]
pub fn nodes(&self) -> &[Node] {
&self.nodes
}
#[inline]
pub fn arcs(&self) -> &[Arc] {
&self.arcs
}
#[inline]
pub fn residual(&self) -> &[ResidualLoop] {
&self.residual
}
#[inline]
pub fn node_count(&self) -> usize {
self.nodes.len()
}
#[inline]
pub fn arc_count(&self) -> usize {
self.arcs.len()
}
#[inline]
pub fn node(&self, n: NodeId) -> &Node {
&self.nodes[n.0 as usize]
}
#[inline]
pub fn arc(&self, a: ArcId) -> &Arc {
&self.arcs[a.0 as usize]
}
pub fn node_ids(&self) -> impl Iterator<Item = NodeId> + '_ {
(0..self.nodes.len() as u32).map(NodeId)
}
pub fn arc_ids(&self) -> impl Iterator<Item = ArcId> + '_ {
(0..self.arcs.len() as u32).map(ArcId)
}
#[inline]
pub fn arcs_at(&self, n: NodeId) -> &[ArcId] {
&self.node_arcs[n.0 as usize]
}
#[inline]
pub fn arc_segment(&self, a: ArcId) -> (Point, Point) {
let arc = self.arc(a);
(
self.node(arc.nodes[0]).position,
self.node(arc.nodes[1]).position,
)
}
#[inline]
pub fn closest_input_edges(&self, a: ArcId) -> [EdgeId; 2] {
self.arc(a).sources
}
#[inline]
pub fn closest_input_edges_to_node(&self, n: NodeId) -> &[EdgeId] {
&self.node(n).sources
}
pub fn max_offset(&self) -> f32 {
self.nodes.iter().map(|n| n.offset).fold(0.0, f32::max)
}
pub fn boundary_node(&self, v: VertexId) -> Option<NodeId> {
let id = NodeId(v.0 as u32);
match self.nodes.get(v.0 as usize)?.kind {
NodeKind::Boundary(w) if w == v => Some(id),
_ => None,
}
}
pub fn face(&self, e: EdgeId) -> Option<Vec<NodeId>> {
let [start, end] = *self.edge_nodes.get(e.0 as usize)?;
let mut loop_ = vec![start, end];
let mut cur = end;
let mut came_from: Option<ArcId> = None;
loop {
let next_arc = self
.arcs_at(cur)
.iter()
.copied()
.find(|&a| Some(a) != came_from && self.arc(a).sources.contains(&e));
let other = match next_arc {
Some(a) => {
came_from = Some(a);
self.arc(a).other(cur)?
}
None => {
came_from = None;
self.residual_step(e, cur)?
}
};
if other == start {
return Some(loop_);
}
loop_.push(other);
cur = other;
if loop_.len() > self.nodes.len() + 2 {
return None;
}
}
}
fn residual_step(&self, e: EdgeId, cur: NodeId) -> Option<NodeId> {
self.residual
.iter()
.flat_map(|l| l.segments())
.find(|&(_, to, edge)| edge == e && to == cur)
.map(|(from, _, _)| from)
}
#[inline]
pub fn input_edge_count(&self) -> usize {
self.edge_nodes.len()
}
pub fn faces(&self) -> Option<Vec<Vec<NodeId>>> {
(0..self.edge_nodes.len() as u16)
.map(|i| self.face(EdgeId(i)))
.collect()
}
pub(crate) fn build_adjacency(&mut self) {
self.node_arcs.clear();
self.node_arcs.resize(self.nodes.len(), Vec::new());
for (i, arc) in self.arcs.iter().enumerate() {
let id = ArcId(i as u32);
self.node_arcs[arc.nodes[0].0 as usize].push(id);
if arc.nodes[0] != arc.nodes[1] {
self.node_arcs[arc.nodes[1].0 as usize].push(id);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
fn node(kind: NodeKind, offset: f32) -> Node {
Node {
position: Point::ORIGIN,
exact: [0.0, 0.0],
offset,
kind,
sources: vec![EdgeId(0), EdgeId(1)],
}
}
#[test]
fn arc_other_endpoint() {
let a = Arc {
nodes: [NodeId(1), NodeId(2)],
sources: [EdgeId(0), EdgeId(1)],
};
assert_eq!(a.other(NodeId(1)), Some(NodeId(2)));
assert_eq!(a.other(NodeId(2)), Some(NodeId(1)));
assert_eq!(a.other(NodeId(3)), None);
assert_eq!(a.lower(), NodeId(1));
assert_eq!(a.upper(), NodeId(2));
}
#[test]
fn node_kind_helpers() {
let b = node(NodeKind::Boundary(VertexId(7)), 0.0);
assert!(b.is_boundary());
assert_eq!(b.input_vertex(), Some(VertexId(7)));
let i = node(NodeKind::EdgeEvent, 3.0);
assert!(!i.is_boundary());
assert_eq!(i.input_vertex(), None);
}
#[test]
fn adjacency_lists_every_incident_arc() {
let mut s = Skeleton {
nodes: vec![
node(NodeKind::Boundary(VertexId(0)), 0.0),
node(NodeKind::Boundary(VertexId(1)), 0.0),
node(NodeKind::EdgeEvent, 5.0),
],
arcs: vec![
Arc {
nodes: [NodeId(0), NodeId(2)],
sources: [EdgeId(0), EdgeId(1)],
},
Arc {
nodes: [NodeId(1), NodeId(2)],
sources: [EdgeId(1), EdgeId(2)],
},
],
node_arcs: Vec::new(),
edge_nodes: Vec::new(),
residual: Vec::new(),
};
s.build_adjacency();
assert_eq!(s.arcs_at(NodeId(0)), &[ArcId(0)]);
assert_eq!(s.arcs_at(NodeId(1)), &[ArcId(1)]);
assert_eq!(s.arcs_at(NodeId(2)), &[ArcId(0), ArcId(1)]);
}
#[test]
fn max_offset_of_empty_skeleton_is_zero() {
assert_eq!(Skeleton::default().max_offset(), 0.0);
}
#[test]
fn max_offset_finds_the_ridge() {
let mut s = Skeleton {
nodes: vec![
node(NodeKind::Boundary(VertexId(0)), 0.0),
node(NodeKind::EdgeEvent, 5.0),
node(NodeKind::EdgeEvent, 2.0),
],
arcs: vec![],
node_arcs: Vec::new(),
edge_nodes: Vec::new(),
residual: Vec::new(),
};
s.build_adjacency();
assert_eq!(s.max_offset(), 5.0);
}
}