use omgkit_chem::{perceive_rings, ring_set};
use omgkit_core::{AtomFlags, BondOrder, MolBuilder};
use omgkit_io::smarts::{AtomProps, BondProps};
#[derive(Debug, Clone)]
pub struct MolProps {
pub atoms: Vec<AtomProps>,
pub bonds: Vec<BondProps>,
}
impl MolProps {
#[must_use]
pub fn compute(mol: &MolBuilder) -> Self {
let mut scratch = mol.clone();
let rings = perceive_rings(&mut scratch);
let cycles = ring_set(&scratch);
let n = mol.num_atoms();
let mut ring_count = vec![0u32; n];
let mut ring_bond_count = vec![0u32; n];
for ring in &cycles {
for &a in &ring.atoms {
ring_count[a as usize] += 1;
}
}
let mut bond_is_ring = vec![false; mol.num_bonds()];
for ring in &cycles {
for &b in &ring.bonds {
bond_is_ring[b as usize] = true;
}
}
for (bi, &in_ring) in bond_is_ring.iter().enumerate() {
if in_ring {
let b = mol.bonds()[bi];
ring_bond_count[b.begin as usize] += 1;
ring_bond_count[b.end as usize] += 1;
}
}
let atoms = (0..n)
.map(|i| {
let a = mol.atoms()[i];
AtomProps {
atomic_num: a.atomic_num,
aromatic: a.flags.contains(AtomFlags::AROMATIC),
charge: i32::from(a.formal_charge),
isotope: a.isotope,
degree: mol.degree(i as u32) as u32,
total_hs: u32::from(a.num_explicit_hs)
+ u32::from(a.num_implicit_hs)
+ neighbour_hydrogens(mol, i as u32),
implicit_hs: u32::from(a.num_implicit_hs),
valence: omgkit_core::valence::total_valence_nonstrict(mol, i as u32)
.max(0)
.unsigned_abs(),
ring_count: ring_count[i],
min_ring_size: u32::from(rings.atom_min_ring_size[i]),
ring_bonds: ring_bond_count[i],
chiral_tag: a.chiral_tag,
atom_map: a.atom_map,
}
})
.collect();
let bonds = (0..mol.num_bonds())
.map(|i| {
let b = mol.bonds()[i];
BondProps {
order: b.order,
in_ring: rings.bond_in_ring[i],
direction: b.direction,
dative_forward: true,
}
})
.collect();
Self { atoms, bonds }
}
}
fn neighbour_hydrogens(mol: &MolBuilder, atom: u32) -> u32 {
mol.neighbors(atom)
.filter(|&(other, _)| mol.atoms()[other as usize].atomic_num == 1)
.count() as u32
}
#[must_use]
pub fn dative_points_from(mol: &MolBuilder, bond: u32, from: u32) -> bool {
let b = mol.bonds()[bond as usize];
b.order != BondOrder::Dative || b.begin == from
}