use cartan_core::Manifold;
use cartan_dec::Mesh;
pub fn length_cross_ratio<M: Manifold>(mesh: &Mesh<M, 3, 2>, manifold: &M, edge: usize) -> f64 {
assert!(edge < mesh.n_boundaries(), "edge index out of bounds");
let adjacent = &mesh.boundary_simplices[edge];
if adjacent.len() < 2 {
return 1.0;
}
let [vi, vj] = mesh.boundaries[edge];
let tri_0 = mesh.simplices[adjacent[0]];
let vk = tri_0
.iter()
.copied()
.find(|&v| v != vi && v != vj)
.expect("triangle must have an opposite vertex");
let tri_1 = mesh.simplices[adjacent[1]];
let vl = tri_1
.iter()
.copied()
.find(|&v| v != vi && v != vj)
.expect("triangle must have an opposite vertex");
let pi = &mesh.vertices[vi];
let pj = &mesh.vertices[vj];
let pk = &mesh.vertices[vk];
let pl = &mesh.vertices[vl];
let dist_il = manifold.dist(pi, pl).unwrap_or(0.0);
let dist_jk = manifold.dist(pj, pk).unwrap_or(0.0);
let dist_ki = manifold.dist(pk, pi).unwrap_or(0.0);
let dist_lj = manifold.dist(pl, pj).unwrap_or(0.0);
let denom = dist_ki * dist_lj;
if denom < 1e-30 {
return 1.0;
}
(dist_il * dist_jk) / denom
}
pub fn capture_reference_lcrs<M: Manifold>(mesh: &Mesh<M, 3, 2>, manifold: &M) -> Vec<f64> {
(0..mesh.n_boundaries())
.map(|e| length_cross_ratio(mesh, manifold, e))
.collect()
}
pub fn lcr_spring_energy<M: Manifold>(
mesh: &Mesh<M, 3, 2>,
manifold: &M,
ref_lcrs: &[f64],
kst: f64,
) -> f64 {
assert_eq!(
ref_lcrs.len(),
mesh.n_boundaries(),
"ref_lcrs length must match edge count"
);
let mut energy = 0.0;
for (e, &ref_val) in ref_lcrs.iter().enumerate() {
if ref_val.abs() < 1e-30 {
continue;
}
let lcr_e = length_cross_ratio(mesh, manifold, e);
let diff = lcr_e - ref_val;
energy += (diff * diff) / (ref_val * ref_val);
}
0.5 * kst * energy
}
pub fn lcr_spring_gradient<M: Manifold>(
mesh: &Mesh<M, 3, 2>,
manifold: &M,
ref_lcrs: &[f64],
_kst: f64,
) -> Vec<M::Tangent> {
assert_eq!(
ref_lcrs.len(),
mesh.n_boundaries(),
"ref_lcrs length must match edge count"
);
(0..mesh.n_vertices())
.map(|v| manifold.zero_tangent(&mesh.vertices[v]))
.collect()
}