From 7adb2f76f8a74e38d280f5c084a87f47ff303a79 Mon Sep 17 00:00:00 2001 From: Geoff Hutchison Date: Sun, 17 Nov 2024 00:36:06 -0500 Subject: [PATCH] Add opacity to the licorice Probably should tune the radius too Signed-off-by: Geoff Hutchison --- avogadro/qtplugins/licorice/licorice.cpp | 129 ++++++++++++++++++++++- avogadro/qtplugins/licorice/licorice.h | 13 ++- 2 files changed, 138 insertions(+), 4 deletions(-) diff --git a/avogadro/qtplugins/licorice/licorice.cpp b/avogadro/qtplugins/licorice/licorice.cpp index 3c5c16ee0e..aec6a5dd75 100644 --- a/avogadro/qtplugins/licorice/licorice.cpp +++ b/avogadro/qtplugins/licorice/licorice.cpp @@ -12,6 +12,10 @@ #include #include +#include +#include +#include + namespace Avogadro::QtPlugins { using QtGui::Molecule; @@ -21,6 +25,62 @@ using Rendering::GeometryNode; using Rendering::GroupNode; using Rendering::SphereGeometry; +struct LayerLicorice : Core::LayerData +{ + QWidget* widget; + float opacity; + + LayerLicorice() + { + widget = nullptr; + QSettings settings; + opacity = settings.value("licorice/opacity", 1.0).toFloat(); + } + + LayerLicorice(std::string settings) + { + widget = nullptr; + deserialize(settings); + } + + LayerData* clone() final { return new LayerLicorice(*this); } + + ~LayerLicorice() override + { + if (widget) + widget->deleteLater(); + } + + std::string serialize() final { return std::to_string(opacity); } + + void deserialize(std::string text) final + { + std::stringstream ss(text); + std::string aux; + ss >> aux; + opacity = std::stof(aux); + } + + void setupWidget(Licorice* slot) + { + if (!widget) { + widget = new QWidget(qobject_cast(slot->parent())); + auto* form = new QFormLayout; + + // Opacity + auto* slider = new QSlider(Qt::Horizontal); + slider->setRange(0, 100); + slider->setTickInterval(1); + slider->setValue(round(opacity * 100)); + QObject::connect(slider, &QSlider::valueChanged, slot, + &Licorice::setOpacity); + + form->addRow(QObject::tr("Opacity:"), slider); + widget->setLayout(form); + } + } +}; + Licorice::Licorice(QObject* p) : ScenePlugin(p) { m_layerManager = PluginLayerManager(m_name); @@ -30,6 +90,8 @@ Licorice::~Licorice() {} void Licorice::process(const Molecule& molecule, Rendering::GroupNode& node) { + m_layerManager.load(); + // Use a common radius for all spheres and cylinders. float radius(0.2f); float selectedRadius(radius * 2.0f); @@ -40,10 +102,20 @@ void Licorice::process(const Molecule& molecule, Rendering::GroupNode& node) auto* spheres = new SphereGeometry; spheres->identifier().molecule = &molecule; spheres->identifier().type = Rendering::AtomType; + auto selectedSpheres = new SphereGeometry; selectedSpheres->setOpacity(0.42); + + auto translucentSpheres = new SphereGeometry; + translucentSpheres->setRenderPass(Rendering::TranslucentPass); + translucentSpheres->identifier().molecule = &molecule; + translucentSpheres->identifier().type = Rendering::AtomType; + geometry->addDrawable(spheres); geometry->addDrawable(selectedSpheres); + geometry->addDrawable(translucentSpheres); + + float opacity = 1.0; for (Index i = 0; i < molecule.atomCount(); ++i) { Core::Atom atom = molecule.atom(i); @@ -51,7 +123,14 @@ void Licorice::process(const Molecule& molecule, Rendering::GroupNode& node) continue; } Vector3ub color = atom.color(); - spheres->addSphere(atom.position3d().cast(), color, radius, i); + + opacity = m_layerManager.getSetting(i)->opacity; + if (opacity < 1.0f) { + translucentSpheres->addSphere(atom.position3d().cast(), color, + radius, i); + translucentSpheres->setOpacity(opacity); + } else + spheres->addSphere(atom.position3d().cast(), color, radius, i); if (atom.selected()) { color = Vector3ub(0, 0, 255); @@ -63,13 +142,34 @@ void Licorice::process(const Molecule& molecule, Rendering::GroupNode& node) auto* cylinders = new CylinderGeometry; cylinders->identifier().molecule = &molecule; cylinders->identifier().type = Rendering::BondType; + + auto* translucentCylinders = new CylinderGeometry; + translucentCylinders->setRenderPass(Rendering::TranslucentPass); + translucentCylinders->identifier().molecule = &molecule; + translucentCylinders->identifier().type = Rendering::BondType; + geometry->addDrawable(cylinders); + geometry->addDrawable(translucentCylinders); + for (Index i = 0; i < molecule.bondCount(); ++i) { Core::Bond bond = molecule.bond(i); if (!m_layerManager.bondEnabled(bond.atom1().index(), bond.atom2().index())) { continue; } + + auto* interface1 = m_layerManager.getSetting( + m_layerManager.getLayerID(bond.atom1().index())); + auto* interface2 = m_layerManager.getSetting( + m_layerManager.getLayerID(bond.atom2().index())); + + bool doOpaque = true; + if (interface1->opacity < 1.0f || interface2->opacity < 1.0f) { + opacity = std::min(interface1->opacity, interface2->opacity); + translucentCylinders->setOpacity(opacity); + doOpaque = false; + } + Vector3f pos1 = bond.atom1().position3d().cast(); Vector3f pos2 = bond.atom2().position3d().cast(); Vector3ub color1 = bond.atom1().color(); @@ -78,8 +178,31 @@ void Licorice::process(const Molecule& molecule, Rendering::GroupNode& node) float bondLength = bondVector.norm(); bondVector /= bondLength; - cylinders->addCylinder(pos1, pos2, radius, color1, color2, i); + if (doOpaque) + cylinders->addCylinder(pos1, pos2, radius, color1, color2, i); + else + translucentCylinders->addCylinder(pos1, pos2, radius, color1, color2, i); + } +} + +void Licorice::setOpacity(int opacity) +{ + m_opacity = static_cast(opacity) / 100.0f; + auto* interface = m_layerManager.getSetting(); + if (m_opacity != interface->opacity) { + interface->opacity = m_opacity; + emit drawablesChanged(); } + + QSettings settings; + settings.setValue("licorice/opacity", m_opacity); +} + +QWidget* Licorice::setupWidget() +{ + auto* interface = m_layerManager.getSetting(); + interface->setupWidget(this); + return interface->widget; } -} // namespace Avogadro +} // namespace Avogadro::QtPlugins diff --git a/avogadro/qtplugins/licorice/licorice.h b/avogadro/qtplugins/licorice/licorice.h index 9d4b1dd3ae..8572de26c4 100644 --- a/avogadro/qtplugins/licorice/licorice.h +++ b/avogadro/qtplugins/licorice/licorice.h @@ -26,7 +26,10 @@ class Licorice : public QtGui::ScenePlugin void process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) override; - QString name() const override { return tr("Licorice", "stick / licorice rendering"); } + QString name() const override + { + return tr("Licorice", "stick / licorice rendering"); + } QString description() const override { @@ -38,8 +41,16 @@ class Licorice : public QtGui::ScenePlugin return DefaultBehavior::False; } + QWidget* setupWidget() override; + bool hasSetupWidget() const override { return true; } + +public slots: + void setOpacity(int opacity); + private: std::string m_name = "Licorice"; + QWidget* m_setupWidget = nullptr; + float m_opacity = 1.0; }; } // end namespace QtPlugins