From 695fa73f48c143ca68b251187a15d5b17ce897cf Mon Sep 17 00:00:00 2001 From: Geoff Hutchison Date: Mon, 13 Nov 2023 22:25:16 -0500 Subject: [PATCH] Adding support for bond label in base classes and CJSON Signed-off-by: Geoff Hutchison --- avogadro/core/atom.h | 4 +- avogadro/core/molecule.cpp | 51 +++++++++-------- avogadro/core/molecule.h | 97 ++++++++++++++++++++++++++------ avogadro/io/cjsonformat.cpp | 33 +++++++++-- avogadro/qtgui/rwmolecule.cpp | 51 ++++++++--------- avogadro/qtgui/rwmolecule.h | 10 ++-- avogadro/qtgui/rwmolecule_undo.h | 10 ++-- 7 files changed, 170 insertions(+), 86 deletions(-) diff --git a/avogadro/core/atom.h b/avogadro/core/atom.h index fb5868d6ea..9efc9c545b 100644 --- a/avogadro/core/atom.h +++ b/avogadro/core/atom.h @@ -380,13 +380,13 @@ Vector3 AtomTemplate::forceVector() const template void AtomTemplate::setLabel(const std::string& label) { - m_molecule->setLabel(m_index, label); + m_molecule->setAtomLabel(m_index, label); } template std::string AtomTemplate::label() const { - return m_molecule->label(m_index); + return m_molecule->atomLabel(m_index); } } // namespace Core diff --git a/avogadro/core/molecule.cpp b/avogadro/core/molecule.cpp index 3b0484cb87..18d632d81a 100644 --- a/avogadro/core/molecule.cpp +++ b/avogadro/core/molecule.cpp @@ -36,8 +36,9 @@ Molecule::Molecule(const Molecule& other) : m_data(other.m_data), m_partialCharges(other.m_partialCharges), m_customElementMap(other.m_customElementMap), m_elements(other.m_elements), m_positions2d(other.m_positions2d), m_positions3d(other.m_positions3d), - m_label(other.m_label), m_coordinates3d(other.m_coordinates3d), - m_timesteps(other.m_timesteps), m_hybridizations(other.m_hybridizations), + m_atomLabels(other.m_atomLabels), m_bondLabels(other.m_bondLabels), + m_coordinates3d(other.m_coordinates3d), m_timesteps(other.m_timesteps), + m_hybridizations(other.m_hybridizations), m_formalCharges(other.m_formalCharges), m_colors(other.m_colors), m_vibrationFrequencies(other.m_vibrationFrequencies), m_vibrationIRIntensities(other.m_vibrationIRIntensities), @@ -78,9 +79,9 @@ Molecule::Molecule(Molecule&& other) noexcept : m_data(other.m_data), m_partialCharges(std::move(other.m_partialCharges)), m_customElementMap(std::move(other.m_customElementMap)), m_elements(other.m_elements), m_positions2d(other.m_positions2d), - m_positions3d(other.m_positions3d), m_label(other.m_label), - m_coordinates3d(other.m_coordinates3d), m_timesteps(other.m_timesteps), - m_hybridizations(other.m_hybridizations), + m_positions3d(other.m_positions3d), m_atomLabels(other.m_atomLabels), + m_bondLabels(other.m_bondLabels), m_coordinates3d(other.m_coordinates3d), + m_timesteps(other.m_timesteps), m_hybridizations(other.m_hybridizations), m_formalCharges(other.m_formalCharges), m_colors(other.m_colors), m_vibrationFrequencies(other.m_vibrationFrequencies), m_vibrationIRIntensities(other.m_vibrationIRIntensities), @@ -119,7 +120,8 @@ Molecule& Molecule::operator=(const Molecule& other) m_elements = other.m_elements; m_positions2d = other.m_positions2d; m_positions3d = other.m_positions3d; - m_label = other.m_label; + m_atomLabels = other.m_atomLabels; + m_bondLabels = other.m_bondLabels; m_coordinates3d = other.m_coordinates3d; m_timesteps = other.m_timesteps; m_hybridizations = other.m_hybridizations; @@ -180,7 +182,8 @@ Molecule& Molecule::operator=(Molecule&& other) noexcept m_elements = other.m_elements; m_positions2d = other.m_positions2d; m_positions3d = other.m_positions3d; - m_label = other.m_label; + m_atomLabels = other.m_atomLabels; + m_bondLabels = other.m_bondLabels; m_coordinates3d = other.m_coordinates3d; m_timesteps = other.m_timesteps; m_hybridizations = other.m_hybridizations; @@ -277,20 +280,20 @@ void Molecule::setFrozenAtom(Index atomId, bool frozen) return; // check if we need to resize - unsigned int size = m_frozenAtomMask.rows(); - if (m_frozenAtomMask.rows() != 3*m_atomicNumbers.size()) - m_frozenAtomMask.conservativeResize(3*m_atomicNumbers.size()); + unsigned int size = m_frozenAtomMask.rows(); + if (m_frozenAtomMask.rows() != 3 * m_atomicNumbers.size()) + m_frozenAtomMask.conservativeResize(3 * m_atomicNumbers.size()); // do we need to initialize new values? if (m_frozenAtomMask.rows() > size) for (unsigned int i = size; i < m_frozenAtomMask.rows(); ++i) m_frozenAtomMask[i] = 1.0; - + float value = frozen ? 0.0 : 1.0; if (atomId * 3 <= m_frozenAtomMask.rows() - 3) { - m_frozenAtomMask[atomId*3] = value; - m_frozenAtomMask[atomId*3+1] = value; - m_frozenAtomMask[atomId*3+2] = value; + m_frozenAtomMask[atomId * 3] = value; + m_frozenAtomMask[atomId * 3 + 1] = value; + m_frozenAtomMask[atomId * 3 + 2] = value; } } @@ -298,9 +301,9 @@ bool Molecule::frozenAtom(Index atomId) const { bool frozen = false; if (atomId * 3 <= m_frozenAtomMask.rows() - 3) { - frozen = (m_frozenAtomMask[atomId*3] == 0.0 && - m_frozenAtomMask[atomId*3+1] == 0.0 && - m_frozenAtomMask[atomId*3+2] == 0.0); + frozen = (m_frozenAtomMask[atomId * 3] == 0.0 && + m_frozenAtomMask[atomId * 3 + 1] == 0.0 && + m_frozenAtomMask[atomId * 3 + 2] == 0.0); } return frozen; } @@ -308,22 +311,21 @@ bool Molecule::frozenAtom(Index atomId) const void Molecule::setFrozenAtomAxis(Index atomId, int axis, bool frozen) { // check if we need to resize - unsigned int size = m_frozenAtomMask.rows(); - if (m_frozenAtomMask.rows() != 3*m_atomicNumbers.size()) - m_frozenAtomMask.conservativeResize(3*m_atomicNumbers.size()); + unsigned int size = m_frozenAtomMask.rows(); + if (m_frozenAtomMask.rows() != 3 * m_atomicNumbers.size()) + m_frozenAtomMask.conservativeResize(3 * m_atomicNumbers.size()); // do we need to initialize new values? if (m_frozenAtomMask.rows() > size) for (unsigned int i = size; i < m_frozenAtomMask.rows(); ++i) m_frozenAtomMask[i] = 1.0; - + float value = frozen ? 0.0 : 1.0; if (atomId * 3 <= m_frozenAtomMask.rows() - 3) { - m_frozenAtomMask[atomId*3 + axis] = value; + m_frozenAtomMask[atomId * 3 + axis] = value; } } - void Molecule::setData(const std::string& name, const Variant& value) { m_data.setValue(name, value); @@ -564,12 +566,13 @@ void Molecule::clearAtoms() { m_positions2d.clear(); m_positions3d.clear(); - m_label.clear(); + m_atomLabels.clear(); m_hybridizations.clear(); m_formalCharges.clear(); m_colors.clear(); m_atomicNumbers.clear(); m_bondOrders.clear(); + m_bondLabels.clear(); m_graph.clear(); m_partialCharges.clear(); m_elements.reset(); diff --git a/avogadro/core/molecule.h b/avogadro/core/molecule.h index b4c61297d3..add2b68232 100644 --- a/avogadro/core/molecule.h +++ b/avogadro/core/molecule.h @@ -271,9 +271,27 @@ class AVOGADROCORE_EXPORT Molecule */ bool setAtomPosition3d(Index atomId, const Vector3& pos); - std::string label(Index atomId) const; - bool setLabel(const Core::Array& label); - bool setLabel(Index atomId, const std::string& label); + /** + * @return Any custom label for the requested atom. + * @param atomId The index of the atom. + */ + std::string atomLabel(Index atomId) const; + /** + * Set the custom label of a single atom. + * @param atomId The index of the atom to modify. + * @param label The new label of the atom. + * @return True on success, false otherwise. + */ + bool setAtomLabel(Index atomId, const std::string& label); + + const Core::Array atomLabels() const { return m_atomLabels; } + + /** + * Set all the atom labels in the molecule. + * @param label The new label array. Must be of length atomCount(). + * @return True on success, false otherwise. + */ + bool setAtomLabels(const Core::Array& label); /** * Set whether the specified atom is selected or not. @@ -388,6 +406,28 @@ class AVOGADROCORE_EXPORT Molecule /** @return the bond between atomId1 and atomId2. */ BondType bond(Index atomId1, Index atomId2) const; + /** + * @return Any custom label for the requested bond. + * @param bondIndex The index of the bond. + */ + std::string bondLabel(Index bondIndex) const; + /** + * Set the custom label of a single bond. + * @param bondIndex The index of the bond to modify. + * @param label The new label of the bond. + * @return True on success, false otherwise. + */ + bool setBondLabel(Index bondIndex, const std::string& label); + + const Core::Array bondLabels() const { return m_bondLabels; } + + /** + * Set all the atom labels in the molecule. + * @param label The new label array. Must be of length atomCount(). + * @return True on success, false otherwise. + */ + bool setBondLabels(const Core::Array& label); + /** * @brief Get all bonds to @p a. * @return A vector of bonds to the supplied atom @p a. @@ -695,12 +735,12 @@ class AVOGADROCORE_EXPORT Molecule /** * Freeze or unfreeze an atom for optimization - */ + */ void setFrozenAtom(Index atomId, bool frozen); /** * Get the frozen status of an atom - */ + */ bool frozenAtom(Index atomId) const; /** @@ -708,7 +748,7 @@ class AVOGADROCORE_EXPORT Molecule * @param atomId The index of the atom to modify. * @param axis The axis to freeze (0, 1, or 2 for X, Y, or Z) * @param frozen True to freeze, false to unfreeze - */ + */ void setFrozenAtomAxis(Index atomId, int axis, bool frozen); Eigen::VectorXd frozenAtomMask() const { return m_frozenAtomMask; } @@ -757,7 +797,8 @@ class AVOGADROCORE_EXPORT Molecule //!< force fields) Array m_positions2d; Array m_positions3d; - Array m_label; + Array m_atomLabels; + Array m_bondLabels; Array> m_coordinates3d; //!< Store conformers/trajectories. Array m_timesteps; Array m_hybridizations; @@ -966,26 +1007,26 @@ inline bool Molecule::setAtomPosition3d(Index atomId, const Vector3& pos) return false; } -inline std::string Molecule::label(Index atomId) const +inline std::string Molecule::atomLabel(Index atomId) const { - return atomId < m_label.size() ? m_label[atomId] : ""; + return atomId < m_atomLabels.size() ? m_atomLabels[atomId] : ""; } -inline bool Molecule::setLabel(const Core::Array& label) +inline bool Molecule::setAtomLabels(const Core::Array& labels) { - if (label.size() == atomCount() || label.size() == 0) { - m_label = label; + if (labels.size() == atomCount() || labels.size() == 0) { + m_atomLabels = labels; return true; } return false; } -inline bool Molecule::setLabel(Index atomId, const std::string& label) +inline bool Molecule::setAtomLabel(Index atomId, const std::string& label) { if (atomId < atomCount()) { - if (atomId >= m_label.size()) - m_label.resize(atomCount(), ""); - m_label[atomId] = label; + if (atomId >= m_atomLabels.size()) + m_atomLabels.resize(atomCount(), ""); + m_atomLabels[atomId] = label; return true; } return false; @@ -1063,6 +1104,30 @@ inline const Array& Molecule::bondOrders() const return m_bondOrders; } +inline std::string Molecule::bondLabel(Index bondId) const +{ + return bondId < m_bondLabels.size() ? m_bondLabels[bondId] : ""; +} + +inline bool Molecule::setBondLabels(const Core::Array& labels) +{ + if (labels.size() == atomCount() || labels.size() == 0) { + m_bondLabels = labels; + return true; + } + return false; +} + +inline bool Molecule::setBondLabel(Index bondId, const std::string& label) +{ + if (bondId < bondCount()) { + if (bondId >= m_bondLabels.size()) + m_bondLabels.resize(bondCount(), ""); + m_bondLabels[bondId] = label; + return true; + } + return false; +} inline const Graph& Molecule::graph() const { return m_graph; diff --git a/avogadro/io/cjsonformat.cpp b/avogadro/io/cjsonformat.cpp index aa73d5e6cb..0017fbe186 100644 --- a/avogadro/io/cjsonformat.cpp +++ b/avogadro/io/cjsonformat.cpp @@ -154,7 +154,7 @@ bool CjsonFormat::deserialize(std::istream& file, Molecule& molecule, json labels = atoms["labels"]; if (labels.is_array() && labels.size() == atomCount) { for (size_t i = 0; i < atomCount; ++i) { - molecule.atom(i).setLabel(labels[i]); + molecule.setAtomLabel(i, labels[i]); } } @@ -244,6 +244,14 @@ bool CjsonFormat::deserialize(std::istream& file, Molecule& molecule, molecule.bond(i).setOrder(static_cast(order[i])); } } + + // are there bond labels? + json bondLabels = bonds["labels"]; + if (bondLabels.is_array() && bondLabels.size() == molecule.bondCount()) { + for (unsigned int i = 0; i < molecule.bondCount(); ++i) { + molecule.setBondLabel(i, bondLabels[i]); + } + } } // residues are optional, but should be loaded @@ -952,12 +960,15 @@ bool CjsonFormat::serialize(std::ostream& file, const Molecule& molecule, } } - // labels - json labels; - for (size_t i = 0; i < molecule.atomCount(); ++i) { - labels.push_back(molecule.label(i)); + // check for atom labels + Array atomLabels = molecule.atomLabels(); + if (atomLabels.size() == molecule.atomCount()) { + json labels; + for (Index i = 0; i < molecule.atomCount(); ++i) { + labels.push_back(atomLabels[i]); + } + root["atoms"]["labels"] = labels; } - root["atoms"]["labels"] = labels; // formal charges json formalCharges; @@ -987,6 +998,16 @@ bool CjsonFormat::serialize(std::ostream& file, const Molecule& molecule, } root["bonds"]["connections"]["index"] = connections; root["bonds"]["order"] = order; + + // check if there are bond labels + Array bondLabels = molecule.bondLabels(); + if (bondLabels.size() == molecule.bondCount()) { + json labels; + for (Index i = 0; i < molecule.bondCount(); ++i) { + labels.push_back(bondLabels[i]); + } + root["bonds"]["labels"] = labels; + } } // Create and populate any residue arrays diff --git a/avogadro/qtgui/rwmolecule.cpp b/avogadro/qtgui/rwmolecule.cpp index 703b8a6649..97bd73b739 100644 --- a/avogadro/qtgui/rwmolecule.cpp +++ b/avogadro/qtgui/rwmolecule.cpp @@ -23,8 +23,10 @@ using Core::CrystalTools; using Core::UnitCell; using std::swap; -RWMolecule::RWMolecule(Molecule& mol, QObject* p) : QObject(p), m_molecule(mol), m_interactive(false) -{} +RWMolecule::RWMolecule(Molecule& mol, QObject* p) + : QObject(p), m_molecule(mol), m_interactive(false) +{ +} RWMolecule::~RWMolecule() {} @@ -33,8 +35,7 @@ RWMolecule::AtomType RWMolecule::addAtom(unsigned char num, bool usingPositions) auto atomId = static_cast(m_molecule.atomCount()); auto atomUid = static_cast(m_molecule.m_atomUniqueIds.size()); - auto* comm = - new AddAtomCommand(*this, num, usingPositions, atomId, atomUid); + auto* comm = new AddAtomCommand(*this, num, usingPositions, atomId, atomUid); comm->setText(tr("Add Atom")); m_undoStack.push(comm); return AtomType(this, atomId); @@ -149,8 +150,8 @@ bool RWMolecule::setAtomicNumber(Index atomId, unsigned char num) if (atomId >= atomCount()) return false; - auto* comm = new SetAtomicNumberCommand( - *this, atomId, m_molecule.atomicNumber(atomId), num); + auto* comm = new SetAtomicNumberCommand(*this, atomId, + m_molecule.atomicNumber(atomId), num); comm->setText(tr("Change Element")); m_undoStack.push(comm); return true; @@ -162,18 +163,17 @@ bool RWMolecule::setAtomPositions3d(const Core::Array& pos, if (pos.size() != m_molecule.atomCount()) return false; - auto* comm = - new SetPositions3dCommand(*this, m_molecule.m_positions3d, pos); + auto* comm = new SetPositions3dCommand(*this, m_molecule.m_positions3d, pos); comm->setText(undoText); comm->setCanMerge(m_interactive); m_undoStack.push(comm); return true; } -bool RWMolecule::setLabel(Index atomId, const std::string& label, - const QString& undoText) +bool RWMolecule::setAtomLabel(Index atomId, const std::string& label, + const QString& undoText) { - auto* comm = new ModifyLabelCommand(*this, atomId, label); + auto* comm = new ModifyAtomLabelCommand(*this, atomId, label); comm->setText(undoText); m_undoStack.push(comm); return true; @@ -188,21 +188,22 @@ bool RWMolecule::setAtomPosition3d(Index atomId, const Vector3& pos, if (m_molecule.m_positions3d.size() != m_molecule.atomCount()) m_molecule.m_positions3d.resize(m_molecule.atomCount(), Vector3::Zero()); - auto* comm = new SetPosition3dCommand( - *this, atomId, m_molecule.m_positions3d[atomId], pos); + auto* comm = new SetPosition3dCommand(*this, atomId, + m_molecule.m_positions3d[atomId], pos); comm->setText(undoText); comm->setCanMerge(m_interactive); m_undoStack.push(comm); return true; } -void RWMolecule::setAtomSelected(Index atomId, bool selected, const QString& undoText) +void RWMolecule::setAtomSelected(Index atomId, bool selected, + const QString& undoText) { auto* comm = new ModifySelectionCommand(*this, atomId, selected); comm->setText(undoText); comm->setCanMerge(true); m_undoStack.push(comm); -// m_molecule.setAtomSelected(atomId, selected); + // m_molecule.setAtomSelected(atomId, selected); } bool RWMolecule::atomSelected(Index atomId) const @@ -315,8 +316,7 @@ bool RWMolecule::setBondOrders(const Core::Array& orders) if (orders.size() != m_molecule.bondCount()) return false; - auto* comm = - new SetBondOrdersCommand(*this, m_molecule.bondOrders(), orders); + auto* comm = new SetBondOrdersCommand(*this, m_molecule.bondOrders(), orders); comm->setText(tr("Set Bond Orders")); m_undoStack.push(comm); return true; @@ -350,8 +350,7 @@ bool RWMolecule::setBondPairs(const Array>& pairs) if (p_const[i].first > p_const[i].second) swap(p[i].first, p[i].second); - auto* comm = - new SetBondPairsCommand(*this, m_molecule.bondPairs(), p); + auto* comm = new SetBondPairsCommand(*this, m_molecule.bondPairs(), p); comm->setText(tr("Update Bonds")); m_undoStack.push(comm); return true; @@ -389,8 +388,7 @@ void RWMolecule::addUnitCell() static_cast(90.0) * DEG_TO_RAD); m_molecule.setUnitCell(cell); - auto* comm = - new AddUnitCellCommand(*this, *m_molecule.unitCell()); + auto* comm = new AddUnitCellCommand(*this, *m_molecule.unitCell()); comm->setText(tr("Add Unit Cell")); m_undoStack.push(comm); emitChanged(Molecule::UnitCell | Molecule::Added); @@ -402,8 +400,7 @@ void RWMolecule::removeUnitCell() if (!m_molecule.unitCell()) return; - auto* comm = - new RemoveUnitCellCommand(*this, *m_molecule.unitCell()); + auto* comm = new RemoveUnitCellCommand(*this, *m_molecule.unitCell()); comm->setText(tr("Remove Unit Cell")); m_undoStack.push(comm); @@ -415,8 +412,7 @@ void RWMolecule::modifyMolecule(const Molecule& newMolecule, Molecule::MoleculeChanges changes, const QString& undoText) { - auto* comm = - new ModifyMoleculeCommand(*this, m_molecule, newMolecule); + auto* comm = new ModifyMoleculeCommand(*this, m_molecule, newMolecule); comm->setText(undoText); m_undoStack.push(comm); @@ -484,8 +480,7 @@ void RWMolecule::wrapAtomsToCell() CrystalTools::wrapAtomsToUnitCell(m_molecule); Core::Array newPos = m_molecule.atomPositions3d(); - auto* comm = - new SetPositions3dCommand(*this, oldPos, newPos); + auto* comm = new SetPositions3dCommand(*this, oldPos, newPos); comm->setText(tr("Wrap Atoms to Cell")); m_undoStack.push(comm); @@ -727,4 +722,4 @@ bool RWMolecule::setForceVector(Index atomId, const Vector3& forces, return true; } -} // namespace Avogadro +} // namespace Avogadro::QtGui diff --git a/avogadro/qtgui/rwmolecule.h b/avogadro/qtgui/rwmolecule.h index b6912d9d87..f47eb302b9 100644 --- a/avogadro/qtgui/rwmolecule.h +++ b/avogadro/qtgui/rwmolecule.h @@ -223,9 +223,9 @@ class AVOGADROQTGUI_EXPORT RWMolecule : public QObject bool setAtomPosition3d(Index atomId, const Vector3& pos, const QString& undoText = tr("Change Atom Position")); - std::string label(Index atomId) const; - bool setLabel(Index atomId, const std::string& label, - const QString& undoText = tr("Change Atom Label")); + std::string atomLabel(Index atomId) const; + bool setAtomLabel(Index atomId, const std::string& label, + const QString& undoText = tr("Change Atom Label")); /** * Set whether the specified atom is selected or not. @@ -735,9 +735,9 @@ inline Vector3 RWMolecule::atomPosition3d(Index atomId) const return m_molecule.atomPosition3d(atomId); } -inline std::string RWMolecule::label(Index atomId) const +inline std::string RWMolecule::atomLabel(Index atomId) const { - return m_molecule.label(atomId); + return m_molecule.atomLabel(atomId); } inline Core::AtomHybridization RWMolecule::hybridization(Index atomId) const diff --git a/avogadro/qtgui/rwmolecule_undo.h b/avogadro/qtgui/rwmolecule_undo.h index 2ea33a919c..142c0cb36b 100644 --- a/avogadro/qtgui/rwmolecule_undo.h +++ b/avogadro/qtgui/rwmolecule_undo.h @@ -667,22 +667,22 @@ class SetForceVectorCommand : public MergeUndoCommand } // namespace namespace { -class ModifyLabelCommand : public RWMolecule::UndoCommand +class ModifyAtomLabelCommand : public RWMolecule::UndoCommand { Index m_atomId; std::string m_newLabel; std::string m_oldLabel; public: - ModifyLabelCommand(RWMolecule& m, Index atomId, const std::string& label) + ModifyAtomLabelCommand(RWMolecule& m, Index atomId, const std::string& label) : UndoCommand(m), m_atomId(atomId), m_newLabel(label) { - m_oldLabel = m_mol.molecule().label(m_atomId); + m_oldLabel = m_mol.molecule().atomLabel(m_atomId); } - void redo() override { m_mol.molecule().setLabel(m_atomId, m_newLabel); } + void redo() override { m_mol.molecule().setAtomLabel(m_atomId, m_newLabel); } - void undo() override { m_mol.molecule().setLabel(m_atomId, m_oldLabel); } + void undo() override { m_mol.molecule().setAtomLabel(m_atomId, m_oldLabel); } }; } // namespace