Skip to content

Commit

Permalink
Add support for atom and bond labels in the property tables
Browse files Browse the repository at this point in the history
Also render support for bond length and custom labels

Signed-off-by: Geoff Hutchison <[email protected]>
  • Loading branch information
ghutchis committed Dec 18, 2024
1 parent ca8a17e commit d52126a
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 14 deletions.
35 changes: 30 additions & 5 deletions avogadro/core/bond.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ class BondTemplate
unsigned char order() const;
/** @} */

/**
* The length of the bond or 0.0 if the bond is invalid.
*/
Real length() const;

/**
* A label for the bond (if any)
*/
std::string label() const;

private:
MoleculeType* m_molecule = nullptr;
Index m_index = MaxIndex;
Expand Down Expand Up @@ -197,8 +207,8 @@ typename BondTemplate<Molecule_T>::AtomType BondTemplate<Molecule_T>::atom2()
}

template <class Molecule_T>
typename BondTemplate<Molecule_T>::AtomType BondTemplate<Molecule_T>::getOtherAtom(Index index)
const
typename BondTemplate<Molecule_T>::AtomType
BondTemplate<Molecule_T>::getOtherAtom(Index index) const
{
if (atom1().index() == index)
return atom2();
Expand All @@ -207,9 +217,9 @@ typename BondTemplate<Molecule_T>::AtomType BondTemplate<Molecule_T>::getOtherAt
}

template <class Molecule_T>
typename BondTemplate<Molecule_T>::AtomType BondTemplate<Molecule_T>::getOtherAtom(
BondTemplate<Molecule_T>::AtomType atom
) const
typename BondTemplate<Molecule_T>::AtomType
BondTemplate<Molecule_T>::getOtherAtom(
BondTemplate<Molecule_T>::AtomType atom) const
{
return getOtherAtom(atom.index());
}
Expand All @@ -226,6 +236,21 @@ unsigned char BondTemplate<Molecule_T>::order() const
return m_molecule->bondOrders()[m_index];
}

template <class Molecule_T>
Real BondTemplate<Molecule_T>::length() const
{
if (!isValid())
return 0.0;

return (atom1().position3d() - atom2().position3d()).norm();
}

template <class Molecule_T>
std::string BondTemplate<Molecule_T>::label() const
{
return m_molecule->bondLabel(m_index);
}

} // namespace Avogadro::Core

#endif // AVOGADRO_CORE_BOND_H
15 changes: 15 additions & 0 deletions avogadro/qtgui/rwmolecule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,27 @@ bool RWMolecule::setAtomPositions3d(const Core::Array<Vector3>& pos,
bool RWMolecule::setAtomLabel(Index atomId, const std::string& label,
const QString& undoText)
{
if (atomId >= atomCount())
return false;

auto* comm = new ModifyAtomLabelCommand(*this, atomId, label);
comm->setText(undoText);
m_undoStack.push(comm);
return true;
}

bool RWMolecule::setBondLabel(Index bondId, const std::string& label,
const QString& undoText)
{
if (bondId >= bondCount())
return false;

auto* comm = new ModifyBondLabelCommand(*this, bondId, label);
comm->setText(undoText);
m_undoStack.push(comm);
return true;
}

bool RWMolecule::setAtomPosition3d(Index atomId, const Vector3& pos,
const QString& undoText)
{
Expand Down
37 changes: 37 additions & 0 deletions avogadro/qtgui/rwmolecule.h
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,42 @@ class AVOGADROQTGUI_EXPORT RWMolecule : public QObject
bool setAtomPosition3d(Index atomId, const Vector3& pos,
const QString& undoText = tr("Change Atom Position"));

/**
* Get the label on an atom.
* @param atomId The index of the atom.
* @return The label of the atom indexed at @a atomId, or an empty string if
* @a atomId is invalid or has no label.
*/
std::string atomLabel(Index atomId) const;

/**
* Set the label of a single atom.
* @param atomId The index of the atom to modify.
* @param label The new label.
* @param undoText The undo text to be displayed for undo commands.
* @return True on success, false otherwise.
*/
bool setAtomLabel(Index atomId, const std::string& label,
const QString& undoText = tr("Change Atom Label"));

/**
* Get the label on a bond
* @param bondId The index of the bond.
* @return The label of the bond indexed at @a bondId, or an empty string if
* @a bondId is invalid or has no label.
*/
std::string bondLabel(Index bondId) const;

/**
* Set the label of a single bond.
* @param bondId The index of the bond to modify.
* @param label The new label.
* @param undoText The undo text to be displayed for undo commands.
* @return True on success, false otherwise.
*/
bool setBondLabel(Index bondId, const std::string& label,
const QString& undoText = tr("Change Bond Label"));

/**
* Set whether the specified atom is selected or not.
*/
Expand Down Expand Up @@ -740,6 +772,11 @@ inline std::string RWMolecule::atomLabel(Index atomId) const
return m_molecule.atomLabel(atomId);
}

inline std::string RWMolecule::bondLabel(Index bondId) const
{
return m_molecule.bondLabel(bondId);
}

inline Core::AtomHybridization RWMolecule::hybridization(Index atomId) const
{
return m_molecule.hybridization(atomId);
Expand Down
20 changes: 20 additions & 0 deletions avogadro/qtgui/rwmolecule_undo.h
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,26 @@ class ModifyAtomLabelCommand : public RWMolecule::UndoCommand
};
} // namespace

