use molgfx_core::{
AtomProperty, AtomPropertyHandle, ColorOverlay, ColorScheme, Scene, StructureHandle,
};
use molgfx_math::Rgba8;
#[derive(Clone, Copy)]
pub struct ColorContext<'a> {
scene: &'a Scene,
structure: StructureHandle,
}
impl std::fmt::Debug for ColorContext<'_> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ColorContext")
.field("structure", &self.structure)
.finish_non_exhaustive()
}
}
impl<'a> ColorContext<'a> {
#[must_use]
pub const fn new(scene: &'a Scene, structure: StructureHandle) -> Self {
Self { scene, structure }
}
#[must_use]
pub fn column(&self, handle: AtomPropertyHandle) -> Option<&'a AtomProperty> {
self.scene.property_for_structure(handle, self.structure)
}
#[must_use]
pub fn scheme(
&self,
base: ColorScheme,
overlay: Option<ColorOverlay>,
atom: usize,
) -> ColorScheme {
let Some(overlay) = overlay else {
return base;
};
let class = self
.column(overlay.classes())
.and_then(|classes| classes.values().get(atom));
match class.and_then(|class| overlay.scheme_for(*class)) {
Some(scheme) => scheme,
None => base,
}
}
#[must_use]
pub fn color(&self, scheme: ColorScheme, element: Rgba8, atom: usize) -> Rgba8 {
match scheme {
ColorScheme::Uniform(color) => color,
ColorScheme::ByCategory { property, palette } => {
let category = self
.column(property)
.and_then(|column| column.values().get(atom))
.and_then(|category| palette.color(*category));
let Some(color) = category else {
return element;
};
color
}
ColorScheme::ByProperty {
property,
ramp,
missing,
} => match self
.column(property)
.and_then(|column| column.values().get(atom))
{
Some(value) => ramp.sample(*value, missing),
None => missing,
},
_ => element,
}
}
}
#[cfg(test)]
#[path = "color_tests.rs"]
mod tests;