#![allow(dead_code)]
#[allow(dead_code)]
pub struct AlphaShapeConfig {
pub alpha: f32,
}
#[allow(dead_code)]
pub struct AlphaShapeResult {
pub vertices: Vec<[f32; 3]>,
pub edges: Vec<[usize; 2]>,
pub triangles: Vec<[usize; 3]>,
pub alpha: f32,
}
#[inline]
fn sub3(a: [f32; 3], b: [f32; 3]) -> [f32; 3] {
[a[0]-b[0], a[1]-b[1], a[2]-b[2]]
}
#[inline]
fn dot3(a: [f32; 3], b: [f32; 3]) -> f32 {
a[0]*b[0]+a[1]*b[1]+a[2]*b[2]
}
#[inline]
fn len3(v: [f32; 3]) -> f32 {
dot3(v,v).sqrt()
}
#[allow(dead_code)]
pub fn circumradius(p0: [f32; 3], p1: [f32; 3], p2: [f32; 3]) -> f32 {
let a = len3(sub3(p1, p0));
let b = len3(sub3(p2, p1));
let c = len3(sub3(p0, p2));
let s = (a + b + c) / 2.0;
let area2 = s * (s - a) * (s - b) * (s - c);
if area2 <= 0.0 {
return f32::INFINITY;
}
let area = area2.sqrt();
(a * b * c) / (4.0 * area)
}
#[allow(dead_code)]
pub fn triangle_in_alpha_shape(p0: [f32; 3], p1: [f32; 3], p2: [f32; 3], alpha: f32) -> bool {
let r = circumradius(p0, p1, p2);
r < 1.0 / alpha
}
#[allow(dead_code)]
pub fn edge_in_alpha_shape(p0: [f32; 3], p1: [f32; 3], alpha: f32) -> bool {
let half_len = len3(sub3(p1, p0)) * 0.5;
half_len < 1.0 / alpha
}
#[allow(dead_code)]
pub fn alpha_shape_2d(points: &[[f32; 3]], alpha: f32) -> AlphaShapeResult {
let n = points.len();
let mut triangles = Vec::new();
let mut edges = Vec::new();
for i in 0..n {
for j in (i + 1)..n {
for k in (j + 1)..n {
if triangle_in_alpha_shape(points[i], points[j], points[k], alpha) {
triangles.push([i, j, k]);
}
}
}
}
for i in 0..n {
for j in (i + 1)..n {
if edge_in_alpha_shape(points[i], points[j], alpha) {
edges.push([i, j]);
}
}
}
AlphaShapeResult {
vertices: points.to_vec(),
edges,
triangles,
alpha,
}
}
#[allow(dead_code)]
pub fn estimate_alpha(points: &[[f32; 3]]) -> f32 {
let mut max_dist = 0.0f32;
let n = points.len();
for i in 0..n {
for j in (i + 1)..n {
let d = len3(sub3(points[i], points[j]));
if d > max_dist { max_dist = d; }
}
}
if max_dist < 1e-10 { 1.0 } else { 1.0 / max_dist }
}
#[allow(dead_code)]
pub fn alpha_shape_vertex_count(result: &AlphaShapeResult) -> usize {
result.vertices.len()
}
#[allow(dead_code)]
pub fn alpha_shape_triangle_count(result: &AlphaShapeResult) -> usize {
result.triangles.len()
}
#[cfg(test)]
mod tests {
use super::*;
fn square_points() -> Vec<[f32; 3]> {
vec![[0.0,0.0,0.0],[1.0,0.0,0.0],[1.0,1.0,0.0],[0.0,1.0,0.0]]
}
#[test]
fn circumradius_equilateral() {
let r = circumradius([0.0,0.0,0.0],[1.0,0.0,0.0],[0.5,0.866,0.0]);
assert!(r > 0.5 && r < 1.0);
}
#[test]
fn triangle_in_alpha_large_alpha() {
let p0 = [0.0,0.0,0.0]; let p1 = [0.1,0.0,0.0]; let p2 = [0.05,0.1,0.0];
assert!(triangle_in_alpha_shape(p0, p1, p2, 10.0));
}
#[test]
fn triangle_not_in_alpha_small() {
let p0 = [0.0,0.0,0.0]; let p1 = [10.0,0.0,0.0]; let p2 = [5.0,10.0,0.0];
assert!(!triangle_in_alpha_shape(p0, p1, p2, 1.0));
}
#[test]
fn edge_in_alpha_short() {
assert!(edge_in_alpha_shape([0.0,0.0,0.0],[0.1,0.0,0.0], 10.0));
}
#[test]
fn alpha_shape_2d_returns_correct_vertex_count() {
let pts = square_points();
let r = alpha_shape_2d(&pts, 2.0);
assert_eq!(alpha_shape_vertex_count(&r), 4);
}
#[test]
fn estimate_alpha_positive() {
let pts = square_points();
let a = estimate_alpha(&pts);
assert!(a > 0.0);
}
#[test]
fn alpha_shape_triangle_count_nonzero() {
let pts = square_points();
let r = alpha_shape_2d(&pts, 1.0);
assert!(alpha_shape_triangle_count(&r) > 0);
}
#[test]
fn circumradius_degenerate_infinite() {
let r = circumradius([0.0,0.0,0.0],[1.0,0.0,0.0],[2.0,0.0,0.0]);
assert!(r.is_infinite() || r > 1e6);
}
}