use std::cell::RefCell;
use std::env;
use crate::transition_sides::*;
use crate::unit_tests::test_utils::*;
use crate::{density::*, extraction::extract_from_fnmut};
use crate::{
extraction::extract,
voxel_source::{VoxelSource, WorldMappingVoxelSource},
};
use crate::{
structs::*,
voxel_coordinates::{HighResolutionVoxelIndex, RegularVoxelIndex},
};
use bevy::math::f32;
use flagset::Flags;
use hamcrest::prelude::*;
use hamcrest::*;
use rand::prelude::*;
#[test]
fn it_works() {
assert_eq!(2 + 2, 4);
}
#[test]
fn empty_extraction() {
let mut f = DensityArray::<f32>::new(10);
let b = Block::from([0.0, 0.0, 0.0], 10.0, 10);
let m = extract(&mut f, &b, 0.5, no_side());
assert_that!(m.num_tris(), equal_to(0));
}
#[test]
fn one_cube_corner_gives_one_triangle() {
let mut f = DensityArray::<f32>::new(1);
f.set(0, 0, 0, 1f32);
let b = Block::from([0.0, 0.0, 0.0], 1.0, 1);
let m = extract(&mut f, &b, 0.5, no_side());
assert_that!(
m.tris(),
tris!(tri_matcher(0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5))
);
}
#[test]
fn another_one_cube_corner() {
let mut f = DensityArray::<f32>::new(3);
f.set(3, 0, 0, 1f32);
let b = Block::from([0.0, 0.0, 0.0], 3.0, 3);
let m = extract(&mut f, &b, 0.5, no_side());
assert_that!(
m.tris(),
tris!(tri_matcher(2.5, 0.0, 0.0, 3.0, 0.0, 0.5, 3.0, 0.5, 0.0))
);
}
#[test]
fn two_corners_give_two_triangles_in_one_cube() {
let mut f = DensityArray::<f32>::new(1);
f.set(0, 0, 0, 1f32);
f.set(1, 0, 0, 1f32);
let b = Block::from([0.0, 0.0, 0.0], 1.0, 1);
let m = extract(&mut f, &b, 0.5, no_side());
assert_that!(
m.tris(),
tris!(
tri_matcher(1.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5),
tri_matcher(0.0, 0.5, 0.0, 1.0, 0.0, 0.5, 1.0, 0.5, 0.0)
)
);
}
#[test]
fn basic_normals() {
let mut f = DensityArray::<f32>::new(1);
for x in -1..3 {
for y in -1..3 {
f.set(x, y, 0, 1f32);
}
}
let b = Block::from([0.0, 0.0, 0.0], 1.0, 1);
let m = extract(&mut f, &b, 0.5, no_side());
#[rustfmt::skip]
assert_that!(
m.tris(),
tris!(
tri_matcher_with_normals(
0.0, 0.0, 0.5, 0.0, 0.0, 1.0,
1.0, 0.0, 0.5, 0.0, 0.0, 1.0,
1.0, 1.0, 0.5, 0.0, 0.0, 1.0),
tri_matcher(
0.0, 0.0, 0.5,
1.0, 1.0, 0.5,
0.0, 1.0, 0.5)
)
);
}
#[test]
fn ambiguous_case() {
let mut f = DensityArray::<f32>::new(1);
f.set(0, 0, 0, 1f32);
f.set(0, 0, 1, 1f32);
f.set(0, 1, 0, 1f32);
f.set(0, 1, 1, 1f32);
f.set(1, 1, 0, 1f32);
f.set(1, 0, 1, 1f32);
let b = Block::from([0.0, 0.0, 0.0], 1.0, 1);
let m = extract(&mut f, &b, 0.5, no_side());
assert_that!(m.tris().len(), equal_to(4));
let mut f = DensityArray::<f32>::new(1);
f.set(0, 1, 0, 1f32);
f.set(0, 0, 1, 1f32);
let b = Block::from([1.0, 1.0, 1.0], 1.0, 1);
let m = extract(&mut f, &b, 0.5, no_side());
#[rustfmt::skip]
assert_that!(
m.tris(),
tris!(
tri_matcher(
1.0, 1.5, 1.0,
1.5, 2.0, 1.0,
1.0, 2.0, 1.5),
tri_matcher(
1.0, 1.0, 1.5,
1.0, 1.5, 2.0,
1.5, 1.0, 2.0)
)
);
}
#[test]
fn vertices_are_reused_within_a_cell() {
let mut f = DensityArray::<f32>::new(1);
f.set(0, 0, 0, 1f32);
f.set(1, 0, 0, 1f32);
let b = Block::from([0.0, 0.0, 0.0], 1.0, 1);
let m = extract(&mut f, &b, 0.5, no_side());
assert_that!(m.tris().len(), equal_to(2));
let positions_for_4_vertices = 4 * 3; assert_that!(m.positions.len(), equal_to(positions_for_4_vertices));
}
#[test]
fn vertices_are_reused_between_cells() {
let mut f = DensityArray::<f32>::new(2);
f.set(0, 2, 2, 1f32);
f.set(1, 2, 2, 1f32);
let b = Block::from([0.0, 0.0, 0.0], 2.0, 2);
let m = extract(&mut f, &b, 0.5, no_side());
assert_that!(m.tris().len(), equal_to(3));
let positions_for_5_vertices = 5 * 3; assert_that!(m.positions.len(), equal_to(positions_for_5_vertices));
}
#[test]
fn trivial_transition_cell() {
let mut f = DensityArray::<f32>::new(10);
let b = Block::from([0.0, 0.0, 0.0], 10.0, 10);
let transition_sides = TransitionSide::LowZ.into();
let m = extract_from_grid(&mut f, &b, 0.5, transition_sides);
assert_that!(m.tris().len(), equal_to(0));
}
#[test]
fn simplest_transition_cell() {
let mut f = DensityArray::<f32>::new(10);
f.set(5, 5, 0, 1f32);
let b = Block::from([0.0, 0.0, 0.0], 100.0, 10);
let transition_sides = TransitionSide::LowZ.into();
let m = extract_from_grid(&mut f, &b, 0.5, transition_sides);
let v_top = (50.0, 50.0, 5.75);
let q1v2 = (55f32, 50f32, 1.5f32); let q1v3 = (50f32, 55f32, 1.5f32); let q1v4 = (50f32, 52.5f32, 0f32); let q1v5 = (52.5f32, 50f32, 0f32);
let q2v2 = (50f32, 55f32, 1.5f32);
let q2v3 = (45f32, 50f32, 1.5f32);
let q2v4 = (47.5f32, 50f32, 0f32);
let q2v5 = (50f32, 52.5f32, 0f32);
let q3v2 = (45f32, 50f32, 1.5f32);
let q3v3 = (50f32, 45f32, 1.5f32);
let q3v4 = (50f32, 47.5f32, 0f32);
let q3v5 = (47.5f32, 50f32, 0f32);
let q4v2 = (50f32, 45f32, 1.5f32);
let q4v3 = (55f32, 50f32, 1.5f32);
let q4v4 = (52.5f32, 50f32, 0f32);
let q4v5 = (50f32, 47.5f32, 0f32);
assert_that!(
restrict(m.tris(), 50f32, 50f32, 0f32, 10f32),
tris!(
tri_matcher_vecs(v_top, q1v2, q1v3),
tri_matcher_vecs(q1v4, q1v3, q1v2),
tri_matcher_vecs(q1v2, q1v5, q1v4)
)
);
assert_that!(
m.tris(),
tris!(
tri_matcher_vecs(v_top, q1v2, q1v3),
tri_matcher_vecs(q1v4, q1v3, q1v2),
tri_matcher_vecs(q1v2, q1v5, q1v4),
tri_matcher_vecs(v_top, q2v2, q2v3),
tri_matcher_vecs(q2v4, q2v3, q2v2),
tri_matcher_vecs(q2v2, q2v5, q2v4),
tri_matcher_vecs(v_top, q3v2, q3v3),
tri_matcher_vecs(q3v4, q3v3, q3v2),
tri_matcher_vecs(q3v2, q3v5, q3v4),
tri_matcher_vecs(v_top, q4v2, q4v3),
tri_matcher_vecs(q4v4, q4v3, q4v2),
tri_matcher_vecs(q4v2, q4v5, q4v4)
)
);
}
#[test]
fn simple_transition_cell() {
let mut f = DensityArray::<f32>::new(3);
f.set(1, 1, 0, 1f32);
f.set_inter(TransitionSide::LowZ, 1, 1, 0, 1, 0, 1f32);
f.set_inter(TransitionSide::LowZ, 0, 1, 2, 1, 0, 1f32);
let b = Block::from([0.0, 0.0, 0.0], 30.0, 3);
let transition_sides = TransitionSide::LowZ.into();
let m = extract_from_grid(&mut f, &b, 0.5, transition_sides);
let v_top = (10.0, 10.0, 5.75);
let q1v2 = (15f32, 10f32, 1.5f32); let q1v3 = (10f32, 15f32, 1.5f32); let q1v4 = (12.5f32, 10f32, 0f32); let q1v5 = (12.5f32, 15f32, 0f32); let q1v6 = (10f32, 17.5f32, 0f32); assert_that!(
restrict(m.tris(), 10f32, 10f32, 0f32, 10f32),
tris!(
tri_matcher_vecs(v_top, q1v2, q1v3),
tri_matcher_vecs(q1v2, q1v4, q1v5),
tri_matcher_vecs(q1v5, q1v3, q1v2),
tri_matcher_vecs(q1v5, q1v6, q1v3)
)
);
}
#[test]
fn simplest_transition_cell_non_negative_z() {
let mut f = DensityArray::<f32>::new(3);
f.set(0, 1, 1, 1f32);
let b = Block::from([0.0, 0.0, 0.0], 30.0, 3);
let transition_sides = TransitionSide::LowX.into();
let m = extract_from_grid(&mut f, &b, 0.5, transition_sides);
let v_top = (5.75, 10.0, 10.0);
let q1v2 = (1.5f32, 15f32, 10f32); let q1v3 = (1.5f32, 10f32, 15f32); let q1v4 = (0f32, 10f32, 12.5f32); let q1v5 = (0f32, 12.5f32, 10f32);
assert_that!(
restrict(m.tris(), 0f32, 10f32, 10f32, 10f32),
tris!(
tri_matcher_vecs(v_top, q1v2, q1v3),
tri_matcher_vecs(q1v4, q1v3, q1v2),
tri_matcher_vecs(q1v2, q1v5, q1v4)
)
);
}
#[test]
fn simple_sphere() {
struct Sphere;
impl ScalarField<f32, f32> for Sphere {
fn get_density(&self, x: f32, y: f32, z: f32) -> f32 {
let distance_from_center =
((x - 10f32) * (x - 10f32) + (y - 10f32) * (y - 10f32) + (z - 10f32) * (z - 10f32))
.sqrt();
let d = 1f32 - distance_from_center / 5f32;
d
}
}
let block = Block::from([0.0, 0.0, 0.0], 20.0, 2);
let mut source = WorldMappingVoxelSource {
field: &mut Sphere {},
block: &block,
};
let threshold = 0f32;
let m = extract(&mut source, &block, threshold, no_side());
let v_plus_x = (15.0, 10.0, 10.0);
let v_minus_x = (5.0, 10.0, 10.0);
let v_plus_y = (10.0, 15.0, 10.0);
let v_minus_y = (10.0, 5.0, 10.0);
let v_plus_z = (10.0, 10.0, 15.0);
let v_minus_z = (10.0, 10.0, 5.0);
assert_that!(
m.tris(),
tris!(
tri_matcher_vecs(v_plus_z, v_plus_x, v_plus_y),
tri_matcher_vecs(v_plus_z, v_plus_y, v_minus_x),
tri_matcher_vecs(v_plus_z, v_minus_x, v_minus_y),
tri_matcher_vecs(v_plus_z, v_minus_y, v_plus_x),
tri_matcher_vecs(v_minus_z, v_plus_x, v_minus_y),
tri_matcher_vecs(v_minus_z, v_plus_y, v_plus_x),
tri_matcher_vecs(v_minus_z, v_minus_x, v_plus_y),
tri_matcher_vecs(v_minus_z, v_minus_y, v_minus_x)
)
);
}
struct CountingField<'b, S> {
pub calls: RefCell<usize>,
underlying: WorldMappingVoxelSource<'b, S, f32>,
}
impl<'b, C> CountingField<'b, ScalarFieldForFn<C>> {
pub fn new(closure: C, block: &'b Block<f32>) -> Self {
let underlying = WorldMappingVoxelSource::<'b, ScalarFieldForFn<C>, f32> {
field: ScalarFieldForFn(closure),
block: block,
};
Self {
calls: RefCell::new(0),
underlying: underlying,
}
}
pub fn count(&self) -> usize {
*self.calls.borrow()
}
}
#[allow(unused_variables)]
impl<'b, S> VoxelSource<f32> for CountingField<'b, S>
where
S: ScalarField<f32, f32>,
{
fn get_density(&self, voxel_index: &RegularVoxelIndex) -> f32 {
*self.calls.borrow_mut() += 1;
self.underlying.get_density(voxel_index)
}
fn get_transition_density(&self, index: &HighResolutionVoxelIndex) -> f32 {
*self.calls.borrow_mut() += 1;
self.underlying.get_transition_density(index)
}
}
#[test]
fn count_density_calls_minimal() {
let block = Block::from([0.0, 0.0, 0.0], 10.0, 1);
let mut source = CountingField::new(|_, _, _| 0.0, &block);
extract(&mut source, &block, 0.5, no_side());
assert_that!(source.count(), equal_to(8));
let mut source = CountingField::new(|_, _, _| 0.0, &block);
extract(&mut source, &block, 0.5, TransitionSide::LowX.into());
assert_that!(source.count(), equal_to(13));
let mut source = CountingField::new(|_, _, _| 0.0, &block);
extract(
&mut source,
&block,
0.5,
(TransitionSide::LowX | TransitionSide::LowZ).into(),
);
assert_that!(source.count(), equal_to(18));
}
#[test]
fn count_density_calls_random() {
let block = Block::from([0.0, 0.0, 0.0], 10.0, 3);
let mut source = CountingField::new(&|_, _, _| rand::random(), &block);
extract(&mut source, &block, 0.5, no_side());
assert_that!(source.count(), greater_than_or_equal_to(4 * 4 * 4));
assert_that!(source.count(), less_than_or_equal_to(4 * 4 * 4 + 6 * 4 * 4));
let mut source = CountingField::new(&|_, _, _| rand::random(), &block);
extract(&mut source, &block, 0.5, TransitionSide::LowX.into());
}
#[test]
fn random_data() {
let seed = match env::var("TEST_SEED") {
Ok(s) => s.parse::<u64>().unwrap(),
_ => random(),
};
println!("Using seed {}", seed);
let mut rng = StdRng::seed_from_u64(seed);
let subdivisions = rng.gen_range(2..30);
let block = Block::from([0.0, 0.0, 0.0], 10.0, subdivisions);
let sides = random_sides(&mut rng);
let source = |_, _, _| rng.gen_range(-1.0..1.0);
let m = extract_from_fnmut(source, &block, 0.5, sides);
println!(
"Extracted mesh with {} tris for sides {:?}",
m.num_tris(),
sides
);
}
fn random_sides(rng: &mut StdRng) -> TransitionSides {
let mut sides = no_side();
for s in TransitionSide::LIST {
if rng.gen_bool(0.5) {
let ss: TransitionSides = (*s).into();
sides = sides | ss;
}
}
sides
}