#![allow(dead_code)]
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub struct EdgeFlow {
pub edges: Vec<[u32; 2]>,
pub directions: Vec<[f32; 3]>,
pub magnitudes: Vec<f32>,
}
#[allow(dead_code)]
pub fn compute_edge_flow(positions: &[[f32; 3]], edges: &[[u32; 2]]) -> EdgeFlow {
let mut directions = Vec::with_capacity(edges.len());
let mut magnitudes = Vec::with_capacity(edges.len());
for e in edges {
let a = positions[e[0] as usize];
let b = positions[e[1] as usize];
let d = [b[0] - a[0], b[1] - a[1], b[2] - a[2]];
let mag = (d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt();
if mag > 1e-12 {
directions.push([d[0] / mag, d[1] / mag, d[2] / mag]);
} else {
directions.push([0.0, 0.0, 0.0]);
}
magnitudes.push(mag);
}
EdgeFlow {
edges: edges.to_vec(),
directions,
magnitudes,
}
}
#[allow(dead_code)]
pub fn flow_direction(flow: &EdgeFlow, index: usize) -> [f32; 3] {
if index < flow.directions.len() {
flow.directions[index]
} else {
[0.0, 0.0, 0.0]
}
}
#[allow(dead_code)]
pub fn flow_magnitude_ef(flow: &EdgeFlow, index: usize) -> f32 {
if index < flow.magnitudes.len() {
flow.magnitudes[index]
} else {
0.0
}
}
#[allow(dead_code)]
pub fn flow_vertex_field(positions: &[[f32; 3]], flow: &EdgeFlow) -> Vec<[f32; 3]> {
let mut field = vec![[0.0_f32; 3]; positions.len()];
let mut counts = vec![0u32; positions.len()];
for (i, e) in flow.edges.iter().enumerate() {
let d = flow.directions[i];
for &vi in &[e[0] as usize, e[1] as usize] {
field[vi][0] += d[0];
field[vi][1] += d[1];
field[vi][2] += d[2];
counts[vi] += 1;
}
}
for (i, c) in counts.iter().enumerate() {
if *c > 0 {
let inv = 1.0 / *c as f32;
field[i][0] *= inv;
field[i][1] *= inv;
field[i][2] *= inv;
}
}
field
}
#[allow(dead_code)]
pub fn smooth_edge_flow(flow: &EdgeFlow, iterations: u32) -> EdgeFlow {
if iterations == 0 || flow.edges.is_empty() {
return flow.clone();
}
let max_v = flow
.edges
.iter()
.flat_map(|e| [e[0], e[1]])
.max()
.map(|m| m as usize + 1)
.unwrap_or(0);
let mut v_to_edges: Vec<Vec<usize>> = vec![Vec::new(); max_v];
for (ei, e) in flow.edges.iter().enumerate() {
let a = e[0] as usize;
let b = e[1] as usize;
if a < max_v {
v_to_edges[a].push(ei);
}
if b < max_v {
v_to_edges[b].push(ei);
}
}
let ne = flow.edges.len();
let mut edge_neighbors: Vec<Vec<usize>> = Vec::with_capacity(ne);
for (ei, e) in flow.edges.iter().enumerate() {
let mut nbrs: Vec<usize> = Vec::new();
for &vi in &[e[0] as usize, e[1] as usize] {
if vi < max_v {
for &nei in &v_to_edges[vi] {
if nei != ei && !nbrs.contains(&nei) {
nbrs.push(nei);
}
}
}
}
edge_neighbors.push(nbrs);
}
let mut directions = flow.directions.clone();
let mut magnitudes = flow.magnitudes.clone();
const LAMBDA: f32 = 0.5;
for _ in 0..iterations {
let prev_dirs = directions.clone();
let prev_mags = magnitudes.clone();
for ei in 0..ne {
let nbrs = &edge_neighbors[ei];
if nbrs.is_empty() {
continue;
}
let count = nbrs.len() as f32;
let mut avg_dir = [0.0f32; 3];
let mut avg_mag = 0.0f32;
for &ni in nbrs {
avg_dir[0] += prev_dirs[ni][0];
avg_dir[1] += prev_dirs[ni][1];
avg_dir[2] += prev_dirs[ni][2];
avg_mag += prev_mags[ni];
}
avg_dir[0] /= count;
avg_dir[1] /= count;
avg_dir[2] /= count;
avg_mag /= count;
let new_dir = [
prev_dirs[ei][0] * (1.0 - LAMBDA) + avg_dir[0] * LAMBDA,
prev_dirs[ei][1] * (1.0 - LAMBDA) + avg_dir[1] * LAMBDA,
prev_dirs[ei][2] * (1.0 - LAMBDA) + avg_dir[2] * LAMBDA,
];
let len = (new_dir[0] * new_dir[0]
+ new_dir[1] * new_dir[1]
+ new_dir[2] * new_dir[2])
.sqrt();
directions[ei] = if len > 1e-12 {
[new_dir[0] / len, new_dir[1] / len, new_dir[2] / len]
} else {
new_dir
};
magnitudes[ei] = prev_mags[ei] * (1.0 - LAMBDA) + avg_mag * LAMBDA;
}
}
EdgeFlow {
edges: flow.edges.clone(),
directions,
magnitudes,
}
}
#[allow(dead_code)]
pub fn flow_to_json(flow: &EdgeFlow) -> String {
format!(
"{{\"edge_count\":{},\"avg_magnitude\":{:.4}}}",
flow.edges.len(),
if flow.magnitudes.is_empty() {
0.0
} else {
flow.magnitudes.iter().sum::<f32>() / flow.magnitudes.len() as f32
}
)
}
#[allow(dead_code)]
pub fn flow_divergence(flow: &EdgeFlow, vertex_count: usize) -> Vec<f32> {
let mut div = vec![0.0_f32; vertex_count];
for (i, e) in flow.edges.iter().enumerate() {
let m = flow.magnitudes[i];
let a = e[0] as usize;
let b = e[1] as usize;
if a < vertex_count {
div[a] += m;
}
if b < vertex_count {
div[b] -= m;
}
}
div
}
#[allow(dead_code)]
pub fn flow_curl(flow: &EdgeFlow, vertex_count: usize) -> Vec<f32> {
let mut curl = vec![0.0_f32; vertex_count];
for (i, e) in flow.edges.iter().enumerate() {
let d = flow.directions[i];
let cross_mag = (d[0] * d[0] + d[1] * d[1]).sqrt();
let a = e[0] as usize;
if a < vertex_count {
curl[a] += cross_mag;
}
}
curl
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compute_edge_flow() {
let pos = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]];
let edges = vec![[0, 1]];
let ef = compute_edge_flow(&pos, &edges);
assert_eq!(ef.directions.len(), 1);
assert!((ef.magnitudes[0] - 1.0).abs() < 1e-6);
}
#[test]
fn test_flow_direction() {
let ef = EdgeFlow {
edges: vec![[0, 1]],
directions: vec![[1.0, 0.0, 0.0]],
magnitudes: vec![1.0],
};
assert_eq!(flow_direction(&ef, 0), [1.0, 0.0, 0.0]);
assert_eq!(flow_direction(&ef, 5), [0.0, 0.0, 0.0]);
}
#[test]
fn test_flow_magnitude() {
let ef = EdgeFlow {
edges: vec![[0, 1]],
directions: vec![[1.0, 0.0, 0.0]],
magnitudes: vec![2.5],
};
assert!((flow_magnitude_ef(&ef, 0) - 2.5).abs() < 1e-6);
}
#[test]
fn test_flow_vertex_field() {
let pos = vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0]];
let ef = compute_edge_flow(&pos, &[[0, 1]]);
let field = flow_vertex_field(&pos, &ef);
assert_eq!(field.len(), 2);
}
#[test]
fn test_smooth_edge_flow() {
let ef = EdgeFlow {
edges: vec![],
directions: vec![],
magnitudes: vec![],
};
let s = smooth_edge_flow(&ef, 1);
assert_eq!(s.edges.len(), 0);
}
#[test]
fn test_flow_to_json() {
let ef = EdgeFlow {
edges: vec![],
directions: vec![],
magnitudes: vec![],
};
let j = flow_to_json(&ef);
assert!(j.contains("edge_count"));
}
#[test]
fn test_flow_divergence() {
let ef = EdgeFlow {
edges: vec![[0, 1]],
directions: vec![[1.0, 0.0, 0.0]],
magnitudes: vec![1.0],
};
let d = flow_divergence(&ef, 2);
assert!((d[0] - 1.0).abs() < 1e-6);
assert!((d[1] + 1.0).abs() < 1e-6);
}
#[test]
fn test_flow_curl() {
let ef = EdgeFlow {
edges: vec![[0, 1]],
directions: vec![[1.0, 0.0, 0.0]],
magnitudes: vec![1.0],
};
let c = flow_curl(&ef, 2);
assert_eq!(c.len(), 2);
}
#[test]
fn test_compute_edge_flow_zero_length() {
let pos = vec![[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]];
let ef = compute_edge_flow(&pos, &[[0, 1]]);
assert_eq!(ef.directions[0], [0.0, 0.0, 0.0]);
}
#[test]
fn test_flow_magnitude_out_of_bounds() {
let ef = EdgeFlow {
edges: vec![],
directions: vec![],
magnitudes: vec![],
};
assert!((flow_magnitude_ef(&ef, 0)).abs() < 1e-6);
}
}