use scirs2_core::ndarray::array;
use scirs2_spatial::spherical_voronoi::SphericalVoronoi;
use std::f64::consts::PI;
#[allow(dead_code)]
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Spherical Voronoi Diagram Example");
println!("=================================\n");
let points = array![
[0.0, 0.0, 1.0],
[0.0, 0.0, -1.0],
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, -1.0, 0.0],
[-1.0, 0.0, 0.0]
];
println!("Points on the sphere (octahedron vertices):");
for (i, row) in points.rows().into_iter().enumerate() {
println!(
" Point {}: [{:.2}, {:.2}, {:.2}]",
i, row[0], row[1], row[2]
);
}
println!();
let radius = 1.0;
let center = array![0.0, 0.0, 0.0];
println!("Creating spherical Voronoi diagram with:");
println!(" Radius: {radius}");
println!(" Center: [{}, {}, {}]", center[0], center[1], center[2]);
println!();
let mut sv = SphericalVoronoi::new(&points.view(), radius, Some(¢er), None)?;
sv.sort_vertices_of_regions()?;
println!("Spherical Voronoi diagram information:");
println!(" Number of regions: {}", sv.regions().len());
println!(" Number of vertices: {}", sv.vertices().nrows());
println!(" Number of Delaunay simplices: {}", sv.simplices().len());
println!();
println!("Voronoi vertices on the sphere:");
for (i, row) in sv.vertices().rows().into_iter().enumerate() {
println!(
" Vertex {}: [{:.6}, {:.6}, {:.6}]",
i, row[0], row[1], row[2]
);
}
println!();
println!("Voronoi regions (point index -> vertex indices):");
for (i, region) in sv.regions().iter().enumerate() {
println!(" Region for point {i}: {region:?}");
}
println!();
let num_regions = sv.regions().len();
println!("Calculating region areas...");
let areas = sv.areas()?;
let total_area = 4.0 * PI * radius * radius;
let expected_area_per_region = total_area / (num_regions as f64);
println!("Region areas on the unit sphere:");
for (i, &area) in areas.iter().enumerate() {
println!(" Region {i}: {area:.6} (expected: {expected_area_per_region:.6})");
}
println!(
"\nTotal area of all regions: {:.6}",
areas.iter().sum::<f64>()
);
println!("Expected total area (4πr²): {total_area:.6}");
println!();
println!("Geodesic distance calculations:");
let north_pole = array![0.0, 0.0, 1.0];
let south_pole = array![0.0, 0.0, -1.0];
let dist = sv.geodesic_distance(&north_pole.view(), &south_pole.view())?;
println!(" North pole to South pole: {dist:.6} (expected: π = {PI:.6})");
let equator_point = array![1.0, 0.0, 0.0];
let dist = sv.geodesic_distance(&north_pole.view(), &equator_point.view())?;
println!(
" North pole to Equator: {:.6} (expected: π/2 = {:.6})",
dist,
PI / 2.0
);
let query_point = array![0.5, 0.5, std::f64::consts::FRAC_1_SQRT_2]; let (nearest_idx, dist) = sv.nearest_generator(&query_point.view())?;
println!("\nNearest generator to [0.5, 0.5, std::f64::consts::FRAC_1_SQRT_2]:");
println!(" Generator index: {nearest_idx}");
println!(" Distance: {dist:.6}");
let distances = sv.geodesic_distances_to_generators(&query_point.view())?;
println!("\nDistances to all generators:");
for (i, &dist) in distances.iter().enumerate() {
println!(" Distance to generator {i}: {dist:.6}");
}
println!();
println!("Creating a more complex example with a dodecahedron-based point set...");
let dodeca_points = generate_dodecahedron_points();
let mut complex_sv = SphericalVoronoi::new(&dodeca_points.view(), radius, Some(¢er), None)?;
complex_sv.sort_vertices_of_regions()?;
println!("Dodecahedron Voronoi diagram information:");
println!(" Number of regions: {}", complex_sv.regions().len());
println!(" Number of vertices: {}", complex_sv.vertices().nrows());
println!(
" Number of Delaunay simplices: {}",
complex_sv.simplices().len()
);
let complex_areas = complex_sv.areas()?;
let total_complex_area: f64 = complex_areas.iter().sum();
println!(" Total area of all regions: {total_complex_area:.6}");
println!(" Expected total area (4πr²): {total_area:.6}");
println!("\nVoronoi diagram calculation completed successfully!");
Ok(())
}
#[allow(dead_code)]
fn generate_dodecahedron_points() -> scirs2_core::ndarray::Array2<f64> {
use scirs2_core::ndarray::Array2;
let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
let vertices = vec![
[1.0, 1.0, 1.0],
[1.0, 1.0, -1.0],
[1.0, -1.0, 1.0],
[1.0, -1.0, -1.0],
[-1.0, 1.0, 1.0],
[-1.0, 1.0, -1.0],
[-1.0, -1.0, 1.0],
[-1.0, -1.0, -1.0],
[0.0, 1.0 / phi, phi],
[0.0, 1.0 / phi, -phi],
[0.0, -1.0 / phi, phi],
[0.0, -1.0 / phi, -phi],
[1.0 / phi, phi, 0.0],
[1.0 / phi, -phi, 0.0],
[-1.0 / phi, phi, 0.0],
[-1.0 / phi, -phi, 0.0],
[phi, 0.0, 1.0 / phi],
[phi, 0.0, -1.0 / phi],
[-phi, 0.0, 1.0 / phi],
[-phi, 0.0, -1.0 / phi],
];
let mut points = Array2::zeros((vertices.len(), 3));
for (i, vertex) in vertices.iter().enumerate() {
let norm = (vertex[0].powi(2) + vertex[1].powi(2) + vertex[2].powi(2)).sqrt();
points[[i, 0]] = vertex[0] / norm;
points[[i, 1]] = vertex[1] / norm;
points[[i, 2]] = vertex[2] / norm;
}
points
}
#[allow(dead_code)]
fn generate_random_points_on_sphere(
n: usize,
radius: f64,
center: &scirs2_core::ndarray::Array1<f64>,
) -> scirs2_core::ndarray::Array2<f64> {
use scirs2_core::ndarray::Array2;
use scirs2_core::random::{Rng, RngExt};
let mut rng = scirs2_core::random::rng();
let mut points = Array2::zeros((n, 3));
for i in 0..n {
let mut valid = false;
let mut x = 0.0;
let mut y = 0.0;
let mut z = 0.0;
while !valid {
x = rng.random_range(-1.0..1.0);
y = rng.random_range(-1.0..1.0);
z = rng.random_range(-1.0..1.0);
let d2: f64 = x * x + y * y + z * z;
if d2 > 0.0 && d2 <= 1.0 {
let scale = radius / d2.sqrt();
x *= scale;
y *= scale;
z *= scale;
valid = true;
}
}
points[[i, 0]] = x + center[0];
points[[i, 1]] = y + center[1];
points[[i, 2]] = z + center[2];
}
points
}