use super::{
evaluate::{MetricsWanted, placeholder_metrics},
scintillation::{
combine_scintillation_pct, spatial_scintillation_pct, temporal_scintillation_pct,
},
types::GemOpticalMetrics,
};
const FIRE_DEGREES_TO_DISPLAY_SCALE: f32 = 275.0;
#[derive(Default)]
pub(super) struct MetricsAccumulators {
pub(super) total_rays: u32,
pub(super) windowed_rays: u32,
pub(super) extinct_rays: u32,
pub(super) returned_rays: u32,
pub(super) fire_energy_weighted_sum_deg: f32,
pub(super) dbg_fire_qualifying: u32,
pub(super) dbg_fire_angle_sum_unweighted: f32,
pub(super) dbg_fire_transmittance_sum: f32,
pub(super) cell_fraction_sum: f32,
pub(super) cell_fraction_sum_sq: f32,
pub(super) cell_count: u32,
pub(super) temporal_variance_sum: f32,
}
fn log_fire_diagnostics(
material_name: &str,
n_total: f32,
fire_index: f32,
acc: &MetricsAccumulators,
) {
let mean_angle_unweighted = if acc.dbg_fire_qualifying > 0 {
acc.dbg_fire_angle_sum_unweighted / acc.dbg_fire_qualifying as f32
} else {
0.0
};
let mean_transmittance = if acc.dbg_fire_qualifying > 0 {
acc.dbg_fire_transmittance_sum / acc.dbg_fire_qualifying as f32
} else {
0.0
};
eprintln!(
"DIAG material={material_name} n_total={n_total} returned_rays={} fire_qualifying={} mean_angle_unweighted={mean_angle_unweighted:.4} mean_transmittance={mean_transmittance:.4} weighted_sum={:.4} fire_index={fire_index:.4}",
acc.returned_rays, acc.dbg_fire_qualifying, acc.fire_energy_weighted_sum_deg
);
}
fn log_scintillation_diagnostics(
material_name: &str,
spatial_scint_pct: f32,
temporal_pct: f32,
scintillation_pct: f32,
) {
eprintln!(
"DIAG-SCINT material={material_name} spatial={spatial_scint_pct:.4} temporal={temporal_pct:.4} combined={scintillation_pct:.4}"
);
}
pub(super) fn finish_metrics(
acc: &MetricsAccumulators,
diag_material: Option<&str>,
wanted: MetricsWanted,
) -> GemOpticalMetrics {
let n_total = acc.total_rays.max(1) as f32;
let windowing_pct = (acc.windowed_rays as f32 / n_total * 100.0).clamp(0.0, 100.0);
let extinction_pct = (acc.extinct_rays as f32 / n_total * 100.0).clamp(0.0, 100.0);
let brilliance_pct = (acc.returned_rays as f32 / n_total * 100.0).clamp(0.0, 100.0);
let (fire_index, scintillation_pct) = if wanted.fire_and_scintillation {
fire_and_scintillation(acc, n_total, diag_material)
} else {
let placeholder = placeholder_metrics();
(placeholder.fire_index, placeholder.scintillation_pct)
};
GemOpticalMetrics {
brilliance_pct,
fire_index,
scintillation_pct,
windowing_pct,
extinction_pct,
}
}
fn fire_and_scintillation(
acc: &MetricsAccumulators,
n_total: f32,
diag_material: Option<&str>,
) -> (f32, f32) {
let fire_index =
(acc.fire_energy_weighted_sum_deg / n_total * FIRE_DEGREES_TO_DISPLAY_SCALE).max(0.1);
if let Some(material_name) = diag_material {
log_fire_diagnostics(material_name, n_total, fire_index, acc);
}
let spatial_scint_pct = spatial_scintillation_pct(
acc.cell_fraction_sum,
acc.cell_fraction_sum_sq,
acc.cell_count,
);
let temporal_pct = temporal_scintillation_pct(acc.temporal_variance_sum, acc.cell_count);
let scintillation_pct = combine_scintillation_pct(spatial_scint_pct, temporal_pct);
if let Some(material_name) = diag_material {
log_scintillation_diagnostics(
material_name,
spatial_scint_pct,
temporal_pct,
scintillation_pct,
);
}
(fire_index, scintillation_pct)
}