use molframe_core::index::{AtomIndex, ChainIndex, EntityIndex, ResidueIndex};
use molframe_core::selection::AtomSelection;
use molframe_core::structure::{ResidueRef, Structure};
use std::collections::HashSet;
pub(crate) fn same_residue(
structure: &Structure,
universe: &AtomSelection,
selected: &AtomSelection,
) -> AtomSelection {
if selected.is_empty() {
return AtomSelection::Empty;
}
let residues = selected_residues(structure, selected);
if residues.is_empty() {
return AtomSelection::Empty;
}
select_residues(structure, universe, &residues)
}
pub(crate) fn same_chain(
structure: &Structure,
universe: &AtomSelection,
selected: &AtomSelection,
) -> AtomSelection {
if selected.is_empty() {
return AtomSelection::Empty;
}
let chains = selected_chains(structure, selected);
if chains.is_empty() {
return AtomSelection::Empty;
}
select_chains(structure, universe, &chains)
}
pub(crate) fn same_entity(
structure: &Structure,
universe: &AtomSelection,
selected: &AtomSelection,
) -> AtomSelection {
if selected.is_empty() {
return AtomSelection::Empty;
}
let entities = selected_entities(structure, selected);
if entities.is_empty() {
return AtomSelection::Empty;
}
select_entities(structure, universe, &entities)
}
fn selected_residues(structure: &Structure, selected: &AtomSelection) -> HashSet<ResidueIndex> {
let mut residues = HashSet::new();
for atom in selected {
let Some(residue) = structure
.atom(AtomIndex::new(atom))
.and_then(molframe_core::structure::AtomRef::residue)
else {
continue;
};
residues.insert(residue.index());
}
residues
}
fn selected_chains(structure: &Structure, selected: &AtomSelection) -> HashSet<ChainIndex> {
let mut chains = HashSet::new();
for atom in selected {
let Some(residue) = structure
.atom(AtomIndex::new(atom))
.and_then(molframe_core::structure::AtomRef::residue)
else {
continue;
};
let Some(chain) = structure
.data()
.topology
.chains
.containing(residue.index().get())
else {
continue;
};
chains.insert(chain);
}
chains
}
fn selected_entities(structure: &Structure, selected: &AtomSelection) -> HashSet<EntityIndex> {
let mut entities = HashSet::new();
for atom in selected {
let Some(residue) = structure
.atom(AtomIndex::new(atom))
.and_then(molframe_core::structure::AtomRef::residue)
else {
continue;
};
let Some(chain_index) = structure
.data()
.topology
.chains
.containing(residue.index().get())
else {
continue;
};
let Some(chain) = structure.chain(chain_index) else {
continue;
};
let Some(entity) = chain.entity() else {
continue;
};
entities.insert(entity);
}
entities
}
fn select_residues(
structure: &Structure,
universe: &AtomSelection,
residues: &HashSet<ResidueIndex>,
) -> AtomSelection {
let mut positions = Vec::new();
for chain in structure.data().chains() {
if chain.entity().is_none() {
continue;
}
for residue in chain.residues() {
if !residues.contains(&residue.index()) {
continue;
}
append_residue_atoms(residue, universe, &mut positions);
}
}
AtomSelection::from_sorted(positions)
}
fn select_chains(
structure: &Structure,
universe: &AtomSelection,
chains: &HashSet<ChainIndex>,
) -> AtomSelection {
let mut positions = Vec::new();
for chain in structure.data().chains() {
if chain.entity().is_none() || !chains.contains(&chain.index()) {
continue;
}
for residue in chain.residues() {
append_residue_atoms(residue, universe, &mut positions);
}
}
AtomSelection::from_sorted(positions)
}
fn select_entities(
structure: &Structure,
universe: &AtomSelection,
entities: &HashSet<EntityIndex>,
) -> AtomSelection {
let mut positions = Vec::new();
for chain in structure.data().chains() {
let Some(entity) = chain.entity() else {
continue;
};
if !entities.contains(&entity) {
continue;
}
for residue in chain.residues() {
append_residue_atoms(residue, universe, &mut positions);
}
}
AtomSelection::from_sorted(positions)
}
#[inline]
fn append_residue_atoms(
residue: ResidueRef<'_>,
universe: &AtomSelection,
positions: &mut Vec<u32>,
) {
positions.extend(
residue
.atoms()
.map(molframe_core::structure::AtomRef::index)
.map(molframe_core::index::AtomIndex::get)
.filter(|atom| universe.contains(*atom)),
);
}