use std::cell::RefCell;
use std::env;
use crate::extraction::extract;
use crate::structs::generic_mesh::*;
use crate::structs::transition_sides::*;
use crate::unit_tests::test_utils::*;
use bevy::math::f32;
use flagset::Flags;
use hamcrest2::prelude::*;
use rand::prelude::*;
use rand::rng;
use rand::Rng;
use crate::structs::block::Block;
use crate::structs::block_star_view::BlockStarView;
use crate::traits::data_field::DataField;
#[test]
fn empty_extraction() {
let f = TestField::new([0.0, 0.0, 0.0], 10.0, 10);
let m = f.extract(0.5).build();
assert_that!(m.num_tris(), equal_to(0));
}
#[test]
fn one_cube_corner_gives_one_triangle() {
let mut f = TestField::new([0.0, 0.0, 0.0], 1.0, 1);
f.set(0.0, 0.0, 0.0);
let m = f.extract(0.5).build();
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 = TestField::new([0.0, 0.0, 0.0], 3.0, 3);
f.set(3.0, 0.0, 0.0);
let m = f.extract(0.5).build();
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 = TestField::new([0.0, 0.0, 0.0], 1.0, 1);
f.set(0.0, 0.0, 0.0);
f.set(1.0, 0.0, 0.0);
let m = f.extract(0.5).build();
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 = TestField::new([0.0, 0.0, 0.0], 1.0, 1);
for x in -1..3 {
for y in -1..3 {
f.set(x as f32, y as f32, 0.0);
}
}
let m = f.extract(0.5).build();
#[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 = TestField::new([0.0, 0.0, 0.0], 1.0, 1);
f.set(0.0, 0.0, 0.0);
f.set(0.0, 0.0, 1.0);
f.set(0.0, 1.0, 0.0);
f.set(0.0, 1.0, 1.0);
f.set(1.0, 1.0, 0.0);
f.set(1.0, 0.0, 1.0);
let m = f.extract(0.5).build();
assert_that!(m.tris().len(), equal_to(4));
let mut f = TestField::new([1.0, 1.0, 1.0], 1.0, 1);
f.set(1.0, 2.0, 1.0);
f.set(1.0, 1.0, 2.0);
let m = f.extract(0.5).build();
#[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 = TestField::new([0.0, 0.0, 0.0], 1.0, 1);
f.set(0.0, 0.0, 0.0);
f.set(1.0, 0.0, 0.0);
let m = f.extract(0.5).build();
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 = TestField::new([0.0, 0.0, 0.0], 2.0, 2);
f.set(0.0, 2.0, 2.0);
f.set(1.0, 2.0, 2.0);
let m = f.extract(0.5).build();
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 f = TestField::new([0.0, 0.0, 0.0], 10.0, 10);
let m = f.extract2(0.5, TransitionSide::LowZ.into()).build();
assert_that!(m.tris().len(), equal_to(0));
}
#[test]
fn simplest_transition_cell() {
let mut f = TestField::new([0.0, 0.0, 0.0], 100.0, 10);
f.set(50.0, 50.0, 0.0);
let m = f.extract2(0.5, TransitionSide::LowZ.into()).build();
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 = TestField::new([0.0, 0.0, 0.0], 30.0, 3);
f.set(10.0, 10.0, 0.0);
f.set(10.0, 15.0, 0.0);
let m = f.extract2(0.5, TransitionSide::LowZ.into()).build();
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 = TestField::new([0.0, 0.0, 0.0], 30.0, 3);
f.set(0.0, 10.0, 10.0);
let m = f.extract2(0.5, TransitionSide::LowX.into()).build();
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 DataField<f32, f32> for Sphere {
fn get_data(&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();
1f32 - distance_from_center / 5f32
}
}
let block = Block::new([0.0, 0.0, 0.0], 20.0, 2);
let threshold = 0f32;
let m = GenericMeshBuilder::new();
let blocks = BlockStarView::new_relaying_to_field(&Sphere, block, &TransitionSide::none());
let m = extract(&blocks, threshold, m).build();
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)
)
);
}
#[test]
fn random_data() {
let seed = match env::var("TEST_SEED") {
Ok(s) => s.parse::<u64>().unwrap(),
_ => rng().random(),
};
println!("Using seed {}", seed);
let mut rng = StdRng::seed_from_u64(seed);
let subdivisions = rng.random_range(2..30);
let rng2 = RefCell::new(StdRng::seed_from_u64(rng.next_u64()));
let block = Block::new([0.0, 0.0, 0.0], 10.0, subdivisions);
let sides = random_sides(&mut rng);
let source = |_, _, _| {
rng2.borrow_mut().random_range(-1.0..1.0)
};
let m = GenericMeshBuilder::new();
let blocks = BlockStarView::new_relaying_to_field(&source, block, &sides);
let m = extract(&blocks, 0.5, m).build();
println!(
"Extracted mesh with {} tris for sides {:?}",
m.num_tris(),
sides
);
}
fn random_sides(rng: &mut StdRng) -> TransitionSides {
let mut sides = TransitionSide::none();
for s in TransitionSide::LIST {
if rng.random_bool(0.5) {
let ss: TransitionSides = (*s).into();
sides |= ss;
}
}
sides
}
#[test]
fn test_regular_cache_extended_loaded() {
let subdivisions = 3;
let size = 3.0;
let block = Block::new([0.0, 0.0, 0.0], size, subdivisions);
let sides = TransitionSide::LowX.into();
let source = |x: f32, y: f32, z: f32| {
if ((x - 0.0).abs() > f32::EPSILON)
|| ((y - 1.5).abs() > f32::EPSILON)
|| ((z - 1.0).abs() > f32::EPSILON) {
0f32
} else {
1f32
}
};
let m = GenericMeshBuilder::new();
let blocks = BlockStarView::new_relaying_to_field(&source, block, &sides);
let m = extract(&blocks, 0.5, m).build();
println!(
"Extracted mesh with {} tris for sides {:?}",
m.num_tris(),
sides
);
}