#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
use crate::fiber::{Fiber, FiberOps, Section, VecSection};
pub trait DiscreteConnection<const D: usize> {
fn n_edges(&self) -> usize;
fn edge_vertices(&self, e: usize) -> [usize; 2];
fn frame_transport(&self, e: usize) -> &[f64];
fn transport_forward<F: Fiber>(&self, e: usize, element: &F::Element) -> F::Element {
F::transport_by(self.frame_transport(e), D, element)
}
fn transport_reverse<F: Fiber>(&self, e: usize, element: &F::Element) -> F::Element {
let r = self.frame_transport(e);
let mut r_t = [0.0_f64; 9]; for i in 0..D {
for j in 0..D {
r_t[i * D + j] = r[j * D + i];
}
}
F::transport_by(&r_t[..D * D], D, element)
}
}
#[derive(Clone, Debug)]
pub struct EdgeTransport2D {
pub edges: Vec<[usize; 2]>,
pub transports: Vec<[f64; 4]>,
}
impl DiscreteConnection<2> for EdgeTransport2D {
fn n_edges(&self) -> usize { self.edges.len() }
fn edge_vertices(&self, e: usize) -> [usize; 2] { self.edges[e] }
fn frame_transport(&self, e: usize) -> &[f64] { &self.transports[e] }
}
#[derive(Clone, Debug)]
pub struct EdgeTransport3D {
pub edges: Vec<[usize; 2]>,
pub transports: Vec<[f64; 9]>,
}
impl DiscreteConnection<3> for EdgeTransport3D {
fn n_edges(&self) -> usize { self.edges.len() }
fn edge_vertices(&self, e: usize) -> [usize; 2] { self.edges[e] }
fn frame_transport(&self, e: usize) -> &[f64] { &self.transports[e] }
}
pub struct CovLaplacian {
cot_weights: Vec<f64>,
dual_areas: Vec<f64>,
edges: Vec<[usize; 2]>,
vertex_edges: Vec<Vec<(usize, bool)>>,
}
impl CovLaplacian {
pub fn new(
n_vertices: usize,
edges: &[[usize; 2]],
cot_weights: &[f64],
dual_areas: &[f64],
) -> Self {
let mut vertex_edges: Vec<Vec<(usize, bool)>> = vec![vec![]; n_vertices];
for (e, &[v0, v1]) in edges.iter().enumerate() {
vertex_edges[v0].push((e, true));
vertex_edges[v1].push((e, false));
}
Self {
cot_weights: cot_weights.to_vec(),
dual_areas: dual_areas.to_vec(),
edges: edges.to_vec(),
vertex_edges,
}
}
pub fn apply<F, const D: usize, C>(
&self,
section: &impl Section<F>,
conn: &C,
) -> VecSection<F>
where
F: FiberOps,
C: DiscreteConnection<D>,
{
let nv = section.n_vertices();
let mut result = VecSection::<F>::zeros(nv);
for v in 0..nv {
let s_v = section.at(v);
for &(e, is_v0) in &self.vertex_edges[v] {
let neighbor = if is_v0 { self.edges[e][1] } else { self.edges[e][0] };
let s_neighbor = section.at(neighbor);
let w = self.cot_weights[e];
let transported = if is_v0 {
conn.transport_reverse::<F>(e, s_neighbor)
} else {
conn.transport_forward::<F>(e, s_neighbor)
};
F::accumulate_diff(result.at_mut(v), s_v, &transported, w);
}
if self.dual_areas[v] > 1e-30 {
F::scale_element(result.at_mut(v), 1.0 / self.dual_areas[v]);
}
}
result
}
}