use crate::{AtomSelection, CoreError};
use roaring::RoaringBitmap;
use std::fmt;
use std::sync::Arc;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct SourceAtom {
pub element: u8,
pub residue: u32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct SourceBond {
pub atoms: [u32; 2],
pub aromatic: bool,
}
#[derive(Clone, Debug, Default)]
pub struct SourceTopology {
pub atoms: Arc<[SourceAtom]>,
pub residue_atom_start: Arc<[u32]>,
pub chain_residue_start: Arc<[u32]>,
pub model_chain_start: Arc<[u32]>,
pub bonds: Arc<[SourceBond]>,
}
pub trait MolecularProvider: fmt::Debug + Send + Sync {
fn identity(&self) -> u64;
fn coordinates(&self) -> &[[f32; 3]];
fn coordinate_revision(&self) -> u64;
fn topology(&self) -> &SourceTopology;
fn select(&self, source: &str) -> Result<AtomSelection, CoreError>;
fn select_compiled(&self, query: &molframe::Query) -> Result<AtomSelection, CoreError> {
self.select(query.source())
}
fn molframe(&self) -> Option<&molframe::Structure> {
None
}
}
#[derive(Clone)]
pub struct MolecularSource(Arc<dyn MolecularProvider>);
impl fmt::Debug for MolecularSource {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("MolecularSource")
.field("identity", &self.identity())
.field("atoms", &self.coordinates().len())
.finish_non_exhaustive()
}
}
impl MolecularSource {
#[must_use]
pub fn new(provider: impl MolecularProvider + 'static) -> Self {
Self(Arc::new(provider))
}
#[must_use]
pub fn from_molframe(structure: &molframe::Structure) -> Self {
Self::new(MolframeProvider::new(structure))
}
#[must_use]
pub fn identity(&self) -> u64 {
self.0.identity()
}
#[must_use]
pub fn coordinates(&self) -> &[[f32; 3]] {
self.0.coordinates()
}
#[must_use]
pub fn coordinate_revision(&self) -> u64 {
self.0.coordinate_revision()
}
#[must_use]
pub fn topology(&self) -> &SourceTopology {
self.0.topology()
}
pub fn select(&self, source: &str) -> Result<AtomSelection, CoreError> {
self.0.select(source)
}
pub fn select_compiled(&self, query: &molframe::Query) -> Result<AtomSelection, CoreError> {
self.0.select_compiled(query)
}
#[must_use]
pub fn molframe(&self) -> Option<&molframe::Structure> {
self.0.molframe()
}
#[must_use]
pub fn shares_storage_with(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
#[derive(Debug)]
struct MolframeProvider {
structure: molframe::Structure,
identity: u64,
topology: SourceTopology,
}
impl MolframeProvider {
fn new(structure: &molframe::Structure) -> Self {
Self {
structure: structure.clone(),
identity: structure.coordinates().as_ptr() as usize as u64,
topology: topology(structure),
}
}
}
impl MolecularProvider for MolframeProvider {
fn identity(&self) -> u64 {
self.identity
}
fn coordinates(&self) -> &[[f32; 3]] {
self.structure.coordinates()
}
fn coordinate_revision(&self) -> u64 {
self.structure.engine().generation().get()
}
fn topology(&self) -> &SourceTopology {
&self.topology
}
fn select(&self, source: &str) -> Result<AtomSelection, CoreError> {
let query = molframe::Query::compile(source).map_err(|_| CoreError::InvalidSelection {
reason: "MolFrame query evaluation failed",
})?;
self.select_compiled_query(&query)
}
fn select_compiled(&self, query: &molframe::Query) -> Result<AtomSelection, CoreError> {
self.select_compiled_query(query)
}
fn molframe(&self) -> Option<&molframe::Structure> {
Some(&self.structure)
}
}
impl MolframeProvider {
fn select_compiled_query(&self, query: &molframe::Query) -> Result<AtomSelection, CoreError> {
let selection = molframe::QueryStructure::select_query(
&self.structure,
query,
&molframe::AnalysisPolicy::default(),
)
.map_err(|_| CoreError::InvalidSelection {
reason: "MolFrame query evaluation failed",
})?;
let selected = selection.selection.len();
Ok(adaptive(selected, self.structure.atom_count(), || {
selection.selection.iter().collect::<RoaringBitmap>()
}))
}
}
fn topology(structure: &molframe::Structure) -> SourceTopology {
let data = structure.engine().data();
let atoms = data
.atoms()
.map(|atom| SourceAtom {
element: atom.element().map_or(0, molframe::Element::atomic_number),
residue: atom.residue().map_or(0, |residue| residue.index().get()),
})
.collect::<Vec<_>>();
let residue_atom_start = offsets(
data.residues()
.map(|residue| residue.atoms().map(|atom| atom.index().get())),
);
let chain_residue_start = offsets(
data.chains()
.map(|chain| chain.residues().map(|residue| residue.index().get())),
);
let model_chain_start = offsets(
data.models()
.map(|model| model.chains().map(|chain| chain.index().get())),
);
let bonds = structure
.bonds()
.iter()
.map(|bond| SourceBond {
atoms: [bond.atom_a.get(), bond.atom_b.get()],
aromatic: bond.order == molframe::BondOrder::Aromatic,
})
.collect::<Vec<_>>();
SourceTopology {
atoms: atoms.into(),
residue_atom_start: residue_atom_start.into(),
chain_residue_start: chain_residue_start.into(),
model_chain_start: model_chain_start.into(),
bonds: bonds.into(),
}
}
fn offsets<I, R>(rows: I) -> Vec<u32>
where
I: Iterator<Item = R>,
R: Iterator<Item = u32>,
{
let mut starts = Vec::new();
let mut end = 0;
for row in rows {
let mut row = row.peekable();
let mut start = end;
if let Some(value) = row.peek().copied() {
start = value;
}
starts.push(start);
end = row.last().map_or(start, |value| value.saturating_add(1));
}
starts.push(end);
starts
}
fn adaptive(selected: u64, table_len: u32, rows: impl FnOnce() -> RoaringBitmap) -> AtomSelection {
if selected == 0 {
return AtomSelection::Empty;
}
if selected == u64::from(table_len) {
return AtomSelection::All;
}
AtomSelection::Roaring(rows())
}