namespace {
class ModifyBondLabelCommand : public RWMolecule::UndoCommand
{
Index m_bondId;
std::string m_newLabel;
std::string m_oldLabel;

public:
ModifyBondLabelCommand(RWMolecule& m, Index bondId, const std::string& label)
: UndoCommand(m), m_bondId(bondId), m_newLabel(label)
{
m_oldLabel = m_mol.molecule().bondLabel(m_bondId);
}

void redo() override { m_mol.molecule().setBondLabel(m_bondId, m_newLabel); }

void undo() override { m_mol.molecule().setBondLabel(m_bondId, m_oldLabel); }
};
} // namespace

namespace {
class ModifySelectionCommand : public MergeUndoCommand<ModifySelectionMergeId>
{
Expand Down
94 changes: 92 additions & 2 deletions avogadro/qtplugins/label/label.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "label.h"

#include <iomanip>
#include <iostream>
#include <sstream>

Expand Down Expand Up @@ -71,10 +72,12 @@ struct LayerLabel : Core::LayerData
Custom = 4,
Ordinal = 8,
UniqueID = 16,
PartialCharge = 32
PartialCharge = 32,
Length = 64 // for bonds obviously
};
unsigned short atomOptions;
unsigned short residueOptions;
unsigned short bondOptions;

QWidget* widget;
float radiusScalar;
Expand All @@ -88,6 +91,8 @@ struct LayerLabel : Core::LayerData
settings.value("label/atomoptions", LabelOptions::Name).toInt();
residueOptions =
settings.value("label/residueoptions", LabelOptions::None).toInt();
bondOptions =
settings.value("label/bondoptions", LabelOptions::None).toInt();
radiusScalar = settings.value("label/radiusscalar", 0.5).toDouble();

auto q_color =
Expand Down Expand Up @@ -115,7 +120,8 @@ struct LayerLabel : Core::LayerData
{
std::stringstream output;
output << atomOptions << " " << residueOptions << " " << radiusScalar << " "
<< (int)color[0] << " " << (int)color[1] << " " << (int)color[2];
<< (int)color[0] << " " << (int)color[1] << " " << (int)color[2]
<< " " << bondOptions;
return output.str();
}

Expand All @@ -135,6 +141,9 @@ struct LayerLabel : Core::LayerData
color[1] = std::stoi(aux);
ss >> aux;
color[2] = std::stoi(aux);
ss >> aux;
if (!aux.empty())
bondOptions = std::stoi(aux); // backwards compatibility
}

void setupWidget(Label* slot)
Expand Down Expand Up @@ -193,6 +202,20 @@ struct LayerLabel : Core::LayerData

form->addRow(QObject::tr("Atom Label:"), atom);

// bond label
auto* bond = new QComboBox;
bond->setObjectName("bond");

// set up the various bond options
bond->addItem(QObject::tr("None"), int(LabelOptions::None));
bond->addItem(QObject::tr("Length"), int(LabelOptions::Length));
bond->addItem(QObject::tr("Index"), int(LabelOptions::Index));
bond->addItem(QObject::tr("Custom"), int(LabelOptions::Custom));

