use crate::common::VertexIndex;
use crate::corner_table::{CornerIndex, TriangleIndex};
use crate::isotropic_remesh::IsotropicRemeshAlgo;
use crate::prelude::*;
use vector_traits::glam::{Mat3, Vec3, Vec3A};
#[test]
fn test_coplanar_triangles_same_orientation() {
let a = Vec3A::new(0.0, 0.0, 0.0);
let b = Vec3A::new(1.0, 0.0, 0.0);
let c = Vec3A::new(0.0, 1.0, 0.0);
let d = Vec3A::new(0.0, -1.0, 0.0);
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow_wrong_order(
a, b, c, d,
);
println!("Coplanar same orientation: {:.6}", coplanarity);
assert!(
(coplanarity - 1.0).abs() < 0.001,
"Coplanar triangles should have coplanarity ≈ 1.0 (coplanarity={coplanarity})"
);
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, 0.95 * 0.95);
assert_eq!(coplanarity, Some(true));
}
#[test]
fn test_45_degree_dihedral_angle() {
let a = Vec3A::new(0.0, 1.0, 0.0);
let b = Vec3A::new(0.0, 0.0, 0.0);
let c = Vec3A::new(1.0, 0.0, 0.0);
let sqrt2_inv = std::f32::consts::FRAC_1_SQRT_2;
let d = Vec3A::new(0.0, -sqrt2_inv, sqrt2_inv);
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow(a, b, c, d);
println!("45-degree dihedral angle: {:.6}", coplanarity);
assert!(
(coplanarity - sqrt2_inv).abs() < 0.01,
"45° dihedral should have coplanarity ≈ 0.707 (coplanarity={coplanarity} ({}°))",
coplanarity.acos().to_degrees()
);
let threshold_45_deg_sq = sqrt2_inv * sqrt2_inv;
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
a,
b,
c,
d,
threshold_45_deg_sq,
);
assert_eq!(
result,
Some(true),
"45° dihedral with 45° threshold should pass (at boundary)"
);
}
#[test]
fn test_90_degree_triangle_angle() {
let a = Vec3A::new(0.0, 0.0, 1.0);
let b = Vec3A::new(0.0, 0.0, 0.0);
let c = Vec3A::new(0.0, 1.0, 0.0);
let d = Vec3A::new(1.0, 0.0, 0.0);
let sqrt2_inv = std::f32::consts::FRAC_1_SQRT_2;
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow_wrong_order(
a, b, c, d,
);
println!("45-degree dihedral angle: {:.6}", coplanarity);
assert!(
(coplanarity).abs() < 0.01,
"90° dihedral should have coplanarity ≈ 0.0 (coplanarity={coplanarity})"
);
let threshold_45_deg_sq = sqrt2_inv * sqrt2_inv;
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
a,
b,
c,
d,
threshold_45_deg_sq,
);
assert_eq!(
result, None,
"45° dihedral with 45° threshold should *not* pass for 90° triangles"
);
}
#[test]
fn test_dihedral_angle_rotation() {
let b = Vec3A::new(0.0, 0.0, 0.0);
let c = Vec3A::new(0.0, 1.0, 0.0);
let d = Vec3A::new(1.0, 0.0, 0.0);
for degrees in (2..=90).rev().step_by(5) {
let radians = (degrees as f32).to_radians();
let a = Vec3A::new(radians.cos(), 0.0, radians.sin());
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding_slow(a, b, c, d);
let expected_cos = radians.cos();
println!(
"Testing:{}° dihedral angle: cos_actual={:.6}, cos_expected={:.6}",
degrees, coplanarity, expected_cos
);
assert!(
(coplanarity - expected_cos).abs() < 0.001,
"expected_cos!=coplanarity {expected_cos}!={coplanarity} coplanarity={}°",
coplanarity.acos().to_degrees()
);
let threshold_above = (radians + 1.0f32.to_radians()).cos().powi(2);
let threshold_below = (radians - 1.0f32.to_radians()).cos().powi(2);
let result_above = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
a,
b,
c,
d,
threshold_above,
);
assert_ne!(
result_above,
Some(true),
"dihedral angle threshold {}° should accept triangles set at {degrees}°",
threshold_above.sqrt().acos().to_degrees()
);
let result_below = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
a,
b,
c,
d,
threshold_below,
);
assert_eq!(
result_below,
None,
"dihedral angle threshold {}° should reject triangles set at {degrees}°",
threshold_below.sqrt().acos().to_degrees()
);
}
}
#[test]
fn test_dihedral_angle_rotation_2() {
let b = Vec3A::new(0.0, 0.0, 0.0);
let c = Vec3A::new(0.0, 1.0, 0.0);
let d = Vec3A::new(1.0, 0.0, 0.0);
for degrees in (-90_i32..=90).step_by(2) {
let radians = (degrees as f32).to_radians();
let a = Vec3A::new(-radians.cos(), 0.0, radians.sin());
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow(a, b, c, d);
let expected_cos = radians.cos();
println!(
"Testing:{}° dihedral angle: cos_actual={:.6}, cos_expected={:.6} a:{:?}",
degrees,
coplanarity,
expected_cos,
<Vec3A as Into<[f32; 3]>>::into(a)
);
assert!(
(coplanarity - expected_cos).abs() < 0.001,
"expected_cos!=coplanarity {expected_cos}!={coplanarity} coplanarity={}°",
coplanarity.acos().to_degrees()
);
let threshold_sq_above = (radians.abs() - 1.0f32.to_radians()).cos().powi(2);
let threshold_sq_below = (radians.abs() + 1.0f32.to_radians()).cos().powi(2);
println!(
"{degrees}°.abs()={}° above:{}° below:{}° coplanarity^2:{} threshold_sq_above:{threshold_sq_above} threshold_sq_below:{threshold_sq_below}",
degrees.abs(),
threshold_sq_above.sqrt().acos().to_degrees(),
threshold_sq_below.sqrt().acos().to_degrees(),
coplanarity.powi(2)
);
}
}
#[test]
fn test_dihedral_angle_thresholds() {
let b = Vec3A::new(0.0, 0.0, 0.0);
let c = Vec3A::new(0.0, 1.0, 0.0);
let d = Vec3A::new(1.0, 0.0, 0.0);
for threshold_degrees in (3..=85).step_by(2) {
let threshold_radians = (threshold_degrees as f32).to_radians();
let threshold_cos_sq = threshold_radians.cos().powi(2);
let pass_angle = threshold_degrees - 1;
let pass_radians = (pass_angle as f32).to_radians();
let a_pass = Vec3A::new(-pass_radians.cos(), 0.0, pass_radians.sin());
let cos_sq_pass =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow(
a_pass, b, c, d,
)
.powi(2);
assert!(
cos_sq_pass > threshold_cos_sq,
"Pass case failed: {}° should pass {}° threshold",
pass_angle,
threshold_degrees
);
let fail_angle = threshold_degrees + 1;
let fail_radians = (fail_angle as f32).to_radians();
let a_fail = Vec3A::new(-fail_radians.cos(), 0.0, fail_radians.sin());
let cos_sq_fail =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow(
a_fail, b, c, d,
)
.powi(2);
assert!(
cos_sq_fail < threshold_cos_sq,
"Fail case failed: {}° should fail {}° threshold",
fail_angle,
threshold_degrees
);
println!(
"Threshold {}°: PASS at {}° (cos_sq={:.6}), FAIL at {}° (cos_sq={:.6})",
threshold_degrees, pass_angle, cos_sq_pass, fail_angle, cos_sq_fail
);
}
}
#[test]
fn test_dihedral_angle_thresholds_works() {
let b = Vec3A::new(0.0, 0.0, 0.0);
let c = Vec3A::new(0.0, 1.0, 0.0);
let d = Vec3A::new(1.0, 0.0, 0.0);
for threshold_degrees in (2..=88).step_by(1) {
let threshold_radians = (threshold_degrees as f32).to_radians();
let threshold_cos_sq = threshold_radians.cos().powi(2);
let pass_angle = threshold_degrees - 1;
let fail_angle = threshold_degrees + 1;
let pass_radians = (pass_angle as f32).to_radians();
let fail_radians = (fail_angle as f32).to_radians();
let a_pass_pos = Vec3A::new(-pass_radians.cos(), 0.0, pass_radians.sin());
let a_fail_pos = Vec3A::new(-fail_radians.cos(), 0.0, fail_radians.sin());
let cos_sq_pass_pos =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow(
a_pass_pos, b, c, d,
)
.powi(2);
let cos_sq_fail_pos =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow(
a_fail_pos, b, c, d,
)
.powi(2);
assert!(
cos_sq_pass_pos > threshold_cos_sq,
"Positive pass case failed"
);
assert!(
cos_sq_fail_pos < threshold_cos_sq,
"Positive fail case failed"
);
let a_pass_neg = Vec3A::new(-pass_radians.cos(), 0.0, -pass_radians.sin());
let a_fail_neg = Vec3A::new(-fail_radians.cos(), 0.0, -fail_radians.sin());
assert_eq!(
Some(true),
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
a_pass_neg,
b,
c,
d,
threshold_cos_sq
)
);
assert_eq!(
Some(true),
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
a_pass_pos,
b,
c,
d,
threshold_cos_sq
)
);
assert_eq!(
None,
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
a_fail_neg,
b,
c,
d,
threshold_cos_sq
)
);
assert_eq!(
None,
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
a_fail_pos,
b,
c,
d,
threshold_cos_sq
)
);
println!(
"Threshold {}°: PASS both signs at {}° (cos_sq={:.6}), FAIL both signs at {}° (cos_sq={:.6})",
threshold_degrees, pass_angle, cos_sq_pass_pos, fail_angle, cos_sq_fail_pos
);
}
}
#[test]
fn test_opposite_normals() {
let a = Vec3A::new(0.0, 0.0, 0.0);
let b = Vec3A::new(1.0, 0.0, 0.0);
let c = Vec3A::new(0.0, 1.0, 0.0);
let d = Vec3A::new(0.0, 2.0, 0.0);
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow_wrong_order(
a, b, c, d,
);
println!("Opposite normals: {:.6}", coplanarity);
let abc = (b - a).cross(c - a);
let dba = (b - d).cross(a - d);
println!(" Triangle abc normal: {:?}", abc.normalize());
println!(" Triangle dba normal: {:?}", dba.normalize());
assert!(
(coplanarity + 1.0).abs() < 0.001,
"Opposite triangles should have coplanarity ≈ -1.0"
);
}
#[test]
fn test_perpendicular_triangles() {
let a = Vec3A::new(0.0, 0.0, 0.0);
let b = Vec3A::new(1.0, 0.0, 0.0);
let c = Vec3A::new(0.0, 1.0, 0.0); let d = Vec3A::new(0.0, 0.0, 1.0);
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow_wrong_order(
a, b, c, d,
);
println!("Perpendicular triangles: {:.6}", coplanarity);
let normal1 = (b - a).cross(c - a); let normal2 = (d - b).cross(a - b);
println!(" Triangle abc normal: {:?}", normal1.normalize());
println!(" Triangle dba normal: {:?}", normal2.normalize());
assert!(
coplanarity.abs() < 0.001,
"Perpendicular triangles should have coplanarity ≈ 0.0"
);
}
#[test]
fn test_shared_edge_ccw_triangles() {
let a = Vec3A::new(0.0, 0.0, 0.0); let b = Vec3A::new(2.0, 0.0, 0.0); let c = Vec3A::new(1.0, 1.0, 0.0); let d = Vec3A::new(1.0, -1.0, 0.0);
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow_wrong_order(
a, b, c, d,
);
println!("Shared edge CCW triangles: {:.6}", coplanarity);
let normal1 = (b - a).cross(c - a); let normal2 = (b - d).cross(a - d).normalize();
println!(
" Triangle abc normal: {:?} -> {:?}",
normal1,
normal1.normalize()
);
println!(
" Triangle dba normal: {:?} -> {:?}",
normal2,
normal2.normalize()
);
println!(" This case shows the issue: both triangles are CCW but normals are opposite");
assert!(
(coplanarity - 1.0).abs() < 0.001,
"This should give 1.0, demonstrating the issue coplanarity:{coplanarity}"
);
}
#[test]
fn test_proper_adjacent_triangles() {
let a = Vec3A::new(0.0, 0.0, 0.0);
let b = Vec3A::new(1.0, 0.0, 0.0);
let c = Vec3A::new(0.5, 1.0, 0.1); let d = Vec3A::new(0.5, -1.0, -0.1);
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow_wrong_order(
a, b, c, d,
);
println!("Proper adjacent triangles: {:.6}", coplanarity);
let normal1 = (b - a).cross(c - a);
let normal2 = (d - b).cross(a - b);
println!(
" Triangle abc normal: {:?} -> {:?}",
normal1,
normal1.normalize()
);
println!(
" Triangle dba normal: {:?} -> {:?}",
normal2,
normal2.normalize()
);
assert!(
coplanarity > 0.5,
"Properly oriented adjacent triangles should have similar normals"
);
}
#[test]
fn debug_specific_case() {
let vc = Vec3A::new(-1.3545021, -1.0532312, -0.2397142); let vn = Vec3A::new(-0.8545021, -1.0532312, 0.2602858); let vp = Vec3A::new(-1.3545021, -1.0532312, 0.2602858); let vo = Vec3A::new(-0.8545021, -1.0532312, -0.2397142);
println!(" vc: {:?}", vc);
println!(" vn: {:?}", vn);
println!(" vp: {:?}", vp);
println!(" vo: {:?}", vo);
let coplanarity =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow_wrong_order(
vc, vn, vp, vo,
);
println!(" Coplanarity: {:.6}", coplanarity);
let normal1 = (vn - vc).cross(vp - vc);
let normal2 = (vo - vn).cross(vc - vn);
println!(" Normal1: {:?} -> {:?}", normal1, normal1.normalize());
println!(" Normal2: {:?} -> {:?}", normal2, normal2.normalize());
println!("\n Trying different orderings:");
let coplanarity2 =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow_wrong_order(
vn, vc, vp, vo,
);
println!(
" triangles_coplanarity(vn, vc, vp, vo): {:.6}",
coplanarity2
);
let coplanarity3 =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding_slow_wrong_order(
vc, vn, vp, vo,
);
println!(
" Original call triangles_coplanarity(vc, vn, vp, vo): {:.6}",
coplanarity3
);
}
#[test]
fn test_coplanar_triangles_same_orientatio_2() {
let a = Vec3A::new(0.0, 0.0, 0.0);
let b = Vec3A::new(0.0, 1.0, 0.0);
let c = Vec3A::new(1.0, 1.0, 0.0);
let d = Vec3A::new(1.0, 0.0, 0.0);
let threshold_sq = 0.9_f32.powi(2);
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, threshold_sq);
assert_eq!(result, Some(true));
}
#[test]
fn test_coplanar_triangles_opposite_orientation() {
let a = Vec3A::new(0.0, 0.0, 0.0);
let b = Vec3A::new(0.0, 1.0, 0.0);
let c = Vec3A::new(1.0, 1.0, 0.0);
let d = Vec3A::new(1.0, 0.0, 0.0);
let threshold_sq = 0.9_f32.powi(2);
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, c, b, d, threshold_sq);
assert_eq!(result, Some(true));
}
#[test]
fn test_triangles_tilted_small_angle() {
let angle = 10_f32.to_radians();
let a = Vec3A::new(0.0, 0.0, 0.0);
let b = Vec3A::new(0.0, 1.0, 0.0);
let c = Vec3A::new(1.0, 1.0, 0.0);
let d = Vec3A::new(1.0, 0.0, angle.sin());
let threshold_sq = angle.cos().powi(2);
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, threshold_sq);
assert_eq!(result, Some(true));
}
#[test]
fn test_triangles_tilted_large_angle() {
let angle = 60_f32.to_radians();
let a = Vec3A::new(0.0, 0.0, 0.0);
let b = Vec3A::new(0.0, 1.0, 0.0);
let c = Vec3A::new(1.0, 1.0, 0.0);
let d = Vec3A::new(1.0, 0.0, angle.sin());
let threshold_sq = (30_f32.to_radians()).cos().powi(2);
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, threshold_sq);
assert_eq!(result, None);
}
#[test]
fn test_degenerate_triangle() {
let a = Vec3A::new(0.0, 1.0, 0.0); let b = Vec3A::new(0.0, 1.0, 0.0);
let c = Vec3A::new(1.0, 1.0, 0.0);
let d = Vec3A::new(0.0, 0.0, 0.0);
let threshold_sq = 0.9_f32.powi(2);
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, threshold_sq);
assert_eq!(result, Some(false));
}
#[test]
fn test_ok_triangle_2() {
let threshold = 0.9;
let a: Vec3A = [0.0, 0.0, 0.0].into();
let b: Vec3A = [1.0, 0.0, 0.0].into();
let c: Vec3A = [1.0, 1.0, 0.0].into();
let d: Vec3A = [0.0, 1.0, 0.0].into();
assert!(IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(
a, b, c, d, threshold
));
}
#[test]
fn test_arrowhead_quad_1() {
let threshold = 0.9;
let a: Vec3A = [0.0, 0.0, 0.0].into(); let b: Vec3A = [1.0, 0.0, 0.0].into();
let c: Vec3A = [1.0, 1.0, 0.0].into();
let d: Vec3A = [0.0, 1.0, 0.0].into();
assert!(IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(
a, b, c, d, threshold
));
let a: Vec3A = [0.9, 0.9, 0.0].into(); let b: Vec3A = [1.0, 0.0, 0.0].into();
let c: Vec3A = [1.0, 1.0, 0.0].into();
let d: Vec3A = [0.0, 1.0, 0.0].into();
assert!(!IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(
a, b, c, d, threshold
));
let a: Vec3A = [0.0, 0.0, 0.0].into();
let b: Vec3A = [0.1, 0.9, 0.0].into(); let c: Vec3A = [1.0, 1.0, 0.0].into();
let d: Vec3A = [0.0, 1.0, 0.0].into();
assert!(!IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(
a, b, c, d, threshold
));
let a: Vec3A = [0.0, 0.0, 0.0].into();
let b: Vec3A = [1.0, 0.0, 0.0].into();
let c: Vec3A = [0.1, 0.1, 0.0].into(); let d: Vec3A = [0.0, 1.0, 0.0].into();
assert!(!IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(
a, b, c, d, threshold
));
let a: Vec3A = [0.0, 0.0, 0.0].into();
let b: Vec3A = [1.0, 0.0, 0.0].into();
let c: Vec3A = [1.0, 1.0, 0.0].into();
let d: Vec3A = [0.9, 0.1, 0.0].into(); assert!(!IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(
a, b, c, d, threshold
));
let a: Vec3A = [0.0, 0.0, 0.0].into();
let b: Vec3A = [1.0, 0.0, 0.0].into();
let c: Vec3A = [1.0, 1.0, 0.0].into();
let d: Vec3A = [0.0, 1.0, 0.0].into();
assert!(IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(
a, b, c, d, threshold
));
let a: Vec3A = [0.0, 0.0, 0.0].into();
let b: Vec3A = [1.0, 0.2, 0.0].into();
let c: Vec3A = [0.8, 1.0, 0.0].into();
let d: Vec3A = [-0.2, 0.8, 0.0].into();
assert!(IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(
a, b, c, d, threshold
));
let a: Vec3A = [0.0, 0.0, 0.0].into();
let b: Vec3A = [1.0, 0.0, 0.0].into();
let c: Vec3A = [0.2, 0.5, 0.0].into(); let d: Vec3A = [0.0, 1.0, 0.0].into();
assert!(!IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(
a, b, c, d, threshold
));
let a: Vec3A = [0.0, 0.0, 0.0].into();
let b: Vec3A = [1.0, 0.0, 0.0].into();
let c: Vec3A = [1.0, 1.0, 1.0].into(); let d: Vec3A = [0.0, 1.0, 0.0].into();
assert!(!IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(
a, b, c, d, threshold
));
}
#[test]
fn dihedral_angle_cosine_test_negative_threshold() {
let a: Vec3A = [0.0, 0.0, 1.0].into();
let b: Vec3A = [1.0, 0.0, 0.0].into();
let c: Vec3A = [0.5, 0.0, 1.0].into();
let d: Vec3A = [0.5, 0.5, 0.5].into();
let threshold_sq = (-0.9_f32).powi(2);
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_crease_angle(a, b, c, d, threshold_sq);
assert_eq!(result, Some(true));
}
#[test]
fn preserves_surface_normals() {
let a: Vec3A = [0.5, 0.0, 1.0].into();
let b: Vec3A = [0.0, 0.0, 1.0].into(); let c: Vec3A = [1.0, 0.0, 0.0].into();
let d: Vec3A = [0.3, 0.3, 1.0].into();
let slow_dihedral =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding_slow(a, b, c, d);
let threshold_sq = 56.0_f32.to_radians().cos().powi(2);
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, threshold_sq);
assert_eq!(
result,
Some(true),
"failed cos at {}° slow_dihedral:{}°",
threshold_sq.sqrt().acos().to_degrees(),
slow_dihedral.acos().to_degrees()
);
let threshold_sq = 54.0_f32.to_radians().cos().powi(2);
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, threshold_sq);
assert_eq!(
result,
None,
"failed cos at {}° slow_dihedral:{}°",
threshold_sq.sqrt().acos().to_degrees(),
slow_dihedral.acos().to_degrees()
);
}
#[test]
fn test_arrowhead_quad_3() {
let a: Vec3A = [-0.25, -1.0, -0.75].into();
let b: Vec3A = [-0.5, -1.0, -1.0].into();
let c: Vec3A = [0.5, -1.0, -1.0].into();
let d: Vec3A = [-0.125, -1.0, -0.375].into();
let threshold = 0.9;
let result = IsotropicRemeshAlgo::<f32, Vec3>::quad_is_convex_coplanar(a, b, c, d, threshold);
assert!(!result);
}
#[test]
fn test_some_triangles() {
let threshold_sq = 0.985_f32.powi(2);
let v = [
[-1.3919225, -0.38369077, 1.2062045],
[1.3399366, -0.38369077, 1.2062045],
[1.3399366, 0.29927406, 0.4109751],
[-1.3919225, -1.1789197, 1.3684515],
];
let a = v[0].into();
let b = v[1].into();
let c = v[2].into();
let d = v[3].into();
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, threshold_sq);
assert_eq!(result, None);
let v = [
[-1.3919225, -0.38369077, 1.2062045],
[1.3399366, 0.29927406, 0.4109751],
[-0.025992602, 0.79981124, -0.2719897],
[-1.3919225, -1.1789197, 1.3684515],
];
let a = v[0].into();
let b = v[1].into();
let c = v[2].into();
let d = v[3].into();
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, threshold_sq);
assert_eq!(result, None);
let v = [
[-1.3919225, -0.38369077, 1.2062045],
[-0.025992602, 0.79981124, -0.2719897],
[-1.3919225, 0.79981124, -0.2719897],
[-1.3919225, -1.1789197, 1.3684515],
];
let a = v[0].into();
let b = v[1].into();
let c = v[2].into();
let d = v[3].into();
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, threshold_sq);
assert_eq!(result, None);
let v = [
[-1.3919225, -0.38369077, 1.2062045],
[1.3399366, -1.1789197, 1.3684515],
[1.3399366, -0.38369077, 1.2062045],
[-1.3919225, -1.1789197, 1.3684515],
];
let a = v[0].into();
let b = v[1].into();
let c = v[2].into();
let d = v[3].into();
let result =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(a, b, c, d, threshold_sq);
assert_eq!(result, Some(true));
}
#[test]
fn test_normal_similarity_for_collapse_rotated() {
let threshold_sq = 0.9_f32.powi(2);
for angle_deg in [0.0_f32, 15.0, 30.0, 45.0, 60.0, 75.0, 90.0] {
let angle = angle_deg.to_radians();
let rotation = Mat3::from_rotation_z(angle);
let a = rotation * Vec3A::new(0.0, 0.0, 0.0);
let b = rotation * Vec3A::new(0.0, 1.0, 0.0);
let c = rotation * Vec3A::new(1.0, 1.0, 0.0);
let d = rotation * Vec3A::new(1.0, 0.0, 0.0);
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(
a,
b,
c,
d,
threshold_sq,
);
assert_eq!(
result,
Some(true),
"Failed at {}° rotation around Z",
angle_deg
);
}
for angle_deg in [0.0_f32, 15.0, 30.0, 45.0, 60.0, 75.0, 90.0] {
let angle = angle_deg.to_radians();
let rotation = Mat3::from_rotation_x(angle);
let a = rotation * Vec3A::new(0.0, 0.0, 0.0);
let b = rotation * Vec3A::new(0.0, 1.0, 0.0);
let c = rotation * Vec3A::new(1.0, 1.0, 0.0);
let d = rotation * Vec3A::new(1.0, 0.0, 0.0);
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(
a,
b,
c,
d,
threshold_sq,
);
assert_eq!(
result,
Some(true),
"Failed at {} degrees rotation around X",
angle_deg
);
}
for angle_deg in [0.0_f32, 15.0, 30.0, 45.0, 60.0, 75.0, 90.0] {
let angle = angle_deg.to_radians();
let rotation = Mat3::from_rotation_y(angle);
let a = rotation * Vec3A::new(0.0, 0.0, 0.0);
let b = rotation * Vec3A::new(0.0, 1.0, 0.0);
let c = rotation * Vec3A::new(1.0, 1.0, 0.0);
let d = rotation * Vec3A::new(1.0, 0.0, 0.0);
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(
a,
b,
c,
d,
threshold_sq,
);
assert_eq!(
result,
Some(true),
"Failed at {} degrees rotation around Y",
angle_deg
);
}
}
#[test]
fn test_triangles_not_inverted_1() {
let va = Vec3A::new(0.0, 0.0, 0.0); let vb = Vec3A::new(1.0, 0.0, 0.0); let vc = Vec3A::new(0.0, 1.0, 0.0);
let vd = Vec3A::new(1.0, 1.0, 0.0);
let cos_threshold_sq = -0.9 * -0.9;
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
va,
vb,
vc,
vd,
cos_threshold_sq,
);
assert_eq!(result, Some(true));
}
#[test]
fn test_triangles_same_orientation() {
let va = Vec3A::new(0.0, 0.0, 0.0);
let vb = Vec3A::new(1.0, 0.0, 0.0);
let vc = Vec3A::new(0.0, 1.0, 0.0);
let vd = Vec3A::new(1.0, 1.0, 0.0);
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
va,
vb,
vc,
vd,
-0.9 * -0.9,
);
assert_eq!(result, Some(true));
}
#[test]
fn test_degenerate_triangle_abc() {
let va = Vec3A::new(0.0, 0.0, 0.0);
let vb = Vec3A::new(1.0, 0.0, 0.0);
let vc = Vec3A::new(2.0, 0.0, 0.0);
let vd = Vec3A::new(1.0, 1.0, 0.0);
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
va,
vb,
vc,
vd,
-0.9 * -0.9,
);
assert_eq!(result, Some(false)); }
#[test]
fn test_nearly_inverted_at_threshold() {
let va = Vec3A::new(0.0, 0.0, 0.0);
let vb = Vec3A::new(1.0, 0.0, 0.0);
let vc = Vec3A::new(0.0, 1.0, 0.0);
let vd = Vec3A::new(1.0, 1.0, -0.4);
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
va,
vb,
vc,
vd,
-0.9 * -0.9,
);
assert!(result.is_some() || result.is_none());
}
#[test]
fn test_obtuse_angle_acceptable() {
let va = Vec3A::new(0.0, 0.0, 0.0);
let vb = Vec3A::new(1.0, 0.0, 0.0);
let vc = Vec3A::new(0.0, 1.0, 0.0);
let vd = Vec3A::new(1.0, 1.0, -0.1);
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
va,
vb,
vc,
vd,
-0.9 * -0.9,
);
assert_eq!(result, Some(true));
}
#[test]
fn test_strict_threshold() {
let va = Vec3A::new(0.0, 0.0, 0.0);
let vb = Vec3A::new(1.0, 0.0, 0.0);
let vc = Vec3A::new(0.0, 1.0, 0.0);
let vd = Vec3A::new(1.0, 1.0, -0.2);
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
va,
vb,
vc,
vd,
-0.5 * -0.5,
);
assert_eq!(result, Some(true));
let result = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_opposite_winding(
va,
vb,
vc,
vd,
-0.95 * -0.95,
);
assert_eq!(result, Some(true));
}
#[test]
fn test_normal_similarity_for_collapse() {
let va: Vec3A = [-0.5, -0.5, 0.5].into();
let vb: Vec3A = [0.5, -0.5, -0.5].into();
let vc: Vec3A = [0.5, -0.5, 0.5].into();
let vd: Vec3A = [-0.5, -0.5, -0.5].into();
let threshold = std::f32::consts::FRAC_1_SQRT_2;
let dot_slow =
IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding_slow(va, vb, vc, vd);
let dot_fast = IsotropicRemeshAlgo::<f32, Vec3>::normal_similarity_same_winding(
va,
vb,
vc,
vd,
threshold * threshold,
);
println!("dot_slow = {}", dot_slow);
println!("dot_fast = {:?}", dot_fast);
assert!(dot_slow > threshold);
assert_eq!(dot_fast, Some(true));
}
#[test]
fn test_corner_pool() -> Result<(), RemeshError> {
let vertices = [
[-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],
];
let indices = [
1, 2, 0, 3, 6, 2, 7, 4, 6, 5, 0, 4, 6, 0, 2, 3, 5, 7, 1, 3, 2, 3, 7, 6, 7, 5, 4, 5, 1, 0,
6, 4, 0, 3, 1, 5,
];
let remesher = IsotropicRemesh::<f32, _, true>::new(&vertices, &indices)?
.with_target_edge_length(0.5_f32)?
.with_default_collapse_multiplier()?
.with_default_split_multiplier()?
.with_default_flip_edges()?
.with_smooth_weight(0.01)?
.with_print_stats(10)?
.with_flip_edges(FlipStrategy::Valence)?;
let mut algo = remesher.new_algo()?;
algo.run_internal(10)?;
assert_eq!(
algo.vertex_pool.active_count(),
142,
"result_vertices.len() mismatch"
);
assert_eq!(
algo.corner_table.triangle_pool.active_count(),
280,
"triangles.len() mismatch"
);
algo.with_target_edge_length(2.2)?;
algo.run_internal(20)?;
let (result_vertices, result_indices) = algo.compress_mesh()?;
assert_eq!(result_vertices.len(), 8, "result_vertices.len() mismatch");
assert_eq!(result_indices.len(), 36, "result_indices.len() mismatch");
Ok(())
}
#[test]
fn test_debug_impl() {
let _ = format!("{:?}", VertexIndex(0));
let _ = format!("{:?}", CornerIndex(0));
let _ = format!("{:?}", TriangleIndex(0));
let _ = format!("{:?}", VertexIndex::INVALID);
let _ = format!("{:?}", CornerIndex::INVALID);
let _ = format!("{:?}", TriangleIndex::INVALID);
}