use super::{
FinishedStone,
evaluate::{MetricsWanted, evaluate_pose},
evaluate_all_axes_profiles_finished_threaded, evaluate_all_axes_profiles_geom_threaded,
evaluate_full_axis_profile_at_azimuth_finished_threaded,
evaluate_full_axis_profile_at_azimuth_geom_threaded,
prepared::PreparedEval,
};
use crate::{
geometry::{cuts::StandardGemCuts, girdle_facet_finishes, tool::StoneGeometry},
optics::{
materials::GemMaterial,
raytracer::{EnvironmentSource, FacetFinish, LightingPreset},
},
};
type Curves = ([f32; 181], [f32; 181], [f32; 181]);
const fn studio() -> EnvironmentSource<'static> {
LightingPreset::RingLights.studio(1.0, 0.85, 0.95)
}
fn bits(curves: &Curves) -> Vec<u32> {
curves
.0
.iter()
.chain(&curves.1)
.chain(&curves.2)
.map(|v| v.to_bits())
.collect()
}
fn axis(
stone: FinishedStone<'_>,
material: &GemMaterial,
azimuth_deg: f32,
threads: usize,
) -> Curves {
evaluate_full_axis_profile_at_azimuth_finished_threaded(
stone,
material,
azimuth_deg,
studio(),
Some(threads),
)
}
#[test]
fn an_empty_or_all_polished_finish_list_is_the_polished_sweep() {
let planes = StandardGemCuts::standard_round_brilliant();
let material = GemMaterial::diamond();
let geom = StoneGeometry::planes_only(&planes);
let plain = evaluate_full_axis_profile_at_azimuth_geom_threaded(
geom,
&material,
45.0,
studio(),
Some(1),
);
let empty: &[FacetFinish] = &[];
let all_polished = vec![FacetFinish::Polished; planes.len()];
for finishes in [empty, all_polished.as_slice()] {
let finished = axis(FinishedStone::new(geom, finishes), &material, 45.0, 1);
assert_eq!(bits(&finished), bits(&plain));
}
assert_eq!(
bits(&axis(FinishedStone::polished(geom), &material, 45.0, 1)),
bits(&plain)
);
let prepared = PreparedEval::new_finished(geom, &all_polished, &material, studio())
.expect("a stone with planes prepares");
assert_eq!(prepared.stone().finishes.len(), 0);
}
#[test]
fn a_frosted_girdles_tilt_profile_differs_from_the_polished_one() {
let planes = StandardGemCuts::standard_round_brilliant();
let material = GemMaterial::diamond();
let geom = StoneGeometry::planes_only(&planes);
let finishes = girdle_facet_finishes(&planes);
assert!(finishes.contains(&FacetFinish::Frosted));
let polished = axis(FinishedStone::polished(geom), &material, 0.0, 1);
let frosted = axis(FinishedStone::new(geom, &finishes), &material, 0.0, 1);
assert_ne!(bits(&frosted), bits(&polished));
for value in frosted.0.iter().chain(&frosted.1).chain(&frosted.2) {
assert!(
value.is_finite() && (0.0..=100.0).contains(value),
"{value}"
);
}
let again = axis(FinishedStone::new(geom, &finishes), &material, 0.0, 1);
let threaded = axis(FinishedStone::new(geom, &finishes), &material, 0.0, 3);
assert_eq!(bits(&frosted), bits(&again));
assert_eq!(bits(&frosted), bits(&threaded));
}
#[test]
fn frosting_every_facet_moves_the_curves_and_the_all_axes_sweep_agrees() {
let planes = StandardGemCuts::standard_round_brilliant();
let material = GemMaterial::diamond();
let geom = StoneGeometry::planes_only(&planes);
let everything = vec![FacetFinish::Frosted; planes.len()];
let stone = FinishedStone::new(geom, &everything);
let polished = evaluate_all_axes_profiles_geom_threaded(geom, &material, studio(), Some(2));
let frosted = evaluate_all_axes_profiles_finished_threaded(stone, &material, studio(), Some(2));
assert_eq!(polished.len(), frosted.len());
assert!(
polished.iter().zip(&frosted).all(|(p, f)| p != f),
"every axis of a fully frosted stone differs from the polished one"
);
let first = &frosted[0];
let direct = axis(stone, &material, 0.0, 2);
assert_eq!(
bits(&direct),
bits(&(first.brilliance, first.extinction, first.windowing))
);
}
#[test]
fn the_full_metrics_pass_sees_frosted_facets() {
let planes = StandardGemCuts::standard_round_brilliant();
let material = GemMaterial::diamond();
let geom = StoneGeometry::planes_only(&planes);
let finishes = vec![FacetFinish::Frosted; planes.len()];
let polished = PreparedEval::new(geom, &material, studio()).expect("planes");
let frosted = PreparedEval::new_finished(geom, &finishes, &material, studio()).expect("planes");
let metrics = |prepared: &PreparedEval<'_>| {
let m = evaluate_pose(prepared, 0.6, 0.45, MetricsWanted::ALL, None);
[
m.brilliance_pct,
m.fire_index,
m.scintillation_pct,
m.windowing_pct,
m.extinction_pct,
]
};
let (p, f) = (metrics(&polished), metrics(&frosted));
assert!(f.iter().all(|v| v.is_finite()), "{f:?}");
assert_ne!(p.map(f32::to_bits), f.map(f32::to_bits));
}