#![allow(dead_code)]
#[allow(dead_code)]
pub fn convex_hull_2d(points: &[[f32; 2]]) -> Vec<[f32; 2]> {
let n = points.len();
if n < 2 {
return points.to_vec();
}
if n == 2 {
return points.to_vec();
}
let pivot_idx = points
.iter()
.enumerate()
.min_by(|(_, a), (_, b)| {
a[1].partial_cmp(&b[1])
.unwrap_or(std::cmp::Ordering::Equal)
.then(a[0].partial_cmp(&b[0]).unwrap_or(std::cmp::Ordering::Equal))
})
.map(|(i, _)| i)
.unwrap_or(0);
let mut pts: Vec<[f32; 2]> = points.to_vec();
pts.swap(0, pivot_idx);
let p0 = pts[0];
pts[1..].sort_by(|a, b| {
let ax = a[0] - p0[0];
let ay = a[1] - p0[1];
let bx = b[0] - p0[0];
let by = b[1] - p0[1];
let cross = ax * by - ay * bx;
if cross.abs() < 1e-10 {
let da = ax * ax + ay * ay;
let db = bx * bx + by * by;
da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
} else if cross > 0.0 {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
});
let mut hull: Vec<[f32; 2]> = Vec::with_capacity(n);
for &p in &pts {
while hull.len() >= 2 {
let n2 = hull.len();
let o = hull[n2 - 2];
let a = hull[n2 - 1];
let cross = (a[0] - o[0]) * (p[1] - o[1]) - (a[1] - o[1]) * (p[0] - o[0]);
if cross <= 0.0 {
hull.pop();
} else {
break;
}
}
hull.push(p);
}
hull
}
#[allow(dead_code)]
pub fn hull_area(hull: &[[f32; 2]]) -> f32 {
let n = hull.len();
if n < 3 {
return 0.0;
}
let mut area = 0.0f32;
for i in 0..n {
let j = (i + 1) % n;
area += hull[i][0] * hull[j][1];
area -= hull[j][0] * hull[i][1];
}
(area * 0.5).abs()
}
#[allow(dead_code)]
pub fn hull_perimeter(hull: &[[f32; 2]]) -> f32 {
let n = hull.len();
if n < 2 {
return 0.0;
}
let mut peri = 0.0f32;
for i in 0..n {
let j = (i + 1) % n;
let dx = hull[j][0] - hull[i][0];
let dy = hull[j][1] - hull[i][1];
peri += (dx * dx + dy * dy).sqrt();
}
peri
}
#[allow(dead_code)]
pub fn hull_contains_point(hull: &[[f32; 2]], p: [f32; 2]) -> bool {
let n = hull.len();
if n < 3 {
return false;
}
for i in 0..n {
let a = hull[i];
let b = hull[(i + 1) % n];
let cross = (b[0] - a[0]) * (p[1] - a[1]) - (b[1] - a[1]) * (p[0] - a[0]);
if cross < 0.0 {
return false;
}
}
true
}
#[allow(dead_code)]
pub fn hull_centroid(hull: &[[f32; 2]]) -> Option<[f32; 2]> {
let n = hull.len();
if n == 0 {
return None;
}
let sum = hull
.iter()
.fold([0.0f32; 2], |acc, v| [acc[0] + v[0], acc[1] + v[1]]);
Some([sum[0] / n as f32, sum[1] / n as f32])
}
#[allow(dead_code)]
pub fn hull_diameter(hull: &[[f32; 2]]) -> f32 {
let n = hull.len();
let mut max_dist = 0.0f32;
for i in 0..n {
for j in (i + 1)..n {
let dx = hull[j][0] - hull[i][0];
let dy = hull[j][1] - hull[i][1];
let d = dx * dx + dy * dy;
if d > max_dist {
max_dist = d;
}
}
}
max_dist.sqrt()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_input() {
let hull = convex_hull_2d(&[]);
assert!(hull.is_empty());
}
#[test]
fn test_single_point() {
let hull = convex_hull_2d(&[[1.0, 2.0]]);
assert_eq!(hull.len(), 1);
}
#[test]
fn test_square_hull() {
let pts = vec![
[0.0f32, 0.0],
[1.0, 0.0],
[1.0, 1.0],
[0.0, 1.0],
[0.5, 0.5], ];
let hull = convex_hull_2d(&pts);
assert_eq!(hull.len(), 4);
let area = hull_area(&hull);
assert!((area - 1.0).abs() < 0.01);
}
#[test]
fn test_hull_area_triangle() {
let hull = vec![[0.0f32, 0.0], [4.0, 0.0], [0.0, 3.0]];
let area = hull_area(&hull);
assert!((area - 6.0).abs() < 0.01);
}
#[test]
fn test_hull_perimeter() {
let hull = vec![[0.0f32, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]];
let peri = hull_perimeter(&hull);
assert!((peri - 4.0).abs() < 0.01);
}
#[test]
fn test_hull_contains_point_inside() {
let hull = vec![[0.0f32, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]];
assert!(hull_contains_point(&hull, [0.5, 0.5]));
}
#[test]
fn test_hull_contains_point_outside() {
let hull = vec![[0.0f32, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]];
assert!(!hull_contains_point(&hull, [2.0, 0.5]));
}
#[test]
fn test_hull_centroid() {
let hull = vec![[0.0f32, 0.0], [2.0, 0.0], [2.0, 2.0], [0.0, 2.0]];
let c = hull_centroid(&hull).expect("should succeed");
assert!((c[0] - 1.0).abs() < 0.01);
assert!((c[1] - 1.0).abs() < 0.01);
}
#[test]
fn test_hull_diameter() {
let hull = vec![[0.0f32, 0.0], [3.0, 0.0], [3.0, 4.0], [0.0, 4.0]];
let d = hull_diameter(&hull);
assert!(d > 4.9 && d < 5.1);
}
#[test]
fn test_collinear_points() {
let pts = vec![[0.0f32, 0.0], [1.0, 0.0], [2.0, 0.0]];
let hull = convex_hull_2d(&pts);
assert!(!hull.is_empty());
}
}