use crate::SegmentedVolume;
use crate::atoms::AtomTable;
use crate::density::ScalarVolume;
use crate::error::CoreError;
use crate::handle::{RepresentationHandle, SlotMap, StructureHandle, VolumeHandle};
use crate::placed::PlacedStructure;
use crate::representation::Representation;
use crate::scene::StoredSelection;
use crate::{Column, DatasetId, StructureAsset};
#[path = "identity.rs"]
mod identity;
#[path = "representation_state.rs"]
mod representation_state;
#[path = "world_bound.rs"]
mod world_bound;
use identity::SceneIdentity;
#[cfg(test)]
#[path = "asset_tests.rs"]
mod asset_tests;
#[cfg(test)]
#[path = "representation_revision_tests.rs"]
mod representation_revision_tests;
#[cfg(test)]
#[path = "state_tests.rs"]
mod tests;
#[derive(Debug, Default)]
pub struct Scene {
identity: SceneIdentity,
pub(crate) structures: SlotMap<PlacedStructure>,
pub(crate) interaction_states: std::collections::BTreeMap<StructureHandle, Column<u32>>,
pub(crate) selections: SlotMap<StoredSelection>,
pub(crate) representations: SlotMap<StoredRepresentation>,
pub(crate) volumes: SlotMap<StoredVolume>,
pub(crate) segmentations: SlotMap<StoredSegmentation>,
pub(crate) interactions: SlotMap<crate::InteractionEdge>,
pub(crate) meshes: SlotMap<crate::Mesh>,
pub(crate) mesh_instances: SlotMap<crate::MeshInstance>,
pub(crate) overlays: SlotMap<crate::ScreenOverlay>,
pub(crate) guides: SlotMap<crate::Guide>,
pub(crate) labels: SlotMap<crate::annotation::LabelObject>,
pub(crate) properties: SlotMap<StoredAtomProperty>,
pub(crate) primitive: SlotMap<crate::Primitive>,
pub(crate) ligand_pose_batches: SlotMap<crate::LigandPoseBatch>,
pub(crate) point_batches: SlotMap<crate::structure::PointBatch>,
pub(crate) instance_batches: SlotMap<crate::structure::InstanceBatch>,
pub(crate) relation_batches: SlotMap<crate::representation::RelationBatch>,
pub(crate) attributes: SlotMap<StoredAttribute>,
pub(crate) ensembles: SlotMap<crate::Ensemble>,
pub(crate) domain_visuals:
std::collections::BTreeMap<crate::RowDomain, crate::VisualDescriptor>,
pub(crate) instance_timeline:
std::collections::BTreeMap<crate::InstanceBatchHandle, TemporalInstances>,
pub(crate) point_timeline: std::collections::BTreeMap<crate::PointBatchHandle, TemporalPoints>,
pub(crate) attribute_timeline:
std::collections::BTreeMap<crate::AttributeHandle, TemporalAttribute>,
structure_revision: u64,
pub(crate) interaction_state_revision: u64,
pub(crate) representation_revision: u64,
pub(crate) representation_membership_revision: u64,
pub(crate) mesh_revision: u64,
pub(crate) overlay_revision: u64,
pub(crate) volume_revision: u64,
pub(crate) segmentation_revision: u64,
pub(crate) interaction_revision: u64,
pub(crate) guide_revision: u64,
pub(crate) label_revision: u64,
pub(crate) property_revision: u64,
pub(crate) primitive_revision: u64,
pub(crate) generic_batch_revision: u64,
pub(crate) attribute_revision: u64,
pub(crate) domain_visual_revision: u64,
pub(crate) ensemble_revision: u64,
pub(crate) presentation_time_seconds: f32,
pub(crate) presentation_revision: u64,
pub(crate) generic_timeline_binding_revision: u64,
pub(crate) spatial_traversal: Vec<u32>,
pub(crate) spatial_candidates: Vec<u32>,
pub(crate) spatial_result: roaring::RoaringBitmap,
next_dataset: u64,
}
#[derive(Clone, Debug)]
pub(crate) struct TemporalInstances {
pub(crate) start: std::sync::Arc<[crate::RigidInstance]>,
pub(crate) end: std::sync::Arc<[crate::RigidInstance]>,
pub(crate) alpha: f32,
pub(crate) revision: u64,
}
#[derive(Clone, Debug)]
pub(crate) struct TemporalPoints {
pub(crate) start: std::sync::Arc<[[f32; 3]]>,
pub(crate) end: std::sync::Arc<[[f32; 3]]>,
pub(crate) alpha: f32,
pub(crate) revision: u64,
}
#[derive(Clone, Debug)]
pub(crate) struct TemporalAttribute {
pub(crate) start: crate::AttributeValues,
pub(crate) end: crate::AttributeValues,
pub(crate) alpha: f32,
pub(crate) revision: u64,
}
#[derive(Clone, Debug)]
pub(crate) struct StoredRepresentation {
pub(crate) value: Representation,
pub(crate) revision: u64,
}
#[derive(Clone, Debug)]
pub(crate) struct StoredVolume {
pub(crate) value: Option<ScalarVolume>,
pub(crate) occupancy: Option<BoundOccupancy>,
pub(crate) revision: u64,
}
#[derive(Clone, Debug)]
pub(crate) struct BoundOccupancy {
pub(crate) stream: crate::OccupancyStream,
pub(crate) structure: StructureHandle,
pub(crate) atom_rows: std::sync::Arc<[u32]>,
}
impl StoredVolume {
pub(crate) fn range(&self) -> [f32; 2] {
self.value.as_ref().map_or_else(
|| {
self.occupancy
.as_ref()
.map_or([0.0, 1.0], |value| [0.0, value.stream.maximum()])
},
ScalarVolume::range,
)
}
pub(crate) fn world_aabb(&self, scene: &Scene) -> molgfx_math::Aabb {
if let Some(value) = &self.value {
return value.world_aabb();
}
let Some(occupancy) = &self.occupancy else {
return molgfx_math::Aabb::EMPTY;
};
scene.structure(occupancy.structure).map_or_else(
|| molgfx_math::Aabb::EMPTY,
|placed| {
occupancy
.stream
.model_aabb()
.transform(&placed.model_to_world)
},
)
}
}
#[derive(Clone, Debug)]
pub(crate) struct StoredSegmentation {
pub(crate) value: SegmentedVolume,
pub(crate) revision: u64,
}
#[derive(Clone, Debug)]
pub(crate) struct StoredAtomProperty {
pub(crate) value: crate::AtomProperty,
pub(crate) revision: u64,
}
#[derive(Clone, Debug)]
pub(crate) struct StoredAttribute {
pub(crate) value: crate::representation::AttributeColumn,
pub(crate) revision: u64,
pub(crate) dirty_rows: std::ops::Range<u32>,
}
impl Scene {
#[must_use]
pub fn new() -> Self {
Self {
next_dataset: 1,
..Self::default()
}
}
pub fn from_structure(structure: &molframe::Structure) -> Result<Self, CoreError> {
let asset = StructureAsset::new(DatasetId::LEGACY, structure)
.map_err(|error| structure_asset_error(&error))?;
Ok(Self::from_asset(&asset))
}
pub fn from_source(source: crate::MolecularSource) -> Result<Self, CoreError> {
let asset = StructureAsset::from_source(DatasetId::new(1), source)
.map_err(|error| structure_asset_error(&error))?;
Ok(Self::from_asset(&asset))
}
#[must_use]
pub fn from_asset(asset: &StructureAsset) -> Self {
let mut scene = Self::new();
scene.add_asset(asset);
scene
}
pub fn add_structure(
&mut self,
structure: &molframe::Structure,
) -> Result<StructureHandle, CoreError> {
let dataset = self.allocate_dataset();
let asset = StructureAsset::new(dataset, structure)
.map_err(|error| structure_asset_error(&error))?;
Ok(self.add_asset(&asset))
}
pub fn add_source(
&mut self,
source: crate::MolecularSource,
) -> Result<StructureHandle, CoreError> {
let dataset = self.allocate_dataset();
let asset = StructureAsset::from_source(dataset, source)
.map_err(|error| structure_asset_error(&error))?;
Ok(self.add_asset(&asset))
}
pub fn add_asset(&mut self, asset: &StructureAsset) -> StructureHandle {
let placed = PlacedStructure::from_asset(asset);
self.reserve_dataset(asset.dataset_id());
let atom_count = placed.atoms.len() as usize;
self.structure_revision = self.structure_revision.wrapping_add(1);
let handle = StructureHandle(self.structures.insert(placed));
let _ = self
.interaction_states
.insert(handle, Column::new(vec![0; atom_count]));
handle
}
pub fn remove_structure(&mut self, handle: StructureHandle) -> Option<PlacedStructure> {
let removed = self.structures.remove(handle.0);
if removed.is_some() {
let _ = self.interaction_states.remove(&handle);
self.structure_revision = self.structure_revision.wrapping_add(1);
self.interaction_state_revision = self.interaction_state_revision.wrapping_add(1);
}
removed
}
#[must_use]
pub fn structure(&self, handle: StructureHandle) -> Option<&PlacedStructure> {
self.structures.get(handle.0)
}
pub fn structure_mut(&mut self, handle: StructureHandle) -> Option<&mut PlacedStructure> {
self.structures.get_mut(handle.0)
}
pub fn apply_secondary_structure(
&mut self,
handle: StructureHandle,
records: &[(molframe::ResidueIndex, crate::SecondaryStructure)],
) -> Result<(), CoreError> {
let placed = self.structure_mut(handle).ok_or(CoreError::StaleHandle)?;
let values = placed.secondary_structure.values_mut();
values.fill(crate::SecondaryStructure::Unknown);
for &(residue, kind) in records {
let index = residue.as_usize();
if let Some(value) = values.get_mut(index) {
*value = kind;
}
}
Ok(())
}
pub fn structures(&self) -> impl Iterator<Item = (StructureHandle, &PlacedStructure)> + '_ {
self.structures.iter().map(|(h, s)| (StructureHandle(h), s))
}
#[must_use]
pub fn atom_count(&self) -> u64 {
self.structures
.iter()
.map(|(_, structure)| u64::from(structure.atoms.len()))
.sum()
}
#[must_use]
pub fn first_atoms(&self) -> Option<&AtomTable> {
self.structures
.iter()
.next()
.map(|(_, structure)| structure.atoms.as_ref())
}
pub fn add_volume(&mut self, volume: ScalarVolume) -> VolumeHandle {
self.volume_revision = self.volume_revision.wrapping_add(1);
VolumeHandle(self.volumes.insert(StoredVolume {
value: Some(volume),
occupancy: None,
revision: 0,
}))
}
#[must_use]
pub fn volume(&self, handle: VolumeHandle) -> Option<&ScalarVolume> {
self.volumes
.get(handle.0)
.and_then(|stored| stored.value.as_ref())
}
pub fn replace_volume(
&mut self,
handle: VolumeHandle,
volume: ScalarVolume,
) -> Result<(), CoreError> {
let stored = self
.volumes
.get_mut(handle.0)
.ok_or(CoreError::StaleHandle)?;
stored.value = Some(volume);
stored.occupancy = None;
stored.revision = stored.revision.wrapping_add(1);
self.volume_revision = self.volume_revision.wrapping_add(1);
Ok(())
}
pub fn remove_volume(&mut self, handle: VolumeHandle) -> Option<ScalarVolume> {
let removed = self.volumes.remove(handle.0);
if removed.is_some() {
self.volume_revision = self.volume_revision.wrapping_add(1);
}
removed.and_then(|stored| stored.value)
}
}
fn structure_asset_error(error: &crate::DatasetError) -> CoreError {
CoreError::StructureRead {
summary: error.to_string(),
}
}