use super::plane::GpuFacetPlane;
use crate::optics::raytracer::FacetFinish;
pub(super) const GIRDLE_NORMAL_Y_EPSILON: f32 = 1e-3;
fn is_girdle_plane(plane: &GpuFacetPlane) -> bool {
plane.normal[1].abs() <= GIRDLE_NORMAL_Y_EPSILON
}
#[must_use]
pub fn classify_girdle_plane_indices(planes: &[GpuFacetPlane]) -> Vec<usize> {
planes
.iter()
.enumerate()
.filter_map(|(i, p)| is_girdle_plane(p).then_some(i))
.collect()
}
#[must_use]
pub fn girdle_facet_finishes(planes: &[GpuFacetPlane]) -> Vec<FacetFinish> {
planes
.iter()
.map(|p| {
if is_girdle_plane(p) {
FacetFinish::Frosted
} else {
FacetFinish::Polished
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::cuts::{STANDARD_ROUND_BRILLIANT_GIRDLE_FACETS, StandardGemCuts};
use glam::Vec3;
#[test]
fn matches_standard_round_brilliant_girdle_constant() {
let planes = StandardGemCuts::standard_round_brilliant();
let got = classify_girdle_plane_indices(&planes);
let expected: Vec<usize> = STANDARD_ROUND_BRILLIANT_GIRDLE_FACETS.collect();
assert_eq!(got, expected);
}
#[test]
fn srb_finishes_mark_exactly_the_girdle_band() {
let planes = StandardGemCuts::standard_round_brilliant();
let finishes = girdle_facet_finishes(&planes);
assert_eq!(finishes.len(), planes.len());
for (i, finish) in finishes.iter().enumerate() {
let expected = if STANDARD_ROUND_BRILLIANT_GIRDLE_FACETS.contains(&i) {
FacetFinish::Frosted
} else {
FacetFinish::Polished
};
assert_eq!(*finish, expected, "index {i}");
}
}
#[test]
fn emerald_cut_girdle_is_a_different_range_than_srb() {
let planes = StandardGemCuts::emerald_cut();
let got = classify_girdle_plane_indices(&planes);
let expected: Vec<usize> = (13..21).collect();
assert_eq!(got, expected);
assert_ne!(
got,
STANDARD_ROUND_BRILLIANT_GIRDLE_FACETS.collect::<Vec<_>>()
);
}
#[test]
fn no_girdle_facets_returns_empty() {
let planes = vec![
GpuFacetPlane::new(Vec3::new(0.0, 1.0, 0.0), -1.0),
GpuFacetPlane::new(Vec3::new(0.0, -1.0, 0.0), -1.0),
GpuFacetPlane::new(Vec3::new(1.0, 1.0, 0.0), -1.0),
GpuFacetPlane::new(Vec3::new(-1.0, 1.0, 0.0), -1.0),
];
assert_eq!(classify_girdle_plane_indices(&planes), Vec::<usize>::new());
assert_eq!(
girdle_facet_finishes(&planes),
vec![FacetFinish::Polished; planes.len()]
);
}
#[test]
fn empty_planes_does_not_panic() {
assert_eq!(classify_girdle_plane_indices(&[]), Vec::<usize>::new());
assert_eq!(girdle_facet_finishes(&[]), Vec::<FacetFinish>::new());
}
#[test]
fn deterministic_across_repeated_calls() {
let planes = StandardGemCuts::standard_round_brilliant();
let a = classify_girdle_plane_indices(&planes);
let b = classify_girdle_plane_indices(&planes);
assert_eq!(a, b);
let fa = girdle_facet_finishes(&planes);
let fb = girdle_facet_finishes(&planes);
assert_eq!(fa, fb);
}
}