QObject::connect(bond, SIGNAL(currentIndexChanged(int)), slot,
SLOT(bondLabelType(int)));
form->addRow(QObject::tr("Bond Label:"), bond);

auto* residue = new QComboBox;
residue->setObjectName("residue");
for (char i = 0x00; i < std::pow(2, 2); ++i) {
Expand Down Expand Up @@ -249,6 +272,9 @@ void Label::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node)
if (interface->atomOptions) {
processAtom(molecule, node, layer);
}
if (interface->bondOptions) {
processBond(molecule, node, layer);
}
}
}

Expand Down Expand Up @@ -389,6 +415,60 @@ void Label::processAtom(const Core::Molecule& molecule,
}
}

void Label::processBond(const Core::Molecule& molecule,
Rendering::GroupNode& node, size_t layer)
{
auto* geometry = new GeometryNode;
node.addChild(geometry);
std::map<unsigned char, size_t> bondCount;
for (Index i = 0; i < molecule.bondCount(); ++i) {
Core::Bond bond = molecule.bond(i);

// check if the bond is enabled in this layer
if (!m_layerManager.bondEnabled(bond.atom1().index(),
bond.atom2().index())) {
continue;
}

// get the options for this bond
Core::Atom atom1 = bond.atom1();
Core::Atom atom2 = bond.atom2();
auto* interface1 = m_layerManager.getSetting<LayerLabel>(
m_layerManager.getLayerID(atom1.index()));
auto* interface2 = m_layerManager.getSetting<LayerLabel>(
m_layerManager.getLayerID(atom2.index()));

// get the union of the options
char options = interface1->bondOptions | interface2->bondOptions;
Vector3ub color = interface1->color;
unsigned char atomicNumber1 = atom1.atomicNumber();
unsigned char atomicNumber2 = atom2.atomicNumber();
float radiusVDW1 = static_cast<float>(Elements::radiusVDW(atomicNumber1));
float radiusVDW2 = static_cast<float>(Elements::radiusVDW(atomicNumber2));
float radius = (radiusVDW1 + radiusVDW2) * interface1->radiusScalar / 2.0f;

std::stringstream text;
// hopefully not all at once
if (options & LayerLabel::LabelOptions::Index) {
text << bond.index();
}
if (options & LayerLabel::LabelOptions::Custom) {
text << bond.label();
}
if (options & LayerLabel::LabelOptions::Length) {
text << std::fixed << std::setprecision(2) << bond.length();
}

// position will be between the two atoms
Vector3f pos =
(atom1.position3d().cast<float>() + atom2.position3d().cast<float>()) /
2.0f;

TextLabel3D* bondLabel = createLabel(text.str(), pos, radius, color);
geometry->addDrawable(bondLabel);
}
}

void Label::setColor(const QColor& color)
{
auto* interface = m_layerManager.getSetting<LayerLabel>();
Expand All @@ -413,6 +493,16 @@ void Label::atomLabelType(int index)
emit drawablesChanged();
}

void Label::bondLabelType(int index)
{
auto* interface = m_layerManager.getSetting<LayerLabel>();
interface->bondOptions = char(setupWidget()
->findChildren<QComboBox*>("bond")[0]
->itemData(index)
.toInt());
emit drawablesChanged();
}

void Label::residueLabelType(int index)
{
auto* interface = m_layerManager.getSetting<LayerLabel>();
Expand Down
3 changes: 3 additions & 0 deletions avogadro/qtplugins/label/label.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,16 @@ class Label : public QtGui::ScenePlugin
}
public slots:
void atomLabelType(int index);
void bondLabelType(int index);
void residueLabelType(int index);
void setRadiusScalar(double radius);
void setColor(const QColor& color);

private:
void processAtom(const Core::Molecule& molecule, Rendering::GroupNode& node,
size_t layer);
void processBond(const Core::Molecule& molecule, Rendering::GroupNode& node,
size_t layer);
void processResidue(const Core::Molecule& molecule,
Rendering::GroupNode& node, size_t layer);

Expand Down
Loading

1 comment on commit d52126a

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERROR: clang-format-diff detected formatting issues. See the artifact for a patch or run clang-format on your branch.

Please sign in to comment.