pub mod algorithms;
pub mod core;
pub mod geometry;
pub mod properties;
pub use core::{ConvexHull, ConvexHullAlgorithm};
pub use algorithms::{
compute_graham_scan, compute_jarvis_march, compute_quickhull, get_algorithm_complexity,
recommend_algorithm,
};
pub use geometry::{
compute_polygon_area, compute_polygon_perimeter, compute_polyhedron_volume, cross_product_2d,
cross_product_3d, tetrahedron_volume, triangle_area_3d,
};
pub use properties::{
analyze_hull, check_point_containment, compute_surface_area, compute_volume,
get_hull_statistics,
};
#[allow(dead_code)]
pub fn convex_hull(
points: &scirs2_core::ndarray::ArrayView2<'_, f64>,
) -> crate::error::SpatialResult<scirs2_core::ndarray::Array2<f64>> {
let hull = ConvexHull::new(points)?;
Ok(hull.vertices_array())
}
#[allow(dead_code)]
pub fn convex_hull_with_algorithm(
points: &scirs2_core::ndarray::ArrayView2<'_, f64>,
algorithm: ConvexHullAlgorithm,
) -> crate::error::SpatialResult<scirs2_core::ndarray::Array2<f64>> {
let hull = ConvexHull::new_with_algorithm(points, algorithm)?;
Ok(hull.vertices_array())
}
pub mod advanced {
use super::*;
#[derive(Debug, Clone)]
pub struct HullComputationMetrics {
pub computation_time: Option<std::time::Duration>,
pub memory_usage: Option<usize>,
pub algorithm_steps: Option<usize>,
pub used_special_case: bool,
pub result_reliable: bool,
}
pub fn compare_algorithms(
points: &scirs2_core::ndarray::ArrayView2<'_, f64>,
) -> crate::error::SpatialResult<
Vec<(ConvexHullAlgorithm, crate::error::SpatialResult<ConvexHull>)>,
> {
let mut results = Vec::new();
results.push((
ConvexHullAlgorithm::Quickhull,
ConvexHull::new_with_algorithm(points, ConvexHullAlgorithm::Quickhull),
));
if points.ncols() == 2 && points.nrows() >= 3 {
results.push((
ConvexHullAlgorithm::GrahamScan,
ConvexHull::new_with_algorithm(points, ConvexHullAlgorithm::GrahamScan),
));
results.push((
ConvexHullAlgorithm::JarvisMarch,
ConvexHull::new_with_algorithm(points, ConvexHullAlgorithm::JarvisMarch),
));
}
Ok(results)
}
pub fn validate_hull(
hull: &ConvexHull,
original_points: &scirs2_core::ndarray::ArrayView2<'_, f64>,
) -> crate::error::SpatialResult<Vec<String>> {
let mut issues = Vec::new();
for i in 0..original_points.nrows() {
let point = original_points.row(i);
let point_slice = point.as_slice().expect("Operation failed");
let is_vertex = hull.vertex_indices().iter().any(|&idx| {
let vertex = hull.points.row(idx);
vertex
.as_slice()
.expect("Operation failed")
.iter()
.zip(point_slice.iter())
.all(|(a, b)| (a - b).abs() < 1e-10)
});
if !is_vertex {
match hull.contains(point_slice) {
Ok(inside) => {
if !inside {
issues.push(format!("Point {} is outside its own hull", i));
}
}
Err(e) => {
issues.push(format!("Failed to test containment for point {}: {}", i, e));
}
}
}
}
if let Ok(volume) = hull.volume() {
if volume < 0.0 {
issues.push("Hull volume is negative".to_string());
}
}
if let Ok(area) = hull.area() {
if area < 0.0 {
issues.push("Hull surface area is negative".to_string());
}
}
for &idx in hull.vertex_indices() {
if idx >= hull.points.nrows() {
issues.push(format!("Invalid vertex index: {}", idx));
}
}
Ok(issues)
}
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::arr2;
#[test]
fn test_convex_hull_function() {
let points = arr2(&[
[0.0, 0.0],
[1.0, 0.0],
[0.0, 1.0],
[0.5, 0.5], ]);
let hull_vertices = convex_hull(&points.view()).expect("Operation failed");
assert!(hull_vertices.nrows() >= 3); assert_eq!(hull_vertices.ncols(), 2);
}
#[test]
fn test_convex_hull_with_algorithm_function() {
let points = arr2(&[
[0.0, 0.0],
[1.0, 0.0],
[0.0, 1.0],
[0.5, 0.5], ]);
let hull_vertices =
convex_hull_with_algorithm(&points.view(), ConvexHullAlgorithm::GrahamScan)
.expect("Operation failed");
assert_eq!(hull_vertices.nrows(), 3); assert_eq!(hull_vertices.ncols(), 2);
let hull_vertices =
convex_hull_with_algorithm(&points.view(), ConvexHullAlgorithm::JarvisMarch)
.expect("Operation failed");
assert_eq!(hull_vertices.nrows(), 3); assert_eq!(hull_vertices.ncols(), 2);
}
#[test]
fn test_backward_compatibility() {
let points = arr2(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]);
let hull = ConvexHull::new(&points.view()).expect("Operation failed");
let vertices = hull.vertices();
let vertex_indices = hull.vertex_indices();
let simplices = hull.simplices();
assert_eq!(hull.ndim(), 2);
assert_eq!(vertices.len(), vertex_indices.len());
assert!(!simplices.is_empty());
let volume = hull.volume().expect("Operation failed");
let area = hull.area().expect("Operation failed");
assert!((volume - 1.0).abs() < 1e-10); assert!((area - 4.0).abs() < 1e-10);
assert!(hull.contains([0.5, 0.5]).expect("Operation failed")); assert!(!hull.contains([2.0, 2.0]).expect("Operation failed")); }
#[test]
fn test_error_cases() {
let too_few = arr2(&[[0.0, 0.0], [1.0, 0.0]]);
let result = ConvexHull::new(&too_few.view());
assert!(result.is_err());
let points = arr2(&[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]]);
let hull = ConvexHull::new(&points.view()).expect("Operation failed");
let result = hull.contains([0.5, 0.5, 0.5]);
assert!(result.is_err());
}
#[test]
fn test_3d_hull() {
let points = arr2(&[
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
[0.5, 0.5, 0.5], ]);
let hull = ConvexHull::new(&points.view()).expect("Operation failed");
assert_eq!(hull.ndim(), 3);
assert!(hull.vertex_indices().len() >= 4);
assert!(hull.contains([0.25, 0.25, 0.25]).expect("Operation failed"));
assert!(!hull.contains([2.0, 2.0, 2.0]).expect("Operation failed"));
let volume = hull.volume().expect("Operation failed");
let surface_area = hull.area().expect("Operation failed");
assert!(volume > 0.0);
assert!(surface_area > 0.0);
}
#[test]
fn test_analysis_functions() {
let points = arr2(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]);
let hull = ConvexHull::new(&points.view()).expect("Operation failed");
let analysis = analyze_hull(&hull).expect("Operation failed");
assert_eq!(analysis.ndim, 2);
assert_eq!(analysis.num_vertices, 4);
assert!((analysis.volume - 1.0).abs() < 1e-10);
assert!((analysis.surface_area - 4.0).abs() < 1e-10);
let stats = get_hull_statistics(&hull).expect("Operation failed");
assert_eq!(stats.num_input_points, 4);
assert_eq!(stats.num_hull_vertices, 4);
assert_eq!(stats.hull_vertex_fraction, 1.0); }
}