use core::marker::PhantomData;
use std::collections::HashMap;
use nalgebra::{SVector, Vector2};
use cartan_core::{Manifold, Real};
use cartan_manifolds::euclidean::Euclidean;
use crate::error::DecError;
#[derive(Debug, Clone)]
pub struct Mesh<M: Manifold, const K: usize = 3, const B: usize = 2> {
pub vertices: Vec<M::Point>,
pub simplices: Vec<[usize; K]>,
pub boundaries: Vec<[usize; B]>,
pub simplex_boundary_ids: Vec<[usize; K]>,
pub boundary_signs: Vec<[f64; K]>,
pub vertex_boundaries: Vec<Vec<usize>>,
pub vertex_simplices: Vec<Vec<usize>>,
pub boundary_simplices: Vec<Vec<usize>>,
_phantom: PhantomData<M>,
}
pub type FlatMesh = Mesh<Euclidean<2>, 3, 2>;
impl<M: Manifold, const K: usize, const B: usize> Mesh<M, K, B> {
pub fn n_vertices(&self) -> usize {
self.vertices.len()
}
pub fn n_boundaries(&self) -> usize {
self.boundaries.len()
}
pub fn n_simplices(&self) -> usize {
self.simplices.len()
}
pub fn euler_characteristic(&self) -> i32 {
self.n_vertices() as i32 - self.n_boundaries() as i32 + self.n_simplices() as i32
}
pub fn from_simplices_generic(
_manifold: &M,
vertices: Vec<M::Point>,
simplices: Vec<[usize; K]>,
) -> Self {
assert_eq!(B, K - 1, "B must equal K-1");
let n_v = vertices.len();
let mut boundary_map: HashMap<[usize; B], usize> = HashMap::new();
let mut boundaries: Vec<[usize; B]> = Vec::new();
let mut simplex_boundary_ids = Vec::with_capacity(simplices.len());
let mut boundary_signs_vec = Vec::with_capacity(simplices.len());
for simplex in &simplices {
for &v in simplex {
assert!(v < n_v, "simplex vertex index {v} out of bounds (n_v={n_v})");
}
let mut local_boundary_ids = [0usize; K];
let mut local_signs = [0.0f64; K];
for omit in 0..K {
let sign = if omit % 2 == 0 { 1.0 } else { -1.0 };
let mut face = [0usize; B];
let mut idx = 0;
for (pos, &v) in simplex.iter().enumerate() {
if pos != omit {
face[idx] = v;
idx += 1;
}
}
let mut sorted_face = face;
sorted_face.sort();
let parity = permutation_sign(&face, &sorted_face);
let effective_sign = sign * parity;
let boundary_idx = *boundary_map.entry(sorted_face).or_insert_with(|| {
let b = boundaries.len();
boundaries.push(sorted_face);
b
});
local_boundary_ids[omit] = boundary_idx;
local_signs[omit] = effective_sign;
}
simplex_boundary_ids.push(local_boundary_ids);
boundary_signs_vec.push(local_signs);
}
let mut mesh = Self {
vertices,
simplices,
boundaries,
simplex_boundary_ids,
boundary_signs: boundary_signs_vec,
vertex_boundaries: Vec::new(),
vertex_simplices: Vec::new(),
boundary_simplices: Vec::new(),
_phantom: PhantomData,
};
mesh.rebuild_adjacency();
mesh
}
pub fn simplex_volume(&self, manifold: &M, s: usize) -> f64 {
let simplex = &self.simplices[s];
let v0 = &self.vertices[simplex[0]];
let n = K - 1;
let mut logs: Vec<M::Tangent> = Vec::with_capacity(n);
for &vi_idx in &simplex[1..] {
let vi = &self.vertices[vi_idx];
let u = manifold
.log(v0, vi)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
logs.push(u);
}
let mut gram = vec![0.0f64; n * n];
for i in 0..n {
for j in 0..n {
gram[i * n + j] = manifold.inner(v0, &logs[i], &logs[j]);
}
}
let det = dense_determinant(&gram, n);
let factorial = (1..K).product::<usize>() as f64;
det.abs().sqrt() / factorial
}
pub fn boundary_volume(&self, manifold: &M, b: usize) -> f64 {
let boundary = &self.boundaries[b];
let v0 = &self.vertices[boundary[0]];
let n = B - 1;
if n == 0 {
return 1.0;
}
let mut logs: Vec<M::Tangent> = Vec::with_capacity(n);
for &vi_idx in &boundary[1..] {
let vi = &self.vertices[vi_idx];
let u = manifold
.log(v0, vi)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
logs.push(u);
}
let mut gram = vec![0.0f64; n * n];
for i in 0..n {
for j in 0..n {
gram[i * n + j] = manifold.inner(v0, &logs[i], &logs[j]);
}
}
let det = dense_determinant(&gram, n);
let factorial = (1..B).product::<usize>().max(1) as f64;
det.abs().sqrt() / factorial
}
pub fn simplex_circumcenter(&self, manifold: &M, s: usize) -> M::Point {
let simplex = &self.simplices[s];
let v0 = &self.vertices[simplex[0]];
let n = K - 1;
let mut logs: Vec<M::Tangent> = Vec::with_capacity(n);
for &vi_idx in &simplex[1..] {
let vi = &self.vertices[vi_idx];
let u = manifold
.log(v0, vi)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
logs.push(u);
}
let mut gram = vec![0.0f64; n * n];
for i in 0..n {
for j in 0..n {
gram[i * n + j] = manifold.inner(v0, &logs[i], &logs[j]);
}
}
let mut rhs = vec![0.0f64; n];
for i in 0..n {
rhs[i] = 0.5 * gram[i * n + i];
}
let t = match dense_solve(&gram, &rhs, n) {
Some(sol) => sol,
None => {
let mut tangent = manifold.zero_tangent(v0);
for log in &logs {
tangent = tangent + log.clone() * (1.0 / K as f64);
}
return manifold.exp(v0, &tangent);
}
};
let mut tangent = manifold.zero_tangent(v0);
for (i, ti) in t.iter().enumerate() {
tangent = tangent + logs[i].clone() * *ti;
}
manifold.exp(v0, &tangent)
}
pub fn boundary_circumcenter(&self, manifold: &M, b: usize) -> M::Point {
let boundary = &self.boundaries[b];
let v0 = &self.vertices[boundary[0]];
let n = B - 1;
if n == 0 {
return v0.clone();
}
if n == 1 {
let v1 = &self.vertices[boundary[1]];
let half_log = manifold
.log(v0, v1)
.map(|u| u * 0.5)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
return manifold.exp(v0, &half_log);
}
let mut logs: Vec<M::Tangent> = Vec::with_capacity(n);
for &vi_idx in &boundary[1..] {
let vi = &self.vertices[vi_idx];
let u = manifold
.log(v0, vi)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
logs.push(u);
}
let mut gram = vec![0.0f64; n * n];
for i in 0..n {
for j in 0..n {
gram[i * n + j] = manifold.inner(v0, &logs[i], &logs[j]);
}
}
let mut rhs = vec![0.0f64; n];
for i in 0..n {
rhs[i] = 0.5 * gram[i * n + i];
}
let t_coeff = match dense_solve(&gram, &rhs, n) {
Some(sol) => sol,
None => {
let mut tangent = manifold.zero_tangent(v0);
for log in &logs {
tangent = tangent + log.clone() * (1.0 / B as f64);
}
return manifold.exp(v0, &tangent);
}
};
let mut tangent = manifold.zero_tangent(v0);
for (i, ti) in t_coeff.iter().enumerate() {
tangent = tangent + logs[i].clone() * *ti;
}
manifold.exp(v0, &tangent)
}
pub fn rebuild_topology(&mut self)
where
M: Manifold,
{
let vertices = std::mem::take(&mut self.vertices);
let simplices = std::mem::take(&mut self.simplices);
let mut edge_map: HashMap<[usize; B], usize> = HashMap::new();
let mut boundaries: Vec<[usize; B]> = Vec::new();
let mut simplex_boundary_ids = Vec::with_capacity(simplices.len());
let mut boundary_signs_vec = Vec::with_capacity(simplices.len());
for simplex in &simplices {
let mut local_boundary_ids = [0usize; K];
let mut local_signs = [0.0f64; K];
for omit in 0..K {
let sign = if omit % 2 == 0 { 1.0 } else { -1.0 };
let mut face = [0usize; B];
let mut idx = 0;
for (pos, &v) in simplex.iter().enumerate() {
if pos != omit {
face[idx] = v;
idx += 1;
}
}
let mut sorted_face = face;
sorted_face.sort();
let parity = permutation_sign(&face, &sorted_face);
let effective_sign = sign * parity;
let boundary_idx = *edge_map.entry(sorted_face).or_insert_with(|| {
let b = boundaries.len();
boundaries.push(sorted_face);
b
});
local_boundary_ids[omit] = boundary_idx;
local_signs[omit] = effective_sign;
}
simplex_boundary_ids.push(local_boundary_ids);
boundary_signs_vec.push(local_signs);
}
self.vertices = vertices;
self.simplices = simplices;
self.boundaries = boundaries;
self.simplex_boundary_ids = simplex_boundary_ids;
self.boundary_signs = boundary_signs_vec;
self.rebuild_adjacency();
}
pub fn rebuild_adjacency(&mut self) {
let nv = self.vertices.len();
let nb = self.boundaries.len();
let mut vb: Vec<Vec<usize>> = vec![Vec::new(); nv];
for (b, boundary) in self.boundaries.iter().enumerate() {
for &v in boundary {
vb[v].push(b);
}
}
let mut vs: Vec<Vec<usize>> = vec![Vec::new(); nv];
for (s, simplex) in self.simplices.iter().enumerate() {
for &v in simplex {
vs[v].push(s);
}
}
let mut bs: Vec<Vec<usize>> = vec![Vec::new(); nb];
for (s, sbi) in self.simplex_boundary_ids.iter().enumerate() {
for &b in sbi {
bs[b].push(s);
}
}
self.vertex_boundaries = vb;
self.vertex_simplices = vs;
self.boundary_simplices = bs;
}
}
impl<M: Manifold> Mesh<M, 3, 2> {
pub fn from_simplices(
_manifold: &M,
vertices: Vec<M::Point>,
triangles: Vec<[usize; 3]>,
) -> Self {
let n_v = vertices.len();
let mut edge_map: HashMap<(usize, usize), usize> = HashMap::new();
let mut edges: Vec<[usize; 2]> = Vec::new();
let mut simplex_boundary_ids = Vec::with_capacity(triangles.len());
let mut boundary_signs = Vec::with_capacity(triangles.len());
for &[i, j, k] in &triangles {
assert!(
i < n_v && j < n_v && k < n_v,
"triangle index out of bounds"
);
let raw_edges = [(i, j), (j, k), (i, k)];
let boundary_coeffs = [1.0f64, 1.0, -1.0];
let mut local_edge_ids = [0usize; 3];
let mut local_signs = [0.0f64; 3];
for (slot, (&(a, b), &coeff)) in
raw_edges.iter().zip(boundary_coeffs.iter()).enumerate()
{
let (lo, hi) = if a < b { (a, b) } else { (b, a) };
let direction_sign = if a < b { 1.0 } else { -1.0 };
let idx = *edge_map.entry((lo, hi)).or_insert_with(|| {
let e = edges.len();
edges.push([lo, hi]);
e
});
local_edge_ids[slot] = idx;
local_signs[slot] = coeff * direction_sign;
}
simplex_boundary_ids.push(local_edge_ids);
boundary_signs.push(local_signs);
}
let mut mesh = Self {
vertices,
simplices: triangles,
boundaries: edges,
simplex_boundary_ids,
boundary_signs,
vertex_boundaries: Vec::new(),
vertex_simplices: Vec::new(),
boundary_simplices: Vec::new(),
_phantom: PhantomData,
};
mesh.rebuild_adjacency();
mesh
}
pub fn edge_faces(&self, e: usize) -> (usize, Option<usize>) {
let cofaces = &self.boundary_simplices[e];
match cofaces.len() {
1 => (cofaces[0], None),
2 => (cofaces[0], Some(cofaces[1])),
n => panic!(
"non-manifold edge {e}: has {n} co-faces (expected 1 or 2)"
),
}
}
pub fn edge_length(&self, manifold: &M, e: usize) -> Real {
let [i, j] = self.boundaries[e];
manifold
.dist(&self.vertices[i], &self.vertices[j])
.unwrap_or(0.0)
}
pub fn triangle_area(&self, manifold: &M, t: usize) -> Real {
let [i, j, k] = self.simplices[t];
let v0 = &self.vertices[i];
let v1 = &self.vertices[j];
let v2 = &self.vertices[k];
let u = manifold
.log(v0, v1)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
let v = manifold
.log(v0, v2)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
let uu = manifold.inner(v0, &u, &u);
let vv = manifold.inner(v0, &v, &v);
let uv = manifold.inner(v0, &u, &v);
0.5 * (uu * vv - uv * uv).abs().sqrt()
}
pub fn circumcenter(&self, manifold: &M, t: usize) -> M::Point {
let [i, j, k] = self.simplices[t];
let v0 = &self.vertices[i];
let v1 = &self.vertices[j];
let v2 = &self.vertices[k];
let u = manifold
.log(v0, v1)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
let v = manifold
.log(v0, v2)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
let uu = manifold.inner(v0, &u, &u);
let vv = manifold.inner(v0, &v, &v);
let uv = manifold.inner(v0, &u, &v);
let det = uu * vv - uv * uv;
if det.abs() < 1e-30 {
return v0.clone();
}
let s = (vv * 0.5 * uu - uv * 0.5 * vv) / det;
let tc = (uu * 0.5 * vv - uv * 0.5 * uu) / det;
let tangent = u * s + v * tc;
manifold.exp(v0, &tangent)
}
pub fn boundary_midpoint(&self, manifold: &M, e: usize) -> M::Point {
let [i, j] = self.boundaries[e];
let v0 = &self.vertices[i];
let v1 = &self.vertices[j];
let half_log = manifold
.log(v0, v1)
.map(|u| u * 0.5)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
manifold.exp(v0, &half_log)
}
pub fn check_well_centered(&self, manifold: &M) -> Result<(), DecError> {
for t in 0..self.n_simplices() {
let [i, j, k] = self.simplices[t];
let v0 = &self.vertices[i];
let v1 = &self.vertices[j];
let v2 = &self.vertices[k];
let u = manifold
.log(v0, v1)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
let v = manifold
.log(v0, v2)
.unwrap_or_else(|_| manifold.zero_tangent(v0));
let uu = manifold.inner(v0, &u, &u);
let vv = manifold.inner(v0, &v, &v);
let uv = manifold.inner(v0, &u, &v);
let det = uu * vv - uv * uv;
if det.abs() < 1e-30 {
continue;
}
let s = (vv * 0.5 * uu - uv * 0.5 * vv) / det;
let tc = (uu * 0.5 * vv - uv * 0.5 * uu) / det;
if s <= 0.0 || tc <= 0.0 || s + tc >= 1.0 {
return Err(DecError::NotWellCentered {
simplex: t,
volume: s.min(tc).min(1.0 - s - tc),
});
}
}
Ok(())
}
}
impl Mesh<Euclidean<2>, 3, 2> {
pub fn from_triangles(vertices: Vec<[f64; 2]>, triangles: Vec<[usize; 3]>) -> Self {
let manifold = Euclidean::<2>;
let mv: Vec<SVector<f64, 2>> = vertices
.iter()
.map(|&[x, y]| SVector::from([x, y]))
.collect();
Self::from_simplices(&manifold, mv, triangles)
}
pub fn vertex(&self, i: usize) -> Vector2<f64> {
Vector2::new(self.vertices[i].x, self.vertices[i].y)
}
pub fn edge_midpoint(&self, e: usize) -> Vector2<f64> {
let [i, j] = self.boundaries[e];
(self.vertex(i) + self.vertex(j)) * 0.5
}
pub fn edge_length_flat(&self, e: usize) -> f64 {
let [i, j] = self.boundaries[e];
(self.vertex(j) - self.vertex(i)).norm()
}
pub fn triangle_area_flat(&self, t: usize) -> f64 {
let [i, j, k] = self.simplices[t];
let a = self.vertex(i);
let b = self.vertex(j);
let c = self.vertex(k);
let ab = b - a;
let ac = c - a;
0.5 * (ab.x * ac.y - ab.y * ac.x)
}
pub fn circumcenter_flat(&self, t: usize) -> Vector2<f64> {
let [i, j, k] = self.simplices[t];
let a = self.vertex(i);
let b = self.vertex(j);
let c = self.vertex(k);
let ab = b - a;
let ac = c - a;
let d = 2.0 * (ab.x * ac.y - ab.y * ac.x);
if d.abs() < 1e-30 {
return (a + b + c) / 3.0;
}
let ux = (ac.y * (ab.x * ab.x + ab.y * ab.y) - ab.y * (ac.x * ac.x + ac.y * ac.y)) / d;
let uy = (ab.x * (ac.x * ac.x + ac.y * ac.y) - ac.x * (ab.x * ab.x + ab.y * ab.y)) / d;
a + Vector2::new(ux, uy)
}
pub fn unit_square_grid(n: usize) -> Self {
assert!(n >= 1, "grid must have at least 1 division");
let mut vertices = Vec::new();
let mut triangles = Vec::new();
for j in 0..=n {
for i in 0..=n {
vertices.push([i as f64 / n as f64, j as f64 / n as f64]);
}
}
let idx = |i: usize, j: usize| j * (n + 1) + i;
for j in 0..n {
for i in 0..n {
let v00 = idx(i, j);
let v10 = idx(i + 1, j);
let v01 = idx(i, j + 1);
let v11 = idx(i + 1, j + 1);
triangles.push([v00, v10, v01]);
triangles.push([v10, v11, v01]);
}
}
Self::from_triangles(vertices, triangles)
}
#[deprecated(since = "0.1.1", note = "use n_boundaries() instead")]
pub fn n_edges(&self) -> usize {
self.n_boundaries()
}
#[deprecated(since = "0.1.1", note = "use n_simplices() instead")]
pub fn n_triangles(&self) -> usize {
self.n_simplices()
}
}
fn dense_determinant(a: &[f64], n: usize) -> f64 {
match n {
0 => 1.0,
1 => a[0],
2 => a[0] * a[3] - a[1] * a[2],
3 => {
a[0] * (a[4] * a[8] - a[5] * a[7])
- a[1] * (a[3] * a[8] - a[5] * a[6])
+ a[2] * (a[3] * a[7] - a[4] * a[6])
}
_ => {
let mut lu: Vec<f64> = a.to_vec();
let mut sign = 1.0f64;
for col in 0..n {
let mut max_val = lu[col * n + col].abs();
let mut max_row = col;
for row in (col + 1)..n {
let v = lu[row * n + col].abs();
if v > max_val {
max_val = v;
max_row = row;
}
}
if max_val < 1e-30 {
return 0.0;
}
if max_row != col {
for k in 0..n {
lu.swap(col * n + k, max_row * n + k);
}
sign = -sign;
}
let pivot = lu[col * n + col];
for row in (col + 1)..n {
let factor = lu[row * n + col] / pivot;
lu[row * n + col] = factor;
for k in (col + 1)..n {
lu[row * n + k] -= factor * lu[col * n + k];
}
}
}
let mut det = sign;
for i in 0..n {
det *= lu[i * n + i];
}
det
}
}
}
fn dense_solve(a: &[f64], b: &[f64], n: usize) -> Option<Vec<f64>> {
let mut aug = vec![0.0f64; n * (n + 1)];
for i in 0..n {
for j in 0..n {
aug[i * (n + 1) + j] = a[i * n + j];
}
aug[i * (n + 1) + n] = b[i];
}
for col in 0..n {
let mut max_val = aug[col * (n + 1) + col].abs();
let mut max_row = col;
for row in (col + 1)..n {
let v = aug[row * (n + 1) + col].abs();
if v > max_val {
max_val = v;
max_row = row;
}
}
if max_val < 1e-30 {
return None;
}
if max_row != col {
for k in 0..(n + 1) {
aug.swap(col * (n + 1) + k, max_row * (n + 1) + k);
}
}
let pivot = aug[col * (n + 1) + col];
for row in (col + 1)..n {
let factor = aug[row * (n + 1) + col] / pivot;
for k in col..(n + 1) {
aug[row * (n + 1) + k] -= factor * aug[col * (n + 1) + k];
}
}
}
let mut x = vec![0.0f64; n];
for i in (0..n).rev() {
let mut sum = aug[i * (n + 1) + n];
for j in (i + 1)..n {
sum -= aug[i * (n + 1) + j] * x[j];
}
x[i] = sum / aug[i * (n + 1) + i];
}
Some(x)
}
fn permutation_sign<const N: usize>(from: &[usize; N], to: &[usize; N]) -> f64 {
let mut perm = [0usize; N];
for (i, &val) in from.iter().enumerate() {
for (j, &tval) in to.iter().enumerate() {
if val == tval {
perm[i] = j;
break;
}
}
}
let mut inversions = 0usize;
for i in 0..N {
for j in (i + 1)..N {
if perm[i] > perm[j] {
inversions += 1;
}
}
}
if inversions % 2 == 0 { 1.0 } else { -1.0 }
}