use crate::error::SpatialResult;
use scirs2_core::ndarray::ArrayView2;
pub fn compute_high_dim_volume(
points: &ArrayView2<'_, f64>,
vertex_indices: &[usize],
equations: &ArrayView2<'_, f64>,
) -> SpatialResult<f64> {
let ndim = points.ncols();
let nfacets = equations.nrows();
if nfacets == 0 {
return Ok(0.0);
}
let mut total_volume = 0.0;
let mut centroid = vec![0.0; ndim];
for &vertex_idx in vertex_indices {
for d in 0..ndim {
centroid[d] += points[[vertex_idx, d]];
}
}
for item in centroid.iter_mut().take(ndim) {
*item /= vertex_indices.len() as f64;
}
for facet_idx in 0..nfacets {
let mut normal = vec![0.0; ndim];
for d in 0..ndim {
normal[d] = equations[[facet_idx, d]];
}
let offset = equations[[facet_idx, ndim]];
let normal_length = (normal.iter().map(|x| x * x).sum::<f64>()).sqrt();
if normal_length < 1e-12 {
continue; }
for item in normal.iter_mut().take(ndim) {
*item /= normal_length;
}
let normalized_offset = offset / normal_length;
let distance_to_centroid: f64 = normal
.iter()
.zip(centroid.iter())
.map(|(n, c)| n * c)
.sum::<f64>()
+ normalized_offset;
let height = distance_to_centroid.abs();
let facet_area = estimate_facet_area(points, vertex_indices, facet_idx, ndim)?;
total_volume += facet_area * height;
}
let volume = total_volume / (ndim as f64);
Ok(volume)
}
pub fn estimate_facet_area(
points: &ArrayView2<'_, f64>,
vertex_indices: &[usize],
_facet_idx: usize,
ndim: usize,
) -> SpatialResult<f64> {
let mut min_coords = vec![f64::INFINITY; ndim];
let mut max_coords = vec![f64::NEG_INFINITY; ndim];
for &vertex_idx in vertex_indices {
for d in 0..ndim {
let coord = points[[vertex_idx, d]];
min_coords[d] = min_coords[d].min(coord);
max_coords[d] = max_coords[d].max(coord);
}
}
let mut size_product = 1.0;
for d in 0..ndim {
let size = max_coords[d] - min_coords[d];
if size > 0.0 {
size_product *= size;
}
}
let estimated_area =
size_product.powf((ndim - 1) as f64 / ndim as f64) / vertex_indices.len() as f64;
Ok(estimated_area)
}
pub fn compute_high_dim_surface_area(
points: &ArrayView2<'_, f64>,
vertex_indices: &[usize],
equations: &ArrayView2<'_, f64>,
) -> SpatialResult<f64> {
let ndim = points.ncols();
let nfacets = equations.nrows();
if nfacets == 0 {
return Ok(0.0);
}
let mut total_surface_area = 0.0;
for facet_idx in 0..nfacets {
let facet_area = estimate_facet_area(points, vertex_indices, facet_idx, ndim)?;
total_surface_area += facet_area;
}
Ok(total_surface_area)
}
pub fn compute_centroid(points: &ArrayView2<'_, f64>, vertex_indices: &[usize]) -> Vec<f64> {
let ndim = points.ncols();
let mut centroid = vec![0.0; ndim];
for &vertex_idx in vertex_indices {
for d in 0..ndim {
centroid[d] += points[[vertex_idx, d]];
}
}
for item in centroid.iter_mut().take(ndim) {
*item /= vertex_indices.len() as f64;
}
centroid
}
pub fn compute_bounding_box(
points: &ArrayView2<'_, f64>,
vertex_indices: &[usize],
) -> (Vec<f64>, Vec<f64>) {
let ndim = points.ncols();
let mut min_coords = vec![f64::INFINITY; ndim];
let mut max_coords = vec![f64::NEG_INFINITY; ndim];
for &vertex_idx in vertex_indices {
for d in 0..ndim {
let coord = points[[vertex_idx, d]];
min_coords[d] = min_coords[d].min(coord);
max_coords[d] = max_coords[d].max(coord);
}
}
(min_coords, max_coords)
}
pub fn compute_characteristic_size(points: &ArrayView2<'_, f64>, vertex_indices: &[usize]) -> f64 {
let (min_coords, max_coords) = compute_bounding_box(points, vertex_indices);
let ndim = min_coords.len();
let mut size_product = 1.0;
let mut valid_dims = 0;
for d in 0..ndim {
let size = max_coords[d] - min_coords[d];
if size > 1e-12 {
size_product *= size;
valid_dims += 1;
}
}
if valid_dims == 0 {
0.0
} else {
size_product.powf(1.0 / valid_dims as f64)
}
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::arr2;
#[test]
fn test_compute_centroid() {
let points = arr2(&[
[0.0, 0.0, 0.0],
[3.0, 0.0, 0.0],
[0.0, 3.0, 0.0],
[0.0, 0.0, 3.0],
]);
let vertices = vec![0, 1, 2, 3];
let centroid = compute_centroid(&points.view(), &vertices);
assert_eq!(centroid, vec![0.75, 0.75, 0.75]);
}
#[test]
fn test_compute_bounding_box() {
let points = arr2(&[[0.0, 5.0, -1.0], [3.0, 0.0, 2.0], [1.0, 3.0, 0.0]]);
let vertices = vec![0, 1, 2];
let (min_coords, max_coords) = compute_bounding_box(&points.view(), &vertices);
assert_eq!(min_coords, vec![0.0, 0.0, -1.0]);
assert_eq!(max_coords, vec![3.0, 5.0, 2.0]);
}
#[test]
fn test_compute_characteristic_size() {
let points = arr2(&[
[0.0, 0.0, 0.0],
[2.0, 0.0, 0.0],
[0.0, 4.0, 0.0],
[0.0, 0.0, 8.0],
]);
let vertices = vec![0, 1, 2, 3];
let size = compute_characteristic_size(&points.view(), &vertices);
assert!(size > 0.0);
assert!((size - 4.0).abs() < 1e-10);
}
#[test]
fn test_estimate_facet_area() {
let points = arr2(&[
[0.0, 0.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]);
let vertices = vec![0, 1, 2, 3, 4];
let area = estimate_facet_area(&points.view(), &vertices, 0, 4).expect("Operation failed");
assert!(area > 0.0);
}
#[test]
fn test_high_dim_surface_area() {
let points = arr2(&[
[0.0, 0.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]);
let vertices = vec![0, 1, 2, 3, 4];
let equations = arr2(&[
[1.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 1.0, 0.0],
[-1.0, -1.0, -1.0, -1.0, 1.0],
]);
let surface_area =
compute_high_dim_surface_area(&points.view(), &vertices, &equations.view())
.expect("Operation failed");
assert!(surface_area >= 0.0);
}
#[test]
fn test_high_dim_volume() {
let points = arr2(&[
[0.0, 0.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 1.0],
]);
let vertices = vec![0, 1, 2, 3, 4];
let equations = arr2(&[[1.0, 0.0, 0.0, 0.0, 0.0], [-1.0, 0.0, 0.0, 0.0, 1.0]]);
let volume = compute_high_dim_volume(&points.view(), &vertices, &equations.view())
.expect("Operation failed");
assert!(volume >= 0.0);
}
}