added tests
This commit is contained in:
parent
f6d1241ecd
commit
5e3441584a
1 changed files with 138 additions and 0 deletions
138
src/lib.rs
138
src/lib.rs
|
|
@ -454,4 +454,142 @@ impl Xenobalanus {
|
||||||
|
|
||||||
clusters
|
clusters
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// --- Point ---
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn point_distance_3_4_5() {
|
||||||
|
let a = Point::new(0.0, 0.0);
|
||||||
|
let b = Point::new(3.0, 4.0);
|
||||||
|
assert!((a.distance(b) - 5.0).abs() < 1e-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn point_bearing_east_is_zero() {
|
||||||
|
let origin = Point::new(0.0, 0.0);
|
||||||
|
let east = Point::new(1.0, 0.0);
|
||||||
|
// delta_y=0, delta_x=1 → atan2(0,1)=0° → bearing=0°
|
||||||
|
assert!((origin.bearing(east) - 0.0).abs() < 1e-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- random_points ---
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn random_points_count_and_bounds() {
|
||||||
|
let mut xb = Xenobalanus::new();
|
||||||
|
xb.random_points((0.0, 0.0), 10.0, 100);
|
||||||
|
let pts = xb.points();
|
||||||
|
assert_eq!(pts.len(), 100);
|
||||||
|
for (x, y) in pts {
|
||||||
|
assert!(x >= -5.0 && x <= 5.0, "x={x} out of [-5, 5]");
|
||||||
|
assert!(y >= -5.0 && y <= 5.0, "y={y} out of [-5, 5]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- delaunay ---
|
||||||
|
|
||||||
|
fn unit_square() -> Vec<Point> {
|
||||||
|
vec![
|
||||||
|
Point::new(0.0, 0.0),
|
||||||
|
Point::new(1.0, 0.0),
|
||||||
|
Point::new(1.0, 1.0),
|
||||||
|
Point::new(0.0, 1.0),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delaunay_four_points_two_triangles() {
|
||||||
|
let mut xb = Xenobalanus::new();
|
||||||
|
xb.set_points(unit_square());
|
||||||
|
xb.delaunay();
|
||||||
|
// 4 convex points → 2 triangles → 6 indices
|
||||||
|
assert_eq!(xb.triangles_flat().len(), 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- preprocess ---
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preprocess_sequential_builds_edges() {
|
||||||
|
let mut xb = Xenobalanus::new();
|
||||||
|
xb.set_points(unit_square());
|
||||||
|
xb.delaunay();
|
||||||
|
xb.preprocess(0, false);
|
||||||
|
// unit square Delaunay: 4 boundary edges + 1 diagonal = 5
|
||||||
|
assert_eq!(xb.edge_lengths().len(), 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preprocess_parallel_matches_sequential() {
|
||||||
|
let mut xb_seq = Xenobalanus::new();
|
||||||
|
xb_seq.set_points(unit_square());
|
||||||
|
xb_seq.delaunay();
|
||||||
|
xb_seq.preprocess(0, false);
|
||||||
|
|
||||||
|
let mut xb_par = Xenobalanus::new();
|
||||||
|
xb_par.set_points(unit_square());
|
||||||
|
xb_par.delaunay();
|
||||||
|
xb_par.preprocess(0, true);
|
||||||
|
|
||||||
|
assert_eq!(xb_seq.edge_lengths().len(), xb_par.edge_lengths().len());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- dtscan ---
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dtscan_finds_cluster_in_grid() {
|
||||||
|
let mut xb = Xenobalanus::new();
|
||||||
|
// 3×3 grid with spacing 1.0; diagonal ≈ 1.414
|
||||||
|
let pts: Vec<Point> = (0..3)
|
||||||
|
.flat_map(|i| (0..3).map(move |j| Point::new(i as f32, j as f32)))
|
||||||
|
.collect();
|
||||||
|
xb.set_points(pts);
|
||||||
|
xb.delaunay();
|
||||||
|
xb.preprocess(0, false);
|
||||||
|
// max_closeness=1.5 covers all edges (max diagonal ≈ 1.414)
|
||||||
|
let clusters = xb.dtscan(2, 1.5);
|
||||||
|
assert!(!clusters.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- delfin ---
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delfin_smoke_test() {
|
||||||
|
let mut xb = Xenobalanus::new();
|
||||||
|
xb.random_points((0.0, 0.0), 100.0, 200);
|
||||||
|
xb.delaunay();
|
||||||
|
xb.preprocess(2, false);
|
||||||
|
// just verify it runs without panic
|
||||||
|
let _voids = xb.delfin(0.0, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- readme example ---
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn readme_example_pipeline() {
|
||||||
|
// Mirrors the workflow shown in the README exactly.
|
||||||
|
let dots: u32 = 10000;
|
||||||
|
let side_length: f32 = 10000.0;
|
||||||
|
let mut xeno = Xenobalanus::new();
|
||||||
|
xeno.random_points((0.0, 0.0), side_length, dots);
|
||||||
|
|
||||||
|
xeno.delaunay();
|
||||||
|
xeno.preprocess(0, false);
|
||||||
|
|
||||||
|
let min_area: f32 = 1000.0;
|
||||||
|
let min_distance: f32 = 200.0;
|
||||||
|
let void_polygons: Vec<HashSet<usize>> = xeno.delfin(min_area, min_distance);
|
||||||
|
|
||||||
|
let min_pts: usize = 5;
|
||||||
|
let max_closeness: f32 = 100.5;
|
||||||
|
let clusters: Vec<Vec<usize>> = xeno.dtscan(min_pts, max_closeness);
|
||||||
|
|
||||||
|
// With 10 000 uniform random points both algorithms should find results.
|
||||||
|
assert!(!void_polygons.is_empty(), "delfin found no voids");
|
||||||
|
assert!(!clusters.is_empty(), "dtscan found no clusters");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue