#![allow(clippy::ptr_arg)]
use super::functions::*;
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Copy)]
pub struct MorseVertex {
pub idx: usize,
pub value: f64,
pub critical_type: MorseCriticalType,
}
pub struct PolygonOffset;
impl PolygonOffset {
pub fn offset_polygon_2d(verts: &[[f64; 2]], dist: f64) -> Vec<[f64; 2]> {
let n = verts.len();
if n < 3 {
return verts.to_vec();
}
let edge_normal = |a: [f64; 2], b: [f64; 2]| -> [f64; 2] {
let dx = b[0] - a[0];
let dy = b[1] - a[1];
let len = (dx * dx + dy * dy).sqrt();
if len < 1e-300 {
[0.0, 1.0]
} else {
[dy / len, -dx / len]
}
};
let mut result = Vec::with_capacity(n);
for i in 0..n {
let prev = if i == 0 { n - 1 } else { i - 1 };
let next = (i + 1) % n;
let n_prev = edge_normal(verts[prev], verts[i]);
let n_next = edge_normal(verts[i], verts[next]);
let bx = n_prev[0] + n_next[0];
let by = n_prev[1] + n_next[1];
let blen = (bx * bx + by * by).sqrt();
let (ox, oy) = if blen < 1e-12 {
(n_next[0] * dist, n_next[1] * dist)
} else {
let dot = n_prev[0] * n_next[0] + n_prev[1] * n_next[1];
let miter_scale = dist / (1.0 + dot).max(0.1).sqrt();
(bx / blen * miter_scale, by / blen * miter_scale)
};
result.push([verts[i][0] + ox, verts[i][1] + oy]);
}
result
}
pub fn signed_area(verts: &[[f64; 2]]) -> f64 {
let n = verts.len();
if n < 3 {
return 0.0;
}
let mut area = 0.0;
for i in 0..n {
let j = (i + 1) % n;
area += verts[i][0] * verts[j][1];
area -= verts[j][0] * verts[i][1];
}
area * 0.5
}
pub fn is_ccw(verts: &[[f64; 2]]) -> bool {
Self::signed_area(verts) > 0.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct HalfEdge {
pub origin: usize,
pub next: usize,
pub prev: usize,
pub twin: usize,
pub face: usize,
}
impl HalfEdge {
pub fn new(origin: usize) -> Self {
Self {
origin,
next: usize::MAX,
prev: usize::MAX,
twin: usize::MAX,
face: usize::MAX,
}
}
pub fn is_boundary(&self) -> bool {
self.twin == usize::MAX
}
}
#[derive(Debug, Clone)]
pub struct HalfEdgeMesh {
pub half_edges: Vec<HalfEdge>,
pub vertices: Vec<[f64; 3]>,
pub vertex_he: Vec<usize>,
pub faces: Vec<Face>,
}
impl HalfEdgeMesh {
pub fn new() -> Self {
Self {
half_edges: Vec::new(),
vertices: Vec::new(),
vertex_he: Vec::new(),
faces: Vec::new(),
}
}
pub fn from_triangles(verts: Vec<[f64; 3]>, faces: &[[usize; 3]]) -> Self {
let nv = verts.len();
let nf = faces.len();
let nhe = nf * 3;
let mut he = vec![
HalfEdge {
origin: 0,
next: usize::MAX,
prev: usize::MAX,
twin: usize::MAX,
face: usize::MAX,
};
nhe
];
let mut face_list = Vec::with_capacity(nf);
let mut vertex_he = vec![usize::MAX; nv];
for (fi, tri) in faces.iter().enumerate() {
let base = fi * 3;
for k in 0..3 {
let he_idx = base + k;
he[he_idx].origin = tri[k];
he[he_idx].next = base + (k + 1) % 3;
he[he_idx].prev = base + (k + 2) % 3;
he[he_idx].face = fi;
if vertex_he[tri[k]] == usize::MAX {
vertex_he[tri[k]] = he_idx;
}
}
face_list.push(Face {
start_he: base,
is_outer: false,
});
}
let mut edge_map: HashMap<(usize, usize), usize> = HashMap::new();
for (i, h) in he.iter().enumerate() {
let dst = he[h.next].origin;
edge_map.insert((h.origin, dst), i);
}
for i in 0..nhe {
if he[i].twin != usize::MAX {
continue;
}
let src = he[i].origin;
let dst = he[he[i].next].origin;
if let Some(&twin_idx) = edge_map.get(&(dst, src)) {
he[i].twin = twin_idx;
he[twin_idx].twin = i;
}
}
Self {
half_edges: he,
vertices: verts,
vertex_he,
faces: face_list,
}
}
pub fn num_vertices(&self) -> usize {
self.vertices.len()
}
pub fn num_faces(&self) -> usize {
self.faces.len()
}
pub fn num_edges(&self) -> usize {
let interior: usize = self
.half_edges
.iter()
.filter(|he| he.twin != usize::MAX && he.twin > self.half_edges.len() / 2)
.count();
let boundary: usize = self
.half_edges
.iter()
.filter(|he| he.twin == usize::MAX)
.count();
interior + boundary
}
pub fn face_half_edges(&self, fi: usize) -> Vec<usize> {
let start = self.faces[fi].start_he;
let mut cur = start;
let mut result = Vec::new();
loop {
result.push(cur);
cur = self.half_edges[cur].next;
if cur == start {
break;
}
if result.len() > 100 {
break;
}
}
result
}
pub fn face_vertices(&self, fi: usize) -> Vec<[f64; 3]> {
self.face_half_edges(fi)
.iter()
.map(|&he_idx| self.vertices[self.half_edges[he_idx].origin])
.collect()
}
pub fn face_normal(&self, fi: usize) -> [f64; 3] {
let verts = self.face_vertices(fi);
if verts.len() < 3 {
return [0.0, 1.0, 0.0];
}
let e1 = sub3(verts[1], verts[0]);
let e2 = sub3(verts[2], verts[0]);
norm3(cross3(e1, e2))
}
pub fn euler_characteristic(&self) -> i64 {
let v = self.num_vertices() as i64;
let e = self.count_unique_edges() as i64;
let f = self.num_faces() as i64;
v - e + f
}
pub fn count_unique_edges(&self) -> usize {
let mut seen: HashSet<(usize, usize)> = HashSet::new();
for he in &self.half_edges {
if he.face == usize::MAX {
continue;
}
let a = he.origin;
let b = self.half_edges[he.next].origin;
let key = if a < b { (a, b) } else { (b, a) };
seen.insert(key);
}
seen.len()
}
pub fn genus(&self) -> i64 {
(2 - self.euler_characteristic()) / 2
}
pub fn boundary_half_edges(&self) -> Vec<usize> {
self.half_edges
.iter()
.enumerate()
.filter(|(_, he)| he.twin == usize::MAX)
.map(|(i, _)| i)
.collect()
}
pub fn is_closed(&self) -> bool {
self.boundary_half_edges().is_empty()
}
pub fn is_manifold(&self) -> bool {
let mut edge_count: HashMap<(usize, usize), usize> = HashMap::new();
for he in &self.half_edges {
if he.face == usize::MAX {
continue;
}
let a = he.origin;
let b = self.half_edges[he.next].origin;
let key = if a < b { (a, b) } else { (b, a) };
*edge_count.entry(key).or_insert(0) += 1;
}
edge_count.values().all(|&c| c <= 2)
}
pub fn vertex_normals(&self) -> Vec<[f64; 3]> {
let nv = self.num_vertices();
let mut normals = vec![[0.0f64; 3]; nv];
let mut counts = vec![0usize; nv];
for fi in 0..self.num_faces() {
let n = self.face_normal(fi);
for he_idx in self.face_half_edges(fi) {
let v = self.half_edges[he_idx].origin;
normals[v] = add3(normals[v], n);
counts[v] += 1;
}
}
for (i, n) in normals.iter_mut().enumerate() {
if counts[i] > 0 {
*n = norm3(*n);
}
}
normals
}
pub fn face_area(&self, fi: usize) -> f64 {
let verts = self.face_vertices(fi);
if verts.len() < 3 {
return 0.0;
}
let e1 = sub3(verts[1], verts[0]);
let e2 = sub3(verts[2], verts[0]);
len3(cross3(e1, e2)) * 0.5
}
pub fn total_area(&self) -> f64 {
(0..self.num_faces()).map(|fi| self.face_area(fi)).sum()
}
pub fn vertex_neighbors(&self, v: usize) -> Vec<usize> {
let mut neighbors = Vec::new();
let start = self.vertex_he[v];
if start == usize::MAX {
return neighbors;
}
let mut cur = start;
loop {
let next_he = self.half_edges[cur].next;
let neighbor = self.half_edges[next_he].origin;
neighbors.push(neighbor);
if self.half_edges[cur].twin == usize::MAX {
break;
}
cur = self.half_edges[self.half_edges[cur].twin].next;
if cur == start {
break;
}
if neighbors.len() > 1000 {
break;
}
}
neighbors
}
}
#[derive(Debug, Clone)]
pub struct WingedEdge {
pub v_start: usize,
pub v_end: usize,
pub face_left: usize,
pub face_right: usize,
pub ccw_start_left: usize,
pub cw_start_right: usize,
pub ccw_end_right: usize,
pub cw_end_left: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MorseCriticalType {
Minimum,
Saddle,
Maximum,
Regular,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct MeshTopology {
pub n_vertices: usize,
pub n_edges: usize,
pub n_faces: usize,
}
impl MeshTopology {
pub fn new(n_vertices: usize, n_edges: usize, n_faces: usize) -> Self {
Self {
n_vertices,
n_edges,
n_faces,
}
}
pub fn euler_characteristic(&self) -> i32 {
self.n_vertices as i32 - self.n_edges as i32 + self.n_faces as i32
}
pub fn genus(&self) -> i32 {
(2 - self.euler_characteristic()) / 2
}
pub fn betti_0_estimate(&self) -> usize {
1
}
pub fn cycle_rank(&self) -> i32 {
self.n_edges as i32 - self.n_vertices as i32 + 1
}
}
pub struct MeshSimplification;
impl MeshSimplification {
pub fn quadric_error_matrix(_vertex: [f64; 3], faces: &[[[f64; 3]; 3]]) -> [[f64; 4]; 4] {
let mut q = [[0.0f64; 4]; 4];
for tri in faces {
let e1 = sub3(tri[1], tri[0]);
let e2 = sub3(tri[2], tri[0]);
let n = norm3(cross3(e1, e2));
let d = -dot3(n, tri[0]);
let p = [n[0], n[1], n[2], d];
for i in 0..4 {
for j in 0..4 {
q[i][j] += p[i] * p[j];
}
}
}
q
}
pub fn edge_collapse_cost(
v1: [f64; 3],
v2: [f64; 3],
q1: [[f64; 4]; 4],
q2: [[f64; 4]; 4],
) -> f64 {
let mut q = [[0.0f64; 4]; 4];
for i in 0..4 {
for j in 0..4 {
q[i][j] = q1[i][j] + q2[i][j];
}
}
let v = [
(v1[0] + v2[0]) * 0.5,
(v1[1] + v2[1]) * 0.5,
(v1[2] + v2[2]) * 0.5,
1.0,
];
let mut cost = 0.0;
for i in 0..4 {
let mut row_sum = 0.0;
for j in 0..4 {
row_sum += q[i][j] * v[j];
}
cost += v[i] * row_sum;
}
cost.abs()
}
pub fn simplify_qem(
positions: &mut Vec<[f64; 3]>,
triangles: &mut Vec<[usize; 3]>,
target_count: usize,
) {
while triangles.len() > target_count {
if triangles.is_empty() || positions.len() < 2 {
break;
}
let mut best_cost = f64::INFINITY;
let mut best_v1 = 0usize;
let mut best_v2 = 1usize;
let nv = positions.len();
let mut vertex_faces: Vec<Vec<usize>> = vec![Vec::new(); nv];
for (fi, tri) in triangles.iter().enumerate() {
for &v in tri.iter() {
if v < nv {
vertex_faces[v].push(fi);
}
}
}
let mut quadrics: Vec<[[f64; 4]; 4]> = vec![[[0.0; 4]; 4]; nv];
for v in 0..nv {
let face_tris: Vec<[[f64; 3]; 3]> = vertex_faces[v]
.iter()
.filter_map(|&fi| {
let t = triangles[fi];
if t[0] < nv && t[1] < nv && t[2] < nv {
Some([positions[t[0]], positions[t[1]], positions[t[2]]])
} else {
None
}
})
.collect();
quadrics[v] = Self::quadric_error_matrix(positions[v], &face_tris);
}
let mut seen_edges: HashSet<(usize, usize)> = HashSet::new();
for tri in triangles.iter() {
for k in 0..3 {
let a = tri[k];
let b = tri[(k + 1) % 3];
if a >= nv || b >= nv {
continue;
}
let key = if a < b { (a, b) } else { (b, a) };
if seen_edges.insert(key) {
let cost = Self::edge_collapse_cost(
positions[a],
positions[b],
quadrics[a],
quadrics[b],
);
if cost < best_cost {
best_cost = cost;
best_v1 = a;
best_v2 = b;
}
}
}
}
if best_cost.is_infinite() {
break;
}
let mid = [
(positions[best_v1][0] + positions[best_v2][0]) * 0.5,
(positions[best_v1][1] + positions[best_v2][1]) * 0.5,
(positions[best_v1][2] + positions[best_v2][2]) * 0.5,
];
positions[best_v1] = mid;
for tri in triangles.iter_mut() {
for v in tri.iter_mut() {
if *v == best_v2 {
*v = best_v1;
}
}
}
triangles.retain(|t| t[0] != t[1] && t[1] != t[2] && t[0] != t[2]);
}
}
}
#[derive(Debug, Clone)]
pub struct CurveSkeleton {
pub nodes: Vec<[f64; 3]>,
pub edges: Vec<(usize, usize)>,
}
impl Default for CurveSkeleton {
fn default() -> Self {
Self::new()
}
}
impl CurveSkeleton {
pub fn new() -> Self {
Self {
nodes: Vec::new(),
edges: Vec::new(),
}
}
pub fn add_node(&mut self, pos: [f64; 3]) -> usize {
let idx = self.nodes.len();
self.nodes.push(pos);
idx
}
pub fn add_edge(&mut self, a: usize, b: usize) {
self.edges.push((a, b));
}
pub fn total_length(&self) -> f64 {
self.edges
.iter()
.map(|&(a, b)| len3(sub3(self.nodes[b], self.nodes[a])))
.sum()
}
pub fn num_nodes(&self) -> usize {
self.nodes.len()
}
pub fn num_edges(&self) -> usize {
self.edges.len()
}
}
#[derive(Debug, Clone)]
pub struct Face {
pub start_he: usize,
pub is_outer: bool,
}
#[derive(Debug, Clone, Default)]
pub struct PersistenceDiagram {
pub pairs: Vec<BirthDeathPair>,
}
impl PersistenceDiagram {
pub fn new() -> Self {
Self { pairs: Vec::new() }
}
pub fn add_pair(&mut self, dim: usize, birth: f64, death: f64) {
self.pairs.push(BirthDeathPair { dim, birth, death });
}
pub fn pairs_for_dim(&self, dim: usize) -> Vec<&BirthDeathPair> {
self.pairs.iter().filter(|p| p.dim == dim).collect()
}
pub fn betti_at(&self, dim: usize, t: f64) -> usize {
self.pairs
.iter()
.filter(|p| p.dim == dim && p.birth <= t && t < p.death)
.count()
}
pub fn max_persistence(&self) -> f64 {
self.pairs
.iter()
.filter(|p| !p.is_essential())
.map(|p| p.persistence())
.fold(0.0_f64, f64::max)
}
pub fn bottleneck_distance(&self, other: &PersistenceDiagram) -> f64 {
let mut dist = 0.0_f64;
for p in &self.pairs {
let closest = other
.pairs
.iter()
.filter(|q| q.dim == p.dim)
.map(|q| (p.birth - q.birth).abs().max((p.death - q.death).abs()))
.fold(f64::MAX, f64::min);
if closest < f64::MAX {
dist = dist.max(closest);
}
}
dist
}
}
#[derive(Debug, Clone)]
pub struct NonManifoldVertex {
pub v: usize,
pub fan_count: usize,
}
#[derive(Debug, Clone)]
pub struct QuadEdgeMesh {
pub edges: Vec<QuadEdge>,
pub vertices: Vec<[f64; 3]>,
}
impl QuadEdgeMesh {
pub fn new() -> Self {
Self {
edges: Vec::new(),
vertices: Vec::new(),
}
}
pub fn make_edge(&mut self, org: usize, dst: usize) -> usize {
let base = self.edges.len() * 4;
let qe = QuadEdge {
next: [base, base + 3, base + 2, base + 1],
data: [org, 0, dst, 0],
};
self.edges.push(qe);
base
}
pub fn add_vertex(&mut self, pos: [f64; 3]) -> usize {
let idx = self.vertices.len();
self.vertices.push(pos);
idx
}
pub fn num_edges(&self) -> usize {
self.edges.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Simplex {
pub vertices: Vec<usize>,
}
impl Simplex {
pub fn new(mut verts: Vec<usize>) -> Self {
verts.sort_unstable();
verts.dedup();
Self { vertices: verts }
}
pub fn dim(&self) -> usize {
self.vertices.len().saturating_sub(1)
}
pub fn boundary_faces(&self) -> Vec<Simplex> {
if self.vertices.len() <= 1 {
return Vec::new();
}
(0..self.vertices.len())
.map(|i| {
let verts: Vec<usize> = self
.vertices
.iter()
.enumerate()
.filter(|(j, _)| *j != i)
.map(|(_, &v)| v)
.collect();
Simplex::new(verts)
})
.collect()
}
pub fn is_face_of(&self, other: &Simplex) -> bool {
self.vertices.iter().all(|v| other.vertices.contains(v))
}
}
#[derive(Debug, Clone)]
pub struct QuadEdge {
pub next: [usize; 4],
pub data: [usize; 4],
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BirthDeathPair {
pub dim: usize,
pub birth: f64,
pub death: f64,
}
impl BirthDeathPair {
pub fn persistence(&self) -> f64 {
if self.death == f64::MAX {
f64::MAX
} else {
self.death - self.birth
}
}
pub fn is_essential(&self) -> bool {
self.death == f64::MAX
}
}
#[derive(Debug, Clone)]
pub struct WingedEdgeMesh {
pub edges: Vec<WingedEdge>,
pub vertices: Vec<[f64; 3]>,
pub face_edge: Vec<usize>,
pub vertex_edge: Vec<usize>,
}
impl WingedEdgeMesh {
pub fn new() -> Self {
Self {
edges: Vec::new(),
vertices: Vec::new(),
face_edge: Vec::new(),
vertex_edge: Vec::new(),
}
}
pub fn add_vertex(&mut self, pos: [f64; 3]) -> usize {
let idx = self.vertices.len();
self.vertices.push(pos);
self.vertex_edge.push(usize::MAX);
idx
}
pub fn add_edge(&mut self, v_start: usize, v_end: usize) -> usize {
let idx = self.edges.len();
self.edges.push(WingedEdge {
v_start,
v_end,
face_left: usize::MAX,
face_right: usize::MAX,
ccw_start_left: usize::MAX,
cw_start_right: usize::MAX,
ccw_end_right: usize::MAX,
cw_end_left: usize::MAX,
});
if self.vertex_edge[v_start] == usize::MAX {
self.vertex_edge[v_start] = idx;
}
if self.vertex_edge[v_end] == usize::MAX {
self.vertex_edge[v_end] = idx;
}
idx
}
pub fn num_edges(&self) -> usize {
self.edges.len()
}
}
#[derive(Debug, Clone)]
pub struct NonManifoldEdge {
pub v0: usize,
pub v1: usize,
pub face_count: usize,
}
#[derive(Debug, Clone)]
pub struct MedialAxisPoint {
pub position: [f64; 3],
pub radius: f64,
pub closest_verts: [usize; 2],
}
pub struct TopologicalSurgery<'a> {
pub mesh: &'a mut HalfEdgeMesh,
}
impl<'a> TopologicalSurgery<'a> {
pub fn new(mesh: &'a mut HalfEdgeMesh) -> Self {
Self { mesh }
}
pub fn attach_handle(&mut self, boundary_a: usize, boundary_b: usize) -> bool {
let boundary_hes = self.mesh.boundary_half_edges();
if boundary_hes.len() < 2 {
return false;
}
let _ = (boundary_a, boundary_b);
true
}
pub fn delete_handle(&mut self) -> bool {
if self.mesh.genus() <= 0 {
return false;
}
true
}
pub fn edge_collapse(&mut self, he_idx: usize) -> bool {
if he_idx >= self.mesh.half_edges.len() {
return false;
}
let v_start = self.mesh.half_edges[he_idx].origin;
let v_end = self.mesh.half_edges[self.mesh.half_edges[he_idx].next].origin;
for he in &mut self.mesh.half_edges {
if he.origin == v_start {
he.origin = v_end;
}
}
let p_start = self.mesh.vertices[v_start];
let p_end = self.mesh.vertices[v_end];
let p_mid = scale3(add3(p_start, p_end), 0.5);
self.mesh.vertices[v_end] = p_mid;
true
}
pub fn edge_split(&mut self, he_idx: usize) -> usize {
if he_idx >= self.mesh.half_edges.len() {
return usize::MAX;
}
let v_start = self.mesh.half_edges[he_idx].origin;
let v_end = self.mesh.half_edges[self.mesh.half_edges[he_idx].next].origin;
let p_start = self.mesh.vertices[v_start];
let p_end = self.mesh.vertices[v_end];
let p_mid = scale3(add3(p_start, p_end), 0.5);
let new_v = self.mesh.vertices.len();
self.mesh.vertices.push(p_mid);
self.mesh.vertex_he.push(he_idx);
new_v
}
}
pub struct IsoCurve;
impl IsoCurve {
pub fn marching_squares(
field: &[Vec<f64>],
nx: usize,
ny: usize,
dx: f64,
dy: f64,
level: f64,
) -> Vec<[[f64; 2]; 2]> {
let mut segments = Vec::new();
if ny < 2 || nx < 2 {
return segments;
}
let lerp = |a: f64, b: f64, va: f64, vb: f64| -> f64 {
if (vb - va).abs() < 1e-300 {
a
} else {
a + (level - va) / (vb - va) * (b - a)
}
};
for iy in 0..ny - 1 {
if iy >= field.len() || iy + 1 >= field.len() {
continue;
}
for ix in 0..nx - 1 {
if ix >= field[iy].len() || ix + 1 >= field[iy].len() {
continue;
}
if ix >= field[iy + 1].len() || ix + 1 >= field[iy + 1].len() {
continue;
}
let v00 = field[iy][ix];
let v10 = field[iy][ix + 1];
let v01 = field[iy + 1][ix];
let v11 = field[iy + 1][ix + 1];
let x0 = ix as f64 * dx;
let x1 = (ix + 1) as f64 * dx;
let y0 = iy as f64 * dy;
let y1 = (iy + 1) as f64 * dy;
let case = ((v00 >= level) as u8)
| (((v10 >= level) as u8) << 1)
| (((v11 >= level) as u8) << 2)
| (((v01 >= level) as u8) << 3);
let e_bottom = [lerp(x0, x1, v00, v10), y0];
let e_right = [x1, lerp(y0, y1, v10, v11)];
let e_top = [lerp(x0, x1, v01, v11), y1];
let e_left = [x0, lerp(y0, y1, v00, v01)];
match case {
0 | 15 => {}
1 | 14 => segments.push([e_bottom, e_left]),
2 | 13 => segments.push([e_bottom, e_right]),
3 | 12 => segments.push([e_left, e_right]),
4 | 11 => segments.push([e_right, e_top]),
5 => {
segments.push([e_bottom, e_right]);
segments.push([e_left, e_top]);
}
6 | 9 => segments.push([e_bottom, e_top]),
7 | 8 => segments.push([e_left, e_top]),
10 => {
segments.push([e_bottom, e_left]);
segments.push([e_right, e_top]);
}
_ => {}
}
}
}
segments
}
}
#[derive(Debug, Clone)]
pub struct SimplicialComplex {
pub simplices: Vec<HashSet<Simplex>>,
pub max_dim: usize,
}
impl SimplicialComplex {
pub fn new(max_dim: usize) -> Self {
Self {
simplices: vec![HashSet::new(); max_dim + 1],
max_dim,
}
}
pub fn add_simplex(&mut self, s: Simplex) {
let d = s.dim();
if d > self.max_dim {
return;
}
for face in s.boundary_faces() {
self.add_simplex(face);
}
self.simplices[d].insert(s);
}
pub fn count(&self, d: usize) -> usize {
if d < self.simplices.len() {
self.simplices[d].len()
} else {
0
}
}
pub fn euler_characteristic(&self) -> i64 {
self.simplices
.iter()
.enumerate()
.map(|(k, s)| {
if k % 2 == 0 {
s.len() as i64
} else {
-(s.len() as i64)
}
})
.sum()
}
pub fn is_valid(&self) -> bool {
for d in 1..=self.max_dim {
for simplex in &self.simplices[d] {
for face in simplex.boundary_faces() {
if !self.simplices[d - 1].contains(&face) {
return false;
}
}
}
}
true
}
}