#[cfg(test)]
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
use {
krabmaga::bevy_a5::prelude::GeoCell, krabmaga::engine::fields::field::Field,
krabmaga::engine::fields::grid_option::GridOption,
krabmaga::engine::fields::sparse_a5_grid::SparseA5Grid, std::cell::Cell,
};
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
const RES: i32 = 2;
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
fn london_root_grid() -> (SparseA5Grid<u32>, Vec<GeoCell>) {
let london = GeoCell::from_lon_lat(-0.1, 51.5, 1).expect("london resolves");
let grid: SparseA5Grid<u32> = SparseA5Grid::new_with_root(london, 4);
let cells = grid.all_cells().expect("london has res-4 descendants");
assert!(!cells.is_empty(), "london should have descendants at res 4");
(grid, cells)
}
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
#[test]
fn sparse_a5_grid_bags() {
let (grid, cells) = london_root_grid();
let total = cells.len();
let empty = grid.get_empty_bags();
assert_eq!(empty.len(), total);
let pick = grid.get_random_empty_bag().expect("non-empty universe");
assert!(grid.contains(&pick));
grid.set_object_location(0, &pick);
assert_eq!(grid.get_location_unbuffered(&0).as_ref(), Some(&pick));
assert!(grid.get_location(&0).is_none());
assert!(grid.get_unbuffered(&0).is_some());
assert!(grid.get(&0).is_none());
assert_eq!(grid.num_objects(), 0);
let mut grid = grid;
grid.update();
assert_eq!(grid.get_location(&0).as_ref(), Some(&pick));
assert_eq!(grid.get(&0), Some(0));
assert_eq!(grid.num_objects(), 1);
assert_eq!(grid.num_objects_at_location(&pick), 1);
assert_eq!(grid.get_objects(&pick).map(|v| v.len()), Some(1));
assert_eq!(grid.get_empty_bags().len(), total - 1);
grid.remove_object_location(0, &pick);
grid.lazy_update();
assert!(grid.get_objects(&pick).is_none());
assert_eq!(grid.num_objects(), 0);
assert_eq!(grid.get_empty_bags().len(), total);
}
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
#[test]
fn sparse_a5_grid_remove_object_scans_all_cells() {
let (grid, cells) = london_root_grid();
let a = cells[0];
let b = cells[1 % cells.len()];
grid.set_object_location(7, &a);
if a != b {
grid.set_object_location(8, &b);
}
let mut grid = grid;
grid.update();
assert_eq!(grid.get_location(&7).as_ref(), Some(&a));
grid.remove_object(&7);
grid.lazy_update();
assert!(grid.get_location(&7).is_none());
assert!(grid.get(&7).is_none());
assert_eq!(grid.get_objects(&a), None);
}
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
struct Tagged {
id: u32,
flag: bool,
}
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
#[test]
fn sparse_a5_grid_apply_and_iter() {
let london = GeoCell::from_lon_lat(-0.1, 51.5, 1).expect("london resolves");
let grid: SparseA5Grid<Tagged> = SparseA5Grid::new_with_root(london, 4);
let cells = grid.all_cells().expect("london has res-4 descendants");
let n = cells.len().min(8);
for (i, cell) in cells.iter().take(n).enumerate() {
grid.set_object_location(
Tagged {
id: i as u32,
flag: false,
},
cell,
);
}
let count = Cell::new(0usize);
grid.iter_objects_unbuffered(|loc, t| {
let bag = grid
.get_objects_unbuffered(loc)
.expect("iter handed us a loc that get can't find");
assert!(bag.iter().any(|x| x.id == t.id));
count.set(count.get() + 1);
});
assert_eq!(count.get(), n);
grid.apply_to_all_values(
|_loc, t| {
Some(Tagged {
id: t.id,
flag: true,
})
},
GridOption::WRITE,
);
grid.iter_objects_unbuffered(|_loc, t| assert!(t.flag));
let mut grid = grid;
grid.lazy_update();
let count = Cell::new(0usize);
grid.iter_objects(|loc, t| {
assert!(t.flag);
let bag = grid
.get_objects(loc)
.expect("iter handed us a loc that get can't find");
assert!(bag.iter().any(|x| x.id == t.id));
count.set(count.get() + 1);
});
assert_eq!(count.get(), n);
grid.apply_to_all_values(
|_loc, t| {
Some(Tagged {
id: t.id,
flag: false,
})
},
GridOption::READ,
);
grid.iter_objects(|_loc, t| assert!(!t.flag));
for (i, cell) in cells.iter().take(n).enumerate() {
grid.set_object_location(
Tagged {
id: i as u32,
flag: false,
},
cell,
);
}
grid.apply_to_all_values(
|_loc, t| {
Some(Tagged {
id: t.id,
flag: true,
})
},
GridOption::READWRITE,
);
grid.lazy_update();
grid.iter_objects(|_loc, t| assert!(t.flag));
}
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
#[test]
fn sparse_a5_grid_update_copies_write_into_read() {
let (grid, cells) = london_root_grid();
let cell = cells[0];
grid.set_object_location(11, &cell);
grid.set_object_location(12, &cell);
let mut grid = grid;
grid.update();
let read_bag = grid.get_objects(&cell).expect("read bag populated");
assert_eq!(read_bag.len(), 2);
assert!(
grid.get_objects_unbuffered(&cell).is_none(),
"update() must clear the write buffer"
);
}
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
#[test]
fn sparse_a5_grid_spatial_queries() {
let (grid, cells) = london_root_grid();
let centre = cells[0];
let neighbours = grid
.cell_neighbors(¢re)
.expect("centre has neighbours within london");
assert!(!neighbours.is_empty());
for (i, n) in neighbours.iter().enumerate() {
grid.set_object_location(100 + i as u32, n);
}
grid.set_object_location(0, ¢re);
let mut grid = grid;
grid.update();
let neigh_objs = grid.get_neighbors(¢re);
assert_eq!(neigh_objs.len(), neighbours.len());
let vneighs = grid.cell_vertex_neighbors(¢re);
if let Some(vneighs) = vneighs {
let v_objs = grid.get_vertex_neighbors(¢re);
assert!(v_objs.len() <= vneighs.len());
}
let disk_objs = grid.get_objects_within_disk(¢re, 1);
assert!(disk_objs.contains(&0));
assert_eq!(disk_objs.len(), 1 + neighbours.len());
let none = grid.get_neighbors_within_distance(¢re, 0.0);
assert!(none.is_empty());
let near = grid.get_neighbors_within_distance(¢re, 1_000_000.0);
assert!(near.contains(&0));
}
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
#[test]
fn sparse_a5_grid_lonlat_and_contains() {
let grid: SparseA5Grid<u32> = SparseA5Grid::new(RES);
let cell = grid
.lonlat_to_cell(-0.1, 51.5)
.expect("london resolves at RES");
assert_eq!(cell.resolution(), RES);
assert!(grid.contains(&cell));
let wrong = GeoCell::from_lon_lat(-0.1, 51.5, RES + 1).expect("res+1 cell exists");
assert!(!grid.contains(&wrong));
}
#[cfg(all(
feature = "gis",
not(any(
feature = "visualization",
feature = "visualization_wasm",
feature = "parallel"
))
))]
#[test]
fn sparse_a5_grid_contains_rejects_root_deeper_than_resolution() {
let deep_root = GeoCell::from_lon_lat(-0.1, 51.5, 4).expect("deep root resolves");
let grid: SparseA5Grid<u32> = SparseA5Grid::new_with_root(deep_root, 2);
let test = GeoCell::from_lon_lat(-0.1, 51.5, 2).expect("res 2 cell exists");
assert!(!grid.contains(&test));
}