use nalgebra::DVector;
use cartan_core::Manifold;
use cartan_manifolds::euclidean::Euclidean;
use crate::error::DecError;
use crate::mesh::{FlatMesh, Mesh};
pub struct HodgeStar {
pub star: Vec<DVector<f64>>,
}
impl HodgeStar {
pub fn from_mesh(mesh: &FlatMesh, _manifold: &Euclidean<2>) -> Self {
let nv = mesh.n_vertices();
let ne = mesh.n_boundaries();
let nt = mesh.n_simplices();
let mut s2 = DVector::<f64>::zeros(nt);
for t in 0..nt {
let area = mesh.triangle_area_flat(t).abs();
s2[t] = if area > 1e-30 { 1.0 / area } else { 0.0 };
}
let mut s0 = DVector::<f64>::zeros(nv);
for t in 0..nt {
let area = mesh.triangle_area_flat(t).abs();
for &v in &mesh.simplices[t] {
s0[v] += area / 3.0;
}
}
let mut s1 = DVector::<f64>::zeros(ne);
for e in 0..ne {
let primal_len = mesh.edge_length_flat(e);
if primal_len < 1e-30 {
s1[e] = 0.0;
continue;
}
let cofaces = &mesh.boundary_simplices[e];
let dual_len = match cofaces.len() {
2 => {
let c1 = mesh.circumcenter_flat(cofaces[0]);
let c2 = mesh.circumcenter_flat(cofaces[1]);
(c2 - c1).norm()
}
1 => {
let c = mesh.circumcenter_flat(cofaces[0]);
let mid = mesh.edge_midpoint(e);
(c - mid).norm()
}
_ => 0.0,
};
s1[e] = dual_len / primal_len;
}
Self {
star: vec![s0, s1, s2],
}
}
pub fn from_mesh_generic<M: Manifold, const K: usize, const B: usize>(
mesh: &Mesh<M, K, B>,
manifold: &M,
) -> Result<Self, DecError> {
assert_eq!(K, 3, "from_mesh_generic currently supports K=3 only");
assert_eq!(B, 2, "from_mesh_generic currently supports B=2 only");
let nv = mesh.n_vertices();
let ne = mesh.n_boundaries();
let nt = mesh.n_simplices();
let mut s2 = DVector::<f64>::zeros(nt);
for t in 0..nt {
let area = mesh.simplex_volume(manifold, t);
s2[t] = if area > 1e-30 { 1.0 / area } else { 0.0 };
}
let mut s0 = DVector::<f64>::zeros(nv);
for t in 0..nt {
let area = mesh.simplex_volume(manifold, t);
for &v in &mesh.simplices[t] {
s0[v] += area / 3.0;
}
}
let mut s1 = DVector::<f64>::zeros(ne);
for e in 0..ne {
let primal_len = mesh.boundary_volume(manifold, e);
if primal_len < 1e-30 {
s1[e] = 0.0;
continue;
}
let cofaces = &mesh.boundary_simplices[e];
let dual_len = match cofaces.len() {
2 => {
let c1 = mesh.simplex_circumcenter(manifold, cofaces[0]);
let c2 = mesh.simplex_circumcenter(manifold, cofaces[1]);
manifold.dist(&c1, &c2).unwrap_or(0.0)
}
1 => {
let c = mesh.simplex_circumcenter(manifold, cofaces[0]);
let mid = mesh.boundary_circumcenter(manifold, e);
manifold.dist(&c, &mid).unwrap_or(0.0)
}
_ => 0.0,
};
s1[e] = dual_len / primal_len;
}
Ok(Self {
star: vec![s0, s1, s2],
})
}
pub fn from_mesh_circumcentric<M: Manifold>(
mesh: &Mesh<M, 3, 2>,
manifold: &M,
) -> Result<Self, DecError> {
mesh.check_well_centered(manifold)?;
let nv = mesh.n_vertices();
let ne = mesh.n_boundaries();
let nt = mesh.n_simplices();
let mut s0 = DVector::<f64>::zeros(nv);
for t in 0..nt {
let cc = mesh.simplex_circumcenter(manifold, t);
let simplex = &mesh.simplices[t];
for local in 0..3 {
let v = simplex[local];
let v_prev = simplex[(local + 2) % 3];
let v_next = simplex[(local + 1) % 3];
let mid_prev =
geodesic_midpoint(manifold, &mesh.vertices[v], &mesh.vertices[v_prev]);
let mid_next =
geodesic_midpoint(manifold, &mesh.vertices[v], &mesh.vertices[v_next]);
let area1 = tangent_triangle_area(manifold, &mesh.vertices[v], &mid_prev, &cc);
let area2 = tangent_triangle_area(manifold, &mesh.vertices[v], &cc, &mid_next);
s0[v] += area1 + area2;
}
}
let mut s2 = DVector::<f64>::zeros(nt);
for t in 0..nt {
let area = mesh.simplex_volume(manifold, t);
s2[t] = if area > 1e-30 { 1.0 / area } else { 0.0 };
}
let mut s1 = DVector::<f64>::zeros(ne);
for e in 0..ne {
let primal_len = mesh.boundary_volume(manifold, e);
if primal_len < 1e-30 {
s1[e] = 0.0;
continue;
}
let cofaces = &mesh.boundary_simplices[e];
let dual_len = match cofaces.len() {
2 => {
let c1 = mesh.simplex_circumcenter(manifold, cofaces[0]);
let c2 = mesh.simplex_circumcenter(manifold, cofaces[1]);
manifold.dist(&c1, &c2).unwrap_or(0.0)
}
1 => {
let c = mesh.simplex_circumcenter(manifold, cofaces[0]);
let mid = mesh.boundary_circumcenter(manifold, e);
manifold.dist(&c, &mid).unwrap_or(0.0)
}
_ => 0.0,
};
s1[e] = dual_len / primal_len;
}
Ok(Self {
star: vec![s0, s1, s2],
})
}
pub fn star_k(&self, k: usize) -> &DVector<f64> {
&self.star[k]
}
pub fn star_k_inv(&self, k: usize) -> DVector<f64> {
self.star[k].map(|x| if x.abs() > 1e-30 { 1.0 / x } else { 0.0 })
}
pub fn star0(&self) -> &DVector<f64> {
&self.star[0]
}
pub fn star1(&self) -> &DVector<f64> {
&self.star[1]
}
pub fn star2(&self) -> &DVector<f64> {
&self.star[2]
}
pub fn star0_inv(&self) -> DVector<f64> {
self.star_k_inv(0)
}
pub fn star1_inv(&self) -> DVector<f64> {
self.star_k_inv(1)
}
pub fn star2_inv(&self) -> DVector<f64> {
self.star_k_inv(2)
}
}
fn geodesic_midpoint<M: Manifold>(manifold: &M, p: &M::Point, q: &M::Point) -> M::Point {
let log = manifold
.log(p, q)
.unwrap_or_else(|_| manifold.zero_tangent(p));
manifold.exp(p, &(log * 0.5))
}
fn tangent_triangle_area<M: Manifold>(
manifold: &M,
a: &M::Point,
b: &M::Point,
c: &M::Point,
) -> f64 {
let ab = manifold
.log(a, b)
.unwrap_or_else(|_| manifold.zero_tangent(a));
let ac = manifold
.log(a, c)
.unwrap_or_else(|_| manifold.zero_tangent(a));
let ab2 = manifold.inner(a, &ab, &ab);
let ac2 = manifold.inner(a, &ac, &ac);
let abac = manifold.inner(a, &ab, &ac);
let det = (ab2 * ac2 - abac * abac).max(0.0);
0.5 * det.sqrt()
}