use std::collections::HashSet;
use spade::{
handles::{FixedFaceHandle, FixedUndirectedEdgeHandle, FixedVertexHandle, InnerTag},
DelaunayTriangulation, HasPosition, HierarchyHintGenerator, Point2, Triangulation,
};
#[cfg(feature = "lophat")]
mod lophat;
#[cfg(feature = "lophat")]
pub use crate::lophat::sparsify;
pub struct FilteredPoint2 {
pub point: Point2<f64>,
#[allow(dead_code)]
pub filtration: f64,
}
impl HasPosition for FilteredPoint2 {
type Scalar = <Point2<f64> as HasPosition>::Scalar;
fn position(&self) -> Point2<Self::Scalar> {
self.point.position()
}
}
pub struct FilteredEdge {
pub filtration: f64,
}
impl Default for FilteredEdge {
fn default() -> Self {
Self {
filtration: f64::NAN,
}
}
}
pub struct FilteredFace {
pub filtration: f64,
}
impl Default for FilteredFace {
fn default() -> Self {
Self {
filtration: f64::NAN,
}
}
}
pub type DelaunayFiltration = DelaunayTriangulation<
FilteredPoint2,
(),
FilteredEdge,
FilteredFace,
HierarchyHintGenerator<f64>,
>;
trait AlphaPropogateFaceExt {
fn is_gabriel(&self, edge: FixedUndirectedEdgeHandle, vertex: FixedVertexHandle) -> bool;
fn alpha_propogate_face(&mut self, face: FixedFaceHandle<InnerTag>);
}
fn get_none_edge_vertex(
triangulation: &DelaunayFiltration,
face: FixedFaceHandle<InnerTag>,
edge: FixedUndirectedEdgeHandle,
) -> FixedVertexHandle {
let face_vertices: HashSet<_> = triangulation.face(face).vertices().into_iter().collect();
let edge_vertices: HashSet<_> = triangulation
.undirected_edge(edge)
.vertices()
.into_iter()
.collect();
let mut difference = face_vertices.difference(&edge_vertices);
difference
.next()
.expect("There should be some face vertex that is not on the edge")
.fix()
}
impl AlphaPropogateFaceExt for DelaunayFiltration {
fn is_gabriel(&self, edge: FixedUndirectedEdgeHandle, vertex: FixedVertexHandle) -> bool {
let edge = self.undirected_edge(edge);
let center = edge.center();
let radius_2 = edge.length_2() / 4.0;
let vertex_pos = self.vertex(vertex).position();
let distance_2 = center.distance_2(vertex_pos);
distance_2 >= radius_2
}
fn alpha_propogate_face(&mut self, face: FixedFaceHandle<InnerTag>) {
let face_fil = self.face(face).data().filtration;
let edges = self
.face(face)
.adjacent_edges()
.map(|e| e.as_undirected().fix());
for edge in edges {
let edge_fil = self.undirected_edge(edge).data().filtration;
if !edge_fil.is_nan() {
self.undirected_edge_data_mut(edge).filtration = face_fil.min(edge_fil);
} else {
let vertex = get_none_edge_vertex(self, face, edge);
if !self.is_gabriel(edge, vertex) {
self.undirected_edge_data_mut(edge).filtration = face_fil;
}
}
}
}
}
pub fn alpha_filtration(points: Vec<Point2<f64>>) -> DelaunayFiltration {
let filtered_points = points
.into_iter()
.map(|point| FilteredPoint2 {
filtration: 0.0,
point,
})
.collect();
let mut triangulation = DelaunayFiltration::bulk_load(filtered_points)
.expect("Should be able to build triangulation on point-set");
for face in triangulation.fixed_inner_faces() {
let (_, square_circum_rad) = triangulation.face(face).circumcircle();
triangulation.face_data_mut(face).filtration = square_circum_rad;
triangulation.alpha_propogate_face(face);
}
for edge in triangulation.fixed_undirected_edges() {
let edge_fil = triangulation.undirected_edge(edge).data().filtration;
if edge_fil.is_nan() {
let length_2 = triangulation.undirected_edge(edge).length_2();
let edge_fil = length_2 / 4.0;
triangulation.undirected_edge_data_mut(edge).filtration = edge_fil;
}
}
triangulation
}
pub fn weak_alpha_filtration(points: Vec<Point2<f64>>) -> DelaunayFiltration {
let filtered_points = points
.into_iter()
.map(|point| FilteredPoint2 {
filtration: 0.0,
point,
})
.collect();
let mut triangulation = DelaunayFiltration::bulk_load(filtered_points)
.expect("Should be able to build triangulation on point-set");
for edge in triangulation.fixed_undirected_edges() {
let positions = triangulation.undirected_edge(edge).positions();
let square_dist = positions[0].distance_2(positions[1]);
triangulation.undirected_edge_data_mut(edge).filtration = square_dist;
}
for face in triangulation.fixed_inner_faces() {
let boundary = triangulation.face(face).adjacent_edges();
let max_entry = boundary
.map(|edge| {
let edge = triangulation.undirected_edge(edge.fix().as_undirected());
let entry_time = edge.data().filtration;
entry_time
})
.into_iter()
.max_by(|a, b| {
a.partial_cmp(b)
.expect("No edge should have a NaN filtration time")
})
.expect("There should be a maximum of the boundary entry times");
triangulation.face_data_mut(face).filtration = max_entry;
}
triangulation
}
#[cfg(test)]
mod tests {
use super::*;
pub(crate) fn simple_square() -> Vec<Point2<f64>> {
vec![
Point2::new(0.0, 0.0),
Point2::new(2.0, 0.0),
Point2::new(0.0, 2.0),
Point2::new(2.0, 2.0),
]
}
#[test]
fn weak_alpha_simple_example() {
let points = simple_square();
let filtration = weak_alpha_filtration(points);
let n_edges = filtration.num_undirected_edges();
assert_eq!(n_edges, 5);
let n_faces = filtration.num_inner_faces();
assert_eq!(n_faces, 2);
for face in filtration.fixed_inner_faces() {
let face_time = filtration.face(face).data().filtration;
assert_eq!(face_time, 8.0)
}
}
#[test]
fn alpha_simple_example() {
let points = simple_square();
let filtration = alpha_filtration(points);
let n_edges = filtration.num_undirected_edges();
assert_eq!(n_edges, 5);
let n_faces = filtration.num_inner_faces();
assert_eq!(n_faces, 2);
for face in filtration.fixed_inner_faces() {
let face_time = filtration.face(face).data().filtration;
assert_eq!(face_time, 2.0)
}
}
}