use super::*;
use crate::linalg::{dot, length, normalize, Vec3};
use crate::types::Error;
const DEFAULT_ANGLE: f64 = 52.5;
fn count_sphere_normal_alignment(
gl: &crate::types::MeshGL,
radius: f64,
expected: impl Fn(Vec3) -> Vec3,
) -> (i32, i32) {
let np = gl.num_prop as usize;
let (mut good, mut bad) = (0, 0);
let num_vert = gl.vert_properties.len() / np;
for v in 0..num_vert {
let pos = Vec3::new(
gl.vert_properties[v * np] as f64,
gl.vert_properties[v * np + 1] as f64,
gl.vert_properties[v * np + 2] as f64,
);
if (length(pos) - radius).abs() > 0.1 {
continue;
}
let n = Vec3::new(
gl.vert_properties[v * np + 3] as f64,
gl.vert_properties[v * np + 4] as f64,
gl.vert_properties[v * np + 5] as f64,
);
if dot(n, expected(pos)) > 0.9 {
good += 1;
} else {
bad += 1;
}
}
(good, bad)
}
#[test]
fn test_cpp_normals_cavity() {
let mesh = Manifold::sphere(10.0, 32)
.difference(&Manifold::sphere(3.0, 32))
.calculate_normals(0, DEFAULT_ANGLE)
.get_mesh_gl(-1);
assert!(mesh.num_prop >= 6, "numProp={}", mesh.num_prop);
let (good, bad) = count_sphere_normal_alignment(&mesh, 3.0, |pos| normalize(pos * -1.0));
assert!(good > 0, "NormalsCavity: good={}", good);
assert_eq!(bad, 0, "NormalsCavity: bad={}", bad);
}
#[test]
fn test_cpp_normals_rotate_before_calc() {
let mesh = Manifold::sphere(10.0, 32)
.rotate(45.0, 0.0, 0.0)
.calculate_normals(0, DEFAULT_ANGLE)
.get_mesh_gl(-1);
let (_, bad) = count_sphere_normal_alignment(&mesh, 10.0, normalize);
assert_eq!(bad, 0, "NormalsRotateBeforeCalc: bad={}", bad);
}
#[test]
fn test_cpp_normals_rotate_after_calc() {
let mesh = Manifold::sphere(10.0, 32)
.calculate_normals(0, DEFAULT_ANGLE)
.rotate(45.0, 0.0, 0.0)
.get_mesh_gl(-1);
let (_, bad) = count_sphere_normal_alignment(&mesh, 10.0, normalize);
assert_eq!(bad, 0, "NormalsRotateAfterCalc: bad={}", bad);
}
#[test]
fn test_cpp_normals_auto_substitute() {
let mesh = Manifold::sphere(10.0, 32)
.calculate_normals(0, DEFAULT_ANGLE)
.get_mesh_gl(-1);
assert!(mesh.num_prop >= 6, "numProp={}", mesh.num_prop);
assert!(!mesh.run_original_id.is_empty(), "no runs");
assert!(mesh.has_normals(0), "run 0 should have hasNormals bit");
}
#[test]
fn test_cpp_normals_round_trip() {
let round = Manifold::sphere(10.0, 32)
.difference(&Manifold::sphere(3.0, 32))
.calculate_normals(0, DEFAULT_ANGLE);
let out1 = round.get_mesh_gl(-1);
assert!(out1.has_normals(0), "out1 run 0 hasNormals");
let out2 = Manifold::from_mesh_gl(&out1).get_mesh_gl(-1);
assert!(out2.has_normals(0), "out2 run 0 hasNormals");
let (good, bad) = count_sphere_normal_alignment(&out2, 3.0, |pos| normalize(pos * -1.0));
assert!(good > 0, "NormalsRoundTrip: good={}", good);
assert_eq!(bad, 0, "NormalsRoundTrip: bad={}", bad);
}
#[test]
fn test_cpp_normals_refine_preserved() {
let mesh = Manifold::sphere(10.0, 32)
.calculate_normals(0, DEFAULT_ANGLE)
.refine(2)
.get_mesh_gl(-1);
assert!(!mesh.run_original_id.is_empty(), "no runs");
assert!(mesh.has_normals(0), "Refine should preserve hasNormals");
}
#[test]
fn test_cpp_normals_smooth_by_normals_no_arg() {
let smoothed = Manifold::sphere(10.0, 32)
.calculate_normals(0, DEFAULT_ANGLE)
.smooth_by_normals(0);
assert_eq!(smoothed.status(), Error::NoError);
}
#[test]
fn test_cpp_normals_non_standard_slot_not_recorded() {
let mesh = Manifold::sphere(10.0, 32)
.calculate_normals(3, DEFAULT_ANGLE)
.get_mesh_gl(-1);
assert!(!mesh.run_original_id.is_empty(), "no runs");
assert!(!mesh.has_normals(0), "non-standard slot must not record hasNormals");
}