#![allow(dead_code)]
#[derive(Debug, Clone, Default)]
pub struct Curve3D {
pub points: Vec<[f32; 3]>,
}
impl Curve3D {
pub fn new(points: Vec<[f32; 3]>) -> Self {
Self { points }
}
pub fn len(&self) -> usize {
self.points.len()
}
pub fn is_empty(&self) -> bool {
self.points.is_empty()
}
}
#[derive(Debug, Clone, Default)]
pub struct ProjectedCurve {
pub projected_points: Vec<[f32; 3]>,
pub triangle_indices: Vec<u32>,
}
pub fn project_curve_onto_mesh(
curve: &Curve3D,
verts: &[[f32; 3]],
tris: &[[u32; 3]],
) -> ProjectedCurve {
let mut projected_points = Vec::with_capacity(curve.points.len());
let mut triangle_indices = Vec::with_capacity(curve.points.len());
for &cp in &curve.points {
let (proj, tri_idx) = closest_point_on_mesh(cp, verts, tris);
projected_points.push(proj);
triangle_indices.push(tri_idx);
}
ProjectedCurve {
projected_points,
triangle_indices,
}
}
pub fn closest_point_on_mesh(
query: [f32; 3],
verts: &[[f32; 3]],
tris: &[[u32; 3]],
) -> ([f32; 3], u32) {
let mut best_dist = f32::MAX;
let mut best_proj = query;
let mut best_idx = 0u32;
for (i, tri) in tris.iter().enumerate() {
let mut centroid = [0.0f32; 3];
for &idx in tri.iter() {
let vi = idx as usize;
if vi < verts.len() {
for k in 0..3 {
centroid[k] += verts[vi][k];
}
}
}
centroid.iter_mut().for_each(|x| *x /= 3.0);
let dist = dist3(query, centroid);
if dist < best_dist {
best_dist = dist;
best_proj = centroid;
best_idx = i as u32;
}
}
(best_proj, best_idx)
}
fn dist3(a: [f32; 3], b: [f32; 3]) -> f32 {
let dx = a[0] - b[0];
let dy = a[1] - b[1];
let dz = a[2] - b[2];
(dx * dx + dy * dy + dz * dz).sqrt()
}
pub fn curve_arc_length(points: &[[f32; 3]]) -> f32 {
points.windows(2).map(|w| dist3(w[0], w[1])).sum()
}
pub fn resample_curve(points: &[[f32; 3]], n: usize) -> Vec<[f32; 3]> {
if points.len() < 2 || n == 0 {
return points.to_vec();
}
let total = curve_arc_length(points);
if total < 1e-12 {
return vec![points[0]; n];
}
let step = total / (n - 1).max(1) as f32;
let mut result = Vec::with_capacity(n);
result.push(points[0]);
let mut dist_acc = 0.0f32;
let mut seg = 0usize;
let mut t_in_seg = 0.0f32;
for i in 1..n {
let target = i as f32 * step;
while seg + 1 < points.len() {
let seg_len = dist3(points[seg], points[seg + 1]);
if dist_acc + seg_len >= target {
t_in_seg = (target - dist_acc) / seg_len.max(1e-12);
break;
}
dist_acc += seg_len;
seg += 1;
t_in_seg = 0.0;
}
let a = points[seg];
let b = if seg + 1 < points.len() {
points[seg + 1]
} else {
points[seg]
};
result.push([
a[0] + t_in_seg * (b[0] - a[0]),
a[1] + t_in_seg * (b[1] - a[1]),
a[2] + t_in_seg * (b[2] - a[2]),
]);
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_curve3d_len() {
let c = Curve3D::new(vec![[0.0f32; 3], [1.0, 0.0, 0.0]]);
assert_eq!(c.len(), 2 );
}
#[test]
fn test_curve3d_is_empty() {
let c = Curve3D::default();
assert!(c.is_empty() );
}
#[test]
fn test_project_curve_empty_mesh() {
let curve = Curve3D::new(vec![[0.0f32, 0.0, 0.0]]);
let result = project_curve_onto_mesh(&curve, &[], &[]);
assert!(result.projected_points[0].iter().all(|&x| x == 0.0) );
}
#[test]
fn test_project_returns_one_per_point() {
let curve = Curve3D::new(vec![[0.5f32, 0.5, 0.0], [1.5, 0.5, 0.0]]);
let verts = vec![[0.0f32, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]];
let tris = vec![[0u32, 1, 2]];
let result = project_curve_onto_mesh(&curve, &verts, &tris);
assert_eq!(
result.projected_points.len(),
2
);
}
#[test]
fn test_arc_length_zero_for_single_point() {
let pts = vec![[0.0f32; 3]];
assert_eq!(curve_arc_length(&pts), 0.0 );
}
#[test]
fn test_arc_length_straight_line() {
let pts = vec![[0.0f32, 0.0, 0.0], [1.0, 0.0, 0.0], [2.0, 0.0, 0.0]];
let len = curve_arc_length(&pts);
assert!((len - 2.0).abs() < 1e-5 );
}
#[test]
fn test_resample_preserves_start() {
let pts = vec![[0.0f32, 0.0, 0.0], [4.0, 0.0, 0.0]];
let resampled = resample_curve(&pts, 5);
assert!(resampled[0]
.iter()
.zip([0.0f32, 0.0, 0.0].iter())
.all(|(a, b)| (a - b).abs() < 1e-5) );
}
#[test]
fn test_resample_count() {
let pts = vec![[0.0f32, 0.0, 0.0], [1.0, 0.0, 0.0], [2.0, 0.0, 0.0]];
let resampled = resample_curve(&pts, 5);
assert_eq!(resampled.len(), 5 );
}
#[test]
fn test_dist3() {
let d = dist3([0.0, 0.0, 0.0], [3.0, 4.0, 0.0]);
assert!((d - 5.0).abs() < 1e-5 );
}
}