use crate::{
CoreError, EntityKind, EntityRef, LicoriceTemplate, LigandPose, LigandPoseBatch,
LigandPoseBatchHandle, Scene, StructureHandle,
};
impl Scene {
pub fn add_licorice_poses(
&mut self,
owner: StructureHandle,
template: LicoriceTemplate,
poses: Vec<LigandPose>,
) -> Result<Option<LigandPoseBatchHandle>, CoreError> {
if self.structure(owner).is_none() {
return Err(CoreError::StaleHandle);
}
let Some(instance_count) = template.instances_per_pose().checked_mul(poses.len()) else {
return Err(invalid("licorice pose batch is too large"));
};
if u32::try_from(instance_count).is_err() {
return Err(invalid("licorice pose batch exceeds the GPU draw range"));
}
if poses
.iter()
.any(|pose| !pose.transforms_finitely(template.bound_radius()))
{
return Err(invalid("licorice pose transform overflows model space"));
}
if instance_count == 0 {
return Ok(None);
}
let handle = LigandPoseBatchHandle(
self.ligand_pose_batches
.insert(LigandPoseBatch::new(owner, template, poses)),
);
if handle.row() > crate::EntityId::MAX_INDEX {
let _ = self.ligand_pose_batches.remove(handle.0);
return Err(invalid("too many licorice pose batches for picking"));
}
self.primitive_revision = self.primitive_revision.wrapping_add(1);
Ok(Some(handle))
}
#[must_use]
pub fn ligand_pose_batch(&self, handle: LigandPoseBatchHandle) -> Option<&LigandPoseBatch> {
self.ligand_pose_batches.get(handle.0)
}
pub fn hide_ligand_pose_batch(&mut self, handle: LigandPoseBatchHandle) -> bool {
self.set_ligand_pose_batch_visibility(handle, false)
}
pub fn show_ligand_pose_batch(&mut self, handle: LigandPoseBatchHandle) -> bool {
self.set_ligand_pose_batch_visibility(handle, true)
}
pub fn remove_ligand_pose_batch(
&mut self,
handle: LigandPoseBatchHandle,
) -> Option<LigandPoseBatch> {
let removed = self.ligand_pose_batches.remove(handle.0);
if removed.is_some() {
self.primitive_revision = self.primitive_revision.wrapping_add(1);
}
removed
}
#[doc(hidden)]
pub fn ligand_pose_batches(
&self,
) -> impl Iterator<Item = (LigandPoseBatchHandle, &LigandPoseBatch)> + '_ {
self.ligand_pose_batches
.iter()
.map(|(raw, batch)| (LigandPoseBatchHandle(raw), batch))
}
#[doc(hidden)]
#[must_use]
pub const fn ligand_pose_batch_entity_row(handle: LigandPoseBatchHandle) -> u32 {
handle.row()
}
#[must_use]
pub fn ligand_pose_batch_for_entity(&self, entity: EntityRef) -> Option<&LigandPoseBatch> {
if entity.kind != EntityKind::LigandPoseBatch {
return None;
}
let (_, batch) = self.ligand_pose_batches.get_index(entity.index)?;
(batch.owner() == entity.structure).then_some(batch)
}
fn set_ligand_pose_batch_visibility(
&mut self,
handle: LigandPoseBatchHandle,
visible: bool,
) -> bool {
let Some(batch) = self.ligand_pose_batches.get_mut(handle.0) else {
return false;
};
if !batch.set_visible(visible) {
return false;
}
self.primitive_revision = self.primitive_revision.wrapping_add(1);
true
}
}
const fn invalid(reason: &'static str) -> CoreError {
CoreError::InvalidPrimitive { reason }
}
#[cfg(test)]
#[path = "ligand_pose_batches_tests.rs"]
mod tests;