#[allow(unused_imports)]
use super::functions::*;
#[allow(unused_imports)]
use super::functions_2::*;
#[derive(Debug, Clone, Copy, PartialEq)]
pub(super) enum FmmStatus {
Far,
Trial,
Known,
}
#[derive(Debug, Clone)]
pub struct HeatMethodParams {
pub time_factor: f64,
}
#[derive(Debug, Clone)]
pub struct GeodesicVoronoiCell {
pub site: usize,
pub vertices: Vec<usize>,
pub area: f64,
}
#[derive(Debug, Clone)]
pub struct ExpMapResult {
pub vertex: usize,
pub position: [f64; 3],
pub face: usize,
pub bary: [f64; 3],
}
#[derive(Debug, Clone)]
pub struct LogMapResult {
pub coords: [f64; 2],
pub distance: f64,
pub angle: f64,
}
#[derive(Debug, Clone)]
pub(super) struct DistNode {
pub(super) vertex: usize,
pub(super) dist: f64,
}
#[derive(Debug, Clone)]
pub struct GeodesicMesh {
pub vertices: Vec<[f64; 3]>,
pub faces: Vec<[usize; 3]>,
}
impl GeodesicMesh {
pub fn new(vertices: Vec<[f64; 3]>, faces: Vec<[usize; 3]>) -> Self {
Self { vertices, faces }
}
pub fn num_vertices(&self) -> usize {
self.vertices.len()
}
pub fn num_faces(&self) -> usize {
self.faces.len()
}
pub fn vertex_adjacency(&self) -> Vec<Vec<usize>> {
let n = self.num_vertices();
let mut adj: Vec<Vec<usize>> = vec![Vec::new(); n];
for f in &self.faces {
for i in 0..3 {
let a = f[i];
let b = f[(i + 1) % 3];
if !adj[a].contains(&b) {
adj[a].push(b);
}
if !adj[b].contains(&a) {
adj[b].push(a);
}
}
}
adj
}
pub fn vertex_to_faces(&self) -> Vec<Vec<usize>> {
let n = self.num_vertices();
let mut v2f: Vec<Vec<usize>> = vec![Vec::new(); n];
for (fi, f) in self.faces.iter().enumerate() {
for &vi in f {
v2f[vi].push(fi);
}
}
v2f
}
pub fn face_normal(&self, fi: usize) -> [f64; 3] {
let f = self.faces[fi];
let e1 = sub3(self.vertices[f[1]], self.vertices[f[0]]);
let e2 = sub3(self.vertices[f[2]], self.vertices[f[0]]);
cross3(e1, e2)
}
pub fn vertex_normal(&self, vi: usize) -> [f64; 3] {
let v2f = self.vertex_to_faces();
let mut n = [0.0; 3];
for &fi in &v2f[vi] {
let fn_ = self.face_normal(fi);
n = add3(n, fn_);
}
normalize3(n)
}
pub fn face_area(&self, fi: usize) -> f64 {
0.5 * len3(self.face_normal(fi))
}
pub fn total_area(&self) -> f64 {
(0..self.num_faces()).map(|fi| self.face_area(fi)).sum()
}
pub fn tangent_frame(&self, vi: usize) -> ([f64; 3], [f64; 3], [f64; 3]) {
let n = self.vertex_normal(vi);
let adj = self.vertex_adjacency();
let t = if !adj[vi].is_empty() {
let edge = sub3(self.vertices[adj[vi][0]], self.vertices[vi]);
let proj = sub3(edge, scale3(n, dot3(edge, n)));
normalize3(proj)
} else {
let candidate = if n[0].abs() < 0.9 {
[1.0, 0.0, 0.0]
} else {
[0.0, 1.0, 0.0]
};
let proj = sub3(candidate, scale3(n, dot3(candidate, n)));
normalize3(proj)
};
let b = cross3(n, t);
(t, b, n)
}
pub fn cotangent_weight(&self, i: usize, j: usize) -> f64 {
let mut cot_sum = 0.0;
for f in &self.faces {
let mut found = false;
let mut opposite = 0;
for k in 0..3 {
let a = f[k];
let b = f[(k + 1) % 3];
let c = f[(k + 2) % 3];
if (a == i && b == j) || (a == j && b == i) {
found = true;
opposite = c;
break;
}
}
if found {
let pi = self.vertices[i];
let pj = self.vertices[j];
let po = self.vertices[opposite];
let ei = sub3(pi, po);
let ej = sub3(pj, po);
let cos_a = dot3(ei, ej) / (len3(ei) * len3(ej) + 1e-30);
let sin_a = len3(cross3(ei, ej)) / (len3(ei) * len3(ej) + 1e-30);
if sin_a.abs() > 1e-15 {
cot_sum += cos_a / sin_a;
}
}
}
cot_sum
}
pub fn voronoi_area(&self, vi: usize) -> f64 {
let v2f = self.vertex_to_faces();
let mut area = 0.0;
for &fi in &v2f[vi] {
let f = self.faces[fi];
let local_idx = f.iter().position(|&v| v == vi).expect("element must exist");
let a = self.vertices[f[local_idx]];
let b = self.vertices[f[(local_idx + 1) % 3]];
let c = self.vertices[f[(local_idx + 2) % 3]];
let angle_a = angle_at_vertex(b, a, c);
let angle_b = angle_at_vertex(a, b, c);
let angle_c = angle_at_vertex(a, c, b);
if angle_a > std::f64::consts::FRAC_PI_2 {
area += self.face_area(fi) * 0.5;
} else if angle_b > std::f64::consts::FRAC_PI_2 || angle_c > std::f64::consts::FRAC_PI_2
{
area += self.face_area(fi) * 0.25;
} else {
let ab2 = dot3(sub3(b, a), sub3(b, a));
let ac2 = dot3(sub3(c, a), sub3(c, a));
let cot_b = angle_b.cos() / (angle_b.sin() + 1e-30);
let cot_c = angle_c.cos() / (angle_c.sin() + 1e-30);
area += 0.125 * (cot_b * ac2 + cot_c * ab2);
}
}
area.max(1e-30)
}
}