From edaa7fd166c540a6c93efaff7b884952821007b8 Mon Sep 17 00:00:00 2001 From: zachcp Date: Wed, 24 Jul 2024 21:27:04 -0400 Subject: [PATCH] Integrate Representation to Parser (#14) * parse booleans directly * update bitmask --- src/pymolparsing/mod.rs | 1 + src/pymolparsing/parsing.rs | 63 ++++++++++++++++++------------ src/pymolparsing/psedata.rs | 1 - src/pymolparsing/representation.rs | 52 ++++++++++++++++-------- 4 files changed, 76 insertions(+), 41 deletions(-) diff --git a/src/pymolparsing/mod.rs b/src/pymolparsing/mod.rs index c33fec9..6be1345 100644 --- a/src/pymolparsing/mod.rs +++ b/src/pymolparsing/mod.rs @@ -3,3 +3,4 @@ pub mod colors; pub mod parsing; pub mod psedata; +pub mod representation; diff --git a/src/pymolparsing/parsing.rs b/src/pymolparsing/parsing.rs index dc3bc1f..c4182b7 100644 --- a/src/pymolparsing/parsing.rs +++ b/src/pymolparsing/parsing.rs @@ -47,6 +47,7 @@ //! m_tmpids[m_iter.getAtm()] = m_id; use crate::molviewspec::nodes::{ComponentExpression, ComponentSelector}; use crate::pymolparsing::colors::{Color, COLOR_SET}; +use crate::pymolparsing::representation::RepType; use itertools::Itertools; use pdbtbx::{self, Residue, PDB}; @@ -134,7 +135,8 @@ pub struct AtomInfo { pub vdw: f64, pub partial_charge: f64, pub formal_charge: i32, - pub hetatm: i8, + #[serde(deserialize_with = "int_to_bool")] + pub is_hetatm: bool, pub vis_rep: i32, pub color: i32, pub id: i32, @@ -150,19 +152,24 @@ pub struct AtomInfo { // Should be equivalent to RDKit::Atom::getTotalDegree() and // OBAtom::GetTotalDegree(). pub valence: i32, // - pub is_masked: i8, - pub is_protected: i8, + #[serde(deserialize_with = "int_to_bool")] + pub is_masked: bool, + #[serde(deserialize_with = "int_to_bool")] + pub is_protected: bool, pub protons: i32, // atomic number pub unique_id: i64, pub stereo: i8, pub discrete_state: i32, pub elec_radius: f64, pub rank: i32, - pub hb_donor: i8, - pub hb_acceptor: i8, + #[serde(deserialize_with = "int_to_bool")] + pub hb_donor: bool, + #[serde(deserialize_with = "int_to_bool")] + pub hb_acceptor: bool, // color and secondary structure pub atomic_color: i32, - pub has_setting: i8, + #[serde(deserialize_with = "int_to_bool")] + pub has_setting: bool, pub anisou_1: f32, pub anisou_2: f32, pub anisou_3: f32, @@ -173,13 +180,6 @@ pub struct AtomInfo { } impl AtomInfo { - pub fn is_hetero(&self) -> bool { - match self.hetatm { - 1 => true, - 0 => false, - _ => false, - } - } // https://github.com/schrodinger/pymol-open-source/blob/03d7a7fcf0bd95cd93d710a1268dbace2ed77765/layer2/AtomInfo.h#L319 pub fn is_metal() { unimplemented!() @@ -196,16 +196,16 @@ impl AtomInfo { pub fn to_pdbtbx_atom(&self) -> pdbtbx::Atom { let formal_charge = self.formal_charge as isize; let atom = pdbtbx::Atom::new( - self.is_hetero(), // hetero - 0, // serial_number - &self.name, // atom_name - 0.0, // x Todo - 0.0, // y Todo - 0.0, // z Todo - 0.0, // occupancy? Todo - self.b, // b-factor - &self.elem, // element - formal_charge, // charge: todo: is this the right charge? + self.is_hetatm, // hetero + 0, // serial_number + &self.name, // atom_name + 0.0, // x Todo + 0.0, // y Todo + 0.0, // z Todo + 0.0, // occupancy? Todo + self.b, // b-factor + &self.elem, // element + formal_charge, // charge: todo: is this the right charge? ); atom.unwrap() } @@ -296,6 +296,9 @@ pub enum ObjectType { } /// Named colors. +/// +/// See also [`crate::pymolparsing::colors::COLOR_SET`]. +/// #[derive(Debug, Serialize_repr, Deserialize_repr, PartialEq, Clone)] #[repr(i32)] pub enum AutoColor { @@ -459,7 +462,7 @@ impl PyObjectMolecule { let serial_number = atom_info.id as usize; let atom = pdbtbx::Atom::new( - atom_info.is_hetero(), // hetero + atom_info.is_hetatm, // hetero serial_number, // serial_number: Note: I am not sure this is correct just yet. atom_info.name.clone(), // atom_name x_coord.into(), // x @@ -1627,6 +1630,18 @@ pub struct Settings { pub value: CustomValue, } +fn int_to_bool<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + use serde::de::Error; + match u8::deserialize(deserializer)? { + 0 => Ok(false), + 1 => Ok(true), + other => Err(Error::custom(format!("Invalid boolean value: {}", other))), + } +} + // Todo: // // struct PyObjectGadget {} diff --git a/src/pymolparsing/psedata.rs b/src/pymolparsing/psedata.rs index 03b04e8..54648d8 100644 --- a/src/pymolparsing/psedata.rs +++ b/src/pymolparsing/psedata.rs @@ -66,7 +66,6 @@ pub struct PSEData { unique_settings: Vec, selector_secrets: Vec, editor: Vec, - // pub view: [f32; 25], pub view: SceneView, view_dict: HashMap, #[serde(with = "serde_bytes")] diff --git a/src/pymolparsing/representation.rs b/src/pymolparsing/representation.rs index 01ffb70..e87b9d8 100644 --- a/src/pymolparsing/representation.rs +++ b/src/pymolparsing/representation.rs @@ -9,7 +9,7 @@ //! # Examples //! //! ``` -//! use crate::RepBitmask; +//! use pseutils::pymolparsing::representation::RepBitmask; //! //! let mut reps = RepBitmask::new(); //! reps.insert(RepBitmask::CYL | RepBitmask::SPHERE); @@ -18,10 +18,11 @@ //! assert!(reps.contains(RepBitmask::SPHERE)); //! ``` use bitflags::bitflags; +use serde::{Deserialize, Serialize}; // First, define an enum for all representation types #[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum RepType { +pub enum RepType { Cyl, Sphere, Surface, @@ -46,7 +47,7 @@ enum RepType { } bitflags! { - struct RepBitmask: u32 { + pub struct RepBitmask: u32 { const CYL = 1 << 0; const SPHERE = 1 << 1; const SURFACE = 1 << 2; @@ -69,13 +70,13 @@ bitflags! { const ELLIPSOID = 1 << 19; const VOLUME = 1 << 20; - const REPS_ATOM_MASK = Self::CYL.bits | Self::SPHERE.bits | Self::SURFACE.bits | - Self::LABEL.bits | Self::NONBONDED_SPHERE.bits | Self::CARTOON.bits | Self::RIBBON.bits | - Self::LINE.bits | Self::MESH.bits | Self::DOT.bits | Self::NONBONDED.bits | Self::ELLIPSOID.bits; + const REPS_ATOM_MASK = Self::CYL.bits() | Self::SPHERE.bits() | Self::SURFACE.bits() | + Self::LABEL.bits() | Self::NONBONDED_SPHERE.bits() | Self::CARTOON.bits() | Self::RIBBON.bits() | + Self::LINE.bits() | Self::MESH.bits() | Self::DOT.bits() | Self::NONBONDED.bits() | Self::ELLIPSOID.bits(); - const REPS_OBJECT_MASK = Self::SURFACE.bits | Self::MESH.bits | Self::DOT.bits | - Self::CELL.bits | Self::CGO.bits | Self::CALLBACK.bits | Self::EXTENT.bits | Self::SLICE.bits | - Self::ANGLE.bits | Self::DIHEDRAL.bits | Self::VOLUME.bits | Self::DASH.bits; + const REPS_OBJECT_MASK = Self::SURFACE.bits() | Self::MESH.bits() | Self::DOT.bits() | + Self::CELL.bits() | Self::CGO.bits() | Self::CALLBACK.bits() | Self::EXTENT.bits() | Self::SLICE.bits() | + Self::ANGLE.bits() | Self::DIHEDRAL.bits() | Self::VOLUME.bits() | Self::DASH.bits(); } } @@ -90,6 +91,27 @@ impl RepBitmask { } } +// Custom Serde deserialization +impl<'de> Deserialize<'de> for RepBitmask { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let bits = u32::deserialize(deserializer)?; + Ok(RepBitmask::from_bits_truncate(bits)) + } +} + +// Custom Serde serialization +impl Serialize for RepBitmask { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_u32(self.bits()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -120,7 +142,10 @@ mod tests { // Check that the correct flags are set assert!(bitmask.contains(RepBitmask::CYL)); assert!(bitmask.contains(RepBitmask::SURFACE)); + } + #[test] + fn test_bitmask_from_int_02() { // I see this value in the example.pse // Binary: 111110110001111111 let int_value = 2060287; @@ -130,7 +155,6 @@ mod tests { assert!(bitmask.contains(RepBitmask::CYL)); assert!(bitmask.contains(RepBitmask::SURFACE)); - assert!(bitmask.contains(RepBitmask::CYL)); assert!(bitmask.contains(RepBitmask::SPHERE)); assert!(bitmask.contains(RepBitmask::SURFACE)); assert!(bitmask.contains(RepBitmask::LABEL)); @@ -142,19 +166,15 @@ mod tests { assert!(bitmask.contains(RepBitmask::DOT)); assert!(bitmask.contains(RepBitmask::DASH)); assert!(bitmask.contains(RepBitmask::NONBONDED)); - assert!(bitmask.contains(RepBitmask::CELL)); assert!(bitmask.contains(RepBitmask::CGO)); assert!(bitmask.contains(RepBitmask::CALLBACK)); - assert!(bitmask.contains(RepBitmask::EXTENT)); assert!(bitmask.contains(RepBitmask::SLICE)); assert!(bitmask.contains(RepBitmask::ANGLE)); assert!(bitmask.contains(RepBitmask::DIHEDRAL)); assert!(bitmask.contains(RepBitmask::ELLIPSOID)); + assert!(bitmask.contains(RepBitmask::VOLUME)); - // Check that the flag that should not be set is indeed not set - assert!(!bitmask.contains(RepBitmask::VOLUME)); - - // Verify the raw bits + assert!(!bitmask.contains(RepBitmask::EXTENT)); assert_eq!(bitmask.bits(), 2060287); } }