use super::*;
use crate::scene_gpu::detail::FINEST_SURFACE_SPACING as SURFACE_GRID_TARGET_SPACING;
#[test]
fn surface_grid_uses_angstrom_spacing_until_the_dimension_cap() {
assert_eq!(axis_cells(10.0, 0.25), 41);
assert_eq!(axis_cells(100.0, 0.25), SURFACE_GRID_MAX_DIMENSION);
}
#[test]
fn a_coarser_target_spacing_shrinks_the_grid_without_changing_the_finest_spacing() {
use molgfx_core::{AtomSelection, Representation, RepresentationKind, Scene};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let representation = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::Surface,
);
let bounds = Aabb::from_points([Vec3::ZERO, Vec3::splat(10.0)]);
let realtime = RepresentationUniforms::for_spacing(&representation, bounds, None, 0.5);
let quality = RepresentationUniforms::for_spacing(
&representation,
bounds,
None,
SURFACE_GRID_TARGET_SPACING,
);
assert!((realtime.grid_cell[0] - 0.5).abs() < f32::EPSILON);
assert!((quality.grid_cell[0] - 0.25).abs() < f32::EPSILON);
assert_eq!(realtime.grid_size[0], 27);
assert_eq!(quality.grid_size[0], 53);
}
#[test]
fn surface_grid_always_has_an_interpolatable_cell() {
assert_eq!(axis_cells(0.0, 0.25), 2);
assert_eq!(axis_cells(0.1, 0.25), 2);
}
#[test]
fn grid_traversal_budget_is_bounded_by_the_longest_axis() {
assert_eq!(march_steps([2, 2, 2]), 2);
assert_eq!(march_steps([192, 121, 87]), 192);
}
#[test]
fn surface_presentation_is_packed_without_changing_the_field() {
use molgfx_core::{AtomSelection, Representation, RepresentationKind, Scene, SurfaceStyle};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let mut representation = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::Surface,
);
representation.params.surface_style = SurfaceStyle::Dots;
representation.params.surface_pattern_spacing = 2.25;
representation.params.surface_pattern_width_pixels = 1.75;
let uniforms = RepresentationUniforms::new(
&representation,
Aabb::from_points([Vec3::ZERO, Vec3::ONE]),
None,
);
assert_eq!(uniforms.options[3], SurfaceStyle::Dots as u32);
assert_eq!(uniforms.visual[1..3], [2.25, 1.75]);
}
#[test]
fn line_width_is_enabled_only_for_the_line_representation() {
use molgfx_core::{AtomSelection, Representation, RepresentationKind, Scene};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let mut lines = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::Lines,
);
lines.params.line_width_pixels = 2.25;
let bounds = Aabb::from_points([Vec3::ZERO, Vec3::ONE]);
let line_uniforms = RepresentationUniforms::new(&lines, bounds, None);
let capsules = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::BallAndStick,
);
let capsule_uniforms = RepresentationUniforms::new(&capsules, bounds, None);
assert_eq!(line_uniforms.visual[3].to_bits(), 2.25f32.to_bits());
assert_eq!(capsule_uniforms.visual[3].to_bits(), 0.0f32.to_bits());
}
#[test]
fn material_response_is_packed_once_per_representation() {
use molgfx_core::{AtomSelection, Representation, RepresentationKind, Scene};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let mut representation = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::BallAndStick,
);
representation.material.roughness = 0.73;
representation.material.specular = 0.19;
let uniforms = RepresentationUniforms::new(
&representation,
Aabb::from_points([Vec3::ZERO, Vec3::ONE]),
None,
);
assert_eq!(
uniforms.material.map(f32::to_bits),
[0.73, 0.19, 0.0, 0.0].map(f32::to_bits)
);
}
#[test]
fn principled_material_tag_and_metallicity_share_the_material_uniform() {
use molgfx_core::{AtomSelection, Material, Representation, RepresentationKind, Scene};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let mut representation = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::Spacefill,
);
representation.material = Material::principled(0.82);
representation.material.roughness = 0.31;
let uniforms = RepresentationUniforms::new(
&representation,
Aabb::from_points([Vec3::ZERO, Vec3::ONE]),
None,
);
assert_eq!(
uniforms.material.map(f32::to_bits),
[0.31, Material::default().specular_strength(), 1.0, 0.82].map(f32::to_bits)
);
}
#[test]
fn anisotropic_ribbon_tag_and_strength_share_the_material_uniform() {
use molgfx_core::{AtomSelection, Material, Representation, RepresentationKind, Scene};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let mut representation = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::Cartoon,
);
representation.material = Material::anisotropic_ribbon(0.68);
representation.material.roughness = 0.36;
let uniforms = RepresentationUniforms::new(
&representation,
Aabb::from_points([Vec3::ZERO, Vec3::ONE]),
None,
);
assert_eq!(
uniforms.material.map(f32::to_bits),
[0.36, Material::default().specular_strength(), 2.0, 0.68].map(f32::to_bits)
);
}
#[test]
fn putty_mapping_lowers_to_raw_reversible_shader_parameters() {
use molgfx_core::{AtomSelection, Representation, RepresentationKind, Scene};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let mut representation = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::Tube,
);
representation.params.tube_radius = 0.3;
representation.params.tube_radius_mapping =
molgfx_core::TubeRadiusMapping::b_factor([10.0, 50.0], [0.2, 0.8])
.unwrap_or_else(|error| panic!("mapping validates: {error}"));
let uniforms = ClipUniforms::new(&representation);
assert_eq!(uniforms.meta[3], 1);
assert_eq!(
uniforms.tube_mapping.map(f32::to_bits),
[10.0, 50.0, 0.2, 0.8].map(f32::to_bits)
);
assert_eq!(uniforms.tube[0].to_bits(), 0.3f32.to_bits());
}
#[test]
fn diffusion_material_tag_and_strength_share_the_material_uniform() {
use molgfx_core::{AtomSelection, Material, Representation, RepresentationKind, Scene};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let mut representation = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::Surface,
);
representation.material = Material::diffusion(0.64);
let uniforms = RepresentationUniforms::new(
&representation,
Aabb::from_points([Vec3::ZERO, Vec3::ONE]),
None,
);
assert_eq!(
uniforms.material.map(f32::to_bits),
[0.34, 0.5, 3.0, 0.64].map(f32::to_bits)
);
}
#[test]
fn van_der_waals_surface_never_inflates_atomic_radii() {
use molgfx_core::{AtomSelection, Representation, RepresentationKind, Scene, SurfaceKind};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let mut representation = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::Surface,
);
representation.params.surface_kind = SurfaceKind::VanDerWaals;
representation.params.probe_radius = 7.0;
representation.params.radius_scale = 1.75;
let uniforms = RepresentationUniforms::new(
&representation,
Aabb::from_points([Vec3::ZERO, Vec3::ONE]),
None,
);
assert_eq!(uniforms.surface[0].to_bits(), 0.0f32.to_bits());
assert_eq!(uniforms.visual[3].to_bits(), 1.75f32.to_bits());
assert_eq!(uniforms.options[0], SurfaceKind::VanDerWaals as u32);
}
#[test]
fn gaussian_surface_uniforms_encode_bounded_support_and_sigma() {
use molgfx_core::{AtomSelection, Representation, RepresentationKind, Scene, SurfaceKind};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let mut representation = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::Surface,
);
representation.params.surface_kind = SurfaceKind::Gaussian;
representation.params.gaussian_sigma = 0.75;
representation.params.isolevel = 0.35;
let uniforms = RepresentationUniforms::new(
&representation,
Aabb::from_points([Vec3::ZERO, Vec3::ONE]),
None,
);
assert_eq!(uniforms.surface[0].to_bits(), (4.0 * 0.75f32).to_bits());
assert_eq!(uniforms.surface[1].to_bits(), 0.35f32.to_bits());
assert_eq!(uniforms.surface[2].to_bits(), 0.75f32.to_bits());
assert_eq!(uniforms.visual[3].to_bits(), 0.0f32.to_bits());
assert_eq!(uniforms.options[0], SurfaceKind::Gaussian as u32);
}
#[test]
fn gaussian_surface_uniforms_keep_the_level_inside_compact_support() {
use molgfx_core::{AtomSelection, Representation, RepresentationKind, Scene, SurfaceKind};
let mut scene = Scene::new();
let selection = scene.add_selection(AtomSelection::All);
let mut representation = Representation::new(
molgfx_core::RepresentationTarget::Selection(selection),
RepresentationKind::Surface,
);
representation.params.surface_kind = SurfaceKind::Gaussian;
let bounds = Aabb::from_points([Vec3::ZERO, Vec3::ONE]);
let defaults = RepresentationUniforms::new(&representation, bounds, None);
assert_eq!(defaults.surface[1].to_bits(), 0.5f32.to_bits());
representation.params.isolevel = f32::MIN_POSITIVE;
let bounded = RepresentationUniforms::new(&representation, bounds, None);
assert_eq!(bounded.surface[1].to_bits(), 0.001f32.to_bits());
}
#[test]
fn scalar_overlay_uniforms_preserve_domain_dimensions_and_contour_units() {
use molgfx_core::{
AtomSelection, Representation, RepresentationKind, RepresentationTarget, ScalarContours,
ScalarRamp, ScalarVolume, Scene, SurfaceScalarOverlay,
};
use std::sync::Arc;
let mut scene = Scene::new();
let volume = match ScalarVolume::from_spacing(
[3, 4, 5],
Vec3::new(-2.0, -3.0, -4.0),
Vec3::splat(0.5),
Arc::from(vec![0.0; 60]),
) {
Ok(volume) => volume,
Err(error) => panic!("volume validates: {error}"),
};
let volume = scene.add_volume(volume);
let selection = scene.add_selection(AtomSelection::All);
let mut representation = Representation::new(
RepresentationTarget::Selection(selection),
RepresentationKind::Surface,
);
let contours = match ScalarContours::new(0.5, 1.25) {
Ok(contours) => contours,
Err(error) => panic!("contours validate: {error}"),
};
representation.surface_scalar = Some(SurfaceScalarOverlay {
contours: Some(contours),
..SurfaceScalarOverlay::new(volume, ScalarRamp::diverging(2.0))
});
let uniforms = RepresentationUniforms::new(
&representation,
Aabb::from_points([Vec3::ZERO, Vec3::ONE]),
scene.volume(volume),
);
assert_eq!(uniforms.overlay_size, [3, 4, 5, 1]);
assert!((uniforms.overlay_contour[0] - 2.0).abs() < f32::EPSILON);
let [first, scale] = uniforms.overlay_ramp.domain_probe();
assert!((first + 2.0).abs() < f32::EPSILON);
assert!((scale - 255.0 / 4.0).abs() < 1.0e-4);
assert!((uniforms.overlay_visual[0] - 1.25).abs() < f32::EPSILON);
}
#[test]
fn projection_parameters_precompute_exact_sphere_frustum_factors() {
let perspective = Projection::Perspective {
fov_y: 1.0,
aspect: 1.5,
near: 0.1,
far: 100.0,
};
let perspective_matrix = perspective.matrix();
let packed = projection_parameters(perspective, perspective_matrix);
assert_eq!(packed[0].to_bits(), 0.0_f32.to_bits());
assert_eq!(
packed[1].to_bits(),
perspective_matrix.x_axis.x.abs().hypot(1.0).to_bits()
);
let orthographic = Projection::Orthographic {
height: 20.0,
aspect: 1.5,
near: 0.1,
far: 100.0,
};
let orthographic_matrix = orthographic.matrix();
let packed = projection_parameters(orthographic, orthographic_matrix);
assert_eq!(packed[0].to_bits(), 1.0_f32.to_bits());
assert_eq!(
packed[1].to_bits(),
orthographic_matrix.x_axis.x.abs().to_bits()
);
}