use std::fmt;
use super::{Fluorescence, GemMaterial, LightingPreset, StoneGeometry};
use crate::geometry::tool::GPU_MAX_TOOL_PRIMITIVES;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpuRouteDecline {
Material,
TooManyTools {
tools: usize,
},
Fluorescence,
UvLamp,
}
impl fmt::Display for GpuRouteDecline {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Material => f.write_str("The material has features only the CPU tracer supports"),
Self::TooManyTools { tools } => write!(
f,
"The stone has {tools} concave tools; the GPU traces at most \
{GPU_MAX_TOOL_PRIMITIVES}"
),
Self::Fluorescence => f.write_str("Fluorescence is traced on the CPU only"),
Self::UvLamp => f.write_str("UV lamp lighting is traced on the CPU only"),
}
}
}
#[must_use]
pub fn scene_decline_with_tools(
material: &GemMaterial,
geom: StoneGeometry<'_>,
fluorescence: &Fluorescence,
lighting: LightingPreset,
) -> Option<GpuRouteDecline> {
if !material.gpu_supported() {
Some(GpuRouteDecline::Material)
} else if geom.tools.len() > GPU_MAX_TOOL_PRIMITIVES {
Some(GpuRouteDecline::TooManyTools {
tools: geom.tools.len(),
})
} else if !fluorescence.is_empty() {
Some(GpuRouteDecline::Fluorescence)
} else if lighting.is_uv_lamp() {
Some(GpuRouteDecline::UvLamp)
} else {
None
}
}
#[must_use]
pub fn scene_routes_to_gpu_with_tools(
material: &GemMaterial,
geom: StoneGeometry<'_>,
fluorescence: &Fluorescence,
lighting: LightingPreset,
) -> bool {
scene_decline_with_tools(material, geom, fluorescence, lighting).is_none()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::{cuts::StandardGemCuts, tool::ToolPrimitive};
use crate::optics::{
absorption::AbsorptionBand,
fluorescence::{EmissionBand, FluorescentEmitter},
};
use glam::Vec3;
fn tools(n: usize) -> Vec<ToolPrimitive> {
(0..n)
.map(|i| {
let x = 0.01 * i as f32;
ToolPrimitive::ball(Vec3::new(x, 0.3, 0.0), 0.05)
})
.collect()
}
fn decline(
planes: &[crate::geometry::GpuFacetPlane],
tools: &[ToolPrimitive],
) -> Option<GpuRouteDecline> {
scene_decline_with_tools(
&GemMaterial::diamond(),
StoneGeometry { planes, tools },
Fluorescence::none(),
LightingPreset::Daylight,
)
}
#[test]
fn concave_stones_route_to_the_gpu_up_to_the_tool_limit() {
let planes = StandardGemCuts::standard_round_brilliant();
assert!(
GemMaterial::diamond().gpu_supported(),
"test premise: a supported material"
);
assert_eq!(decline(&planes, &[]), None);
assert_eq!(decline(&planes, &tools(1)), None);
assert_eq!(decline(&planes, &tools(GPU_MAX_TOOL_PRIMITIVES)), None);
let over = tools(GPU_MAX_TOOL_PRIMITIVES + 1);
assert_eq!(
decline(&planes, &over),
Some(GpuRouteDecline::TooManyTools {
tools: GPU_MAX_TOOL_PRIMITIVES + 1
})
);
assert!(scene_routes_to_gpu_with_tools(
&GemMaterial::diamond(),
StoneGeometry {
planes: &planes,
tools: &tools(3)
},
Fluorescence::none(),
LightingPreset::Daylight
));
assert!(!scene_routes_to_gpu_with_tools(
&GemMaterial::diamond(),
StoneGeometry {
planes: &planes,
tools: &over
},
Fluorescence::none(),
LightingPreset::Daylight
));
}
#[test]
fn the_decline_reason_names_the_limit() {
let text = GpuRouteDecline::TooManyTools { tools: 65 }.to_string();
assert!(text.contains("65"), "{text}");
assert!(
text.contains(&GPU_MAX_TOOL_PRIMITIVES.to_string()),
"{text}"
);
}
const _: () = assert!(GPU_MAX_TOOL_PRIMITIVES <= crate::geometry::tool::MAX_TOOL_PRIMITIVES);
#[test]
fn fluorescence_and_uv_lamps_still_decline_a_concave_stone() {
let planes = StandardGemCuts::standard_round_brilliant();
let t = tools(2);
let stone = StoneGeometry {
planes: &planes,
tools: &t,
};
let material = GemMaterial::diamond();
let emitter = FluorescentEmitter {
excitation: vec![AbsorptionBand::new(410.0, 20.0, 1.0)],
emission: vec![EmissionBand::new(694.0, 2.0, 1.0)],
quantum_yield: 0.9,
};
let fluorescent = Fluorescence::new(vec![emitter]);
assert_eq!(
scene_decline_with_tools(&material, stone, &fluorescent, LightingPreset::Daylight),
Some(GpuRouteDecline::Fluorescence)
);
for preset in LightingPreset::ALL {
assert_eq!(
scene_routes_to_gpu_with_tools(&material, stone, Fluorescence::none(), preset),
!preset.is_uv_lamp(),
"{preset:?}"
);
}
}
#[test]
fn the_legacy_routing_agrees_without_tools_and_still_declines_tools() {
let planes = StandardGemCuts::standard_round_brilliant();
let material = GemMaterial::diamond();
for preset in LightingPreset::ALL {
assert_eq!(
super::super::scene_routes_to_gpu(
&material,
StoneGeometry::planes_only(&planes),
Fluorescence::none(),
preset
),
scene_routes_to_gpu_with_tools(
&material,
StoneGeometry::planes_only(&planes),
Fluorescence::none(),
preset
),
"{preset:?}"
);
}
let t = tools(1);
assert!(!super::super::scene_routes_to_gpu(
&material,
StoneGeometry {
planes: &planes,
tools: &t
},
Fluorescence::none(),
LightingPreset::Daylight
));
}
}