molgfx-geometry 0.3.2

GPU geometry generation: impostor packing, cartoons, labels, bounding hierarchies.
Documentation
use super::ColorContext;
use molgfx_core::{
    AtomProperty, AtomPropertyMeaning, CategoryPalette, ColorOverlay, ColorScheme,
    ScalarFieldSemantics, Scene,
};
use molgfx_math::Rgba8;
use std::sync::Arc;

fn scene_with(
    values: &[f32],
) -> (
    Scene,
    molgfx_core::StructureHandle,
    molgfx_core::AtomPropertyHandle,
) {
    let structure = match molframe::read_bytes(
        b"ATOM      1  N   ALA A   1       0.000   0.000   0.000  1.00  0.00           N\n\
ATOM      2  CA  ALA A   1       1.400   0.000   0.000  1.00  0.00           C\nEND\n"
            .to_vec(),
        Some("two.pdb"),
        &molframe::ReadOptions::new(),
    ) {
        Ok((structure, _)) => structure,
        Err(error) => panic!("fixture parses: {error:?}"),
    };
    let mut scene = match Scene::from_structure(&structure) {
        Ok(scene) => scene,
        Err(error) => panic!("scene builds: {error}"),
    };
    let Some((handle, _)) = scene.structures().next() else {
        panic!("one structure")
    };
    let column = match AtomProperty::new(
        handle,
        Arc::<str>::from("categories"),
        values.to_vec().into(),
        AtomPropertyMeaning::Generic,
        ScalarFieldSemantics::UncalibratedRank,
    ) {
        Ok(column) => column,
        Err(error) => panic!("column builds: {error}"),
    };
    let Ok(property) = scene.add_atom_property(column) else {
        panic!("column binds")
    };
    (scene, handle, property)
}

const ELEMENT: Rgba8 = Rgba8::opaque(200, 200, 200);

#[test]
fn a_category_takes_its_palette_colour_and_a_missing_one_keeps_the_element() {
    let (scene, structure, property) = scene_with(&[1.0, f32::NAN]);
    let context = ColorContext::new(&scene, structure);
    let scheme = ColorScheme::category(property, CategoryPalette::Dark2);
    assert_eq!(
        context.color(scheme, ELEMENT, 0),
        CategoryPalette::Dark2.color(1.0).unwrap_or(ELEMENT)
    );
    assert_eq!(context.color(scheme, ELEMENT, 1), ELEMENT);
}

#[test]
fn an_overlay_class_overrides_the_base_scheme_for_its_atoms_only() {
    let (scene, structure, classes) = scene_with(&[0.0, 1.0]);
    let context = ColorContext::new(&scene, structure);
    let red = Rgba8::opaque(255, 0, 0);
    let Ok(overlay) = ColorOverlay::new(classes, &[ColorScheme::Uniform(red)]) else {
        panic!("overlay builds")
    };
    let base = ColorScheme::ByElement;
    assert_eq!(context.scheme(base, Some(overlay), 0), base);
    assert_eq!(
        context.scheme(base, Some(overlay), 1),
        ColorScheme::Uniform(red)
    );
    assert_eq!(context.scheme(base, None, 1), base);
}