diff --git a/avogadro/core/layermanager.h b/avogadro/core/layermanager.h index 83a434e279..27009b829f 100644 --- a/avogadro/core/layermanager.h +++ b/avogadro/core/layermanager.h @@ -42,6 +42,8 @@ struct LayerData virtual ~LayerData() = default; + virtual LayerData* clone() { return new LayerData(serialize()); }; + /** get the saved data */ std::string getSave() const { return m_save; } diff --git a/avogadro/qtgui/pluginlayermanager.h b/avogadro/qtgui/pluginlayermanager.h index fa97b52625..0ed8d57c3d 100644 --- a/avogadro/qtgui/pluginlayermanager.h +++ b/avogadro/qtgui/pluginlayermanager.h @@ -10,6 +10,7 @@ #include #include +#include namespace Avogadro { namespace QtGui { @@ -79,22 +80,25 @@ class AVOGADROQTGUI_EXPORT PluginLayerManager : protected Core::LayerManager /** @return custom data T derived from LayerData. if @p layer is equal to * MaxIndex returns activeLayer */ template - T& getSetting(size_t layer = MaxIndex) + T* getSetting(size_t layer = MaxIndex) { auto info = m_molToInfo[m_activeMolecule]; + if (layer == MaxIndex) { layer = info->layer.activeLayer(); } + assert(layer <= info->layer.maxLayer()); if (info->settings.find(m_name) == info->settings.end()) { info->settings[m_name] = Core::Array(); } + // do we need to create new layers in the array? while (info->settings[m_name].size() < layer + 1) { info->settings[m_name].push_back(new T()); } - auto result = static_cast(info->settings[m_name][layer]); - return *result; + auto* result = static_cast(info->settings[m_name][layer]); + return result; } private: diff --git a/avogadro/qtgui/rwlayermanager.cpp b/avogadro/qtgui/rwlayermanager.cpp index 6f9ccb787e..cb7186302c 100644 --- a/avogadro/qtgui/rwlayermanager.cpp +++ b/avogadro/qtgui/rwlayermanager.cpp @@ -45,7 +45,6 @@ class AddLayerCommand : public QUndoCommand auto value = names.second[activeLayer]; m_settings[names.first] = value; } - } void redo() override @@ -59,7 +58,9 @@ class AddLayerCommand : public QUndoCommand m_moleculeInfo->enable[enable.first].push_back(enable.second); } for (const auto& settings : m_settings) { - m_moleculeInfo->settings[settings.first].push_back(settings.second); + // create newSettings pointer with the same type as settings.second + auto* newSettings = settings.second->clone(); + m_moleculeInfo->settings[settings.first].push_back(newSettings); } m_moleculeInfo->layer.addLayer(); @@ -130,7 +131,8 @@ class RemoveLayerCommand : public QUndoCommand RemoveLayerCommand(shared_ptr mol, size_t layer) : QUndoCommand(QObject::tr("Modify Layers")), m_moleculeInfo(mol), m_layer(layer) - {} + { + } void redo() override { @@ -285,4 +287,4 @@ Array> RWLayerManager::activeMoleculeNames() const return result; } -} // namespace Avogadro +} // namespace Avogadro::QtGui diff --git a/avogadro/qtplugins/ballandstick/ballandstick.cpp b/avogadro/qtplugins/ballandstick/ballandstick.cpp index 69972d1514..19ae495bcc 100644 --- a/avogadro/qtplugins/ballandstick/ballandstick.cpp +++ b/avogadro/qtplugins/ballandstick/ballandstick.cpp @@ -21,6 +21,8 @@ #include #include +#include + namespace Avogadro::QtPlugins { using Core::Elements; @@ -37,15 +39,24 @@ struct LayerBallAndStick : Core::LayerData bool showHydrogens; float atomScale; float bondRadius; + float opacity; LayerBallAndStick() { widget = nullptr; QSettings settings; + atomScale = settings.value("ballandstick/atomScale", 0.3).toDouble(); bondRadius = settings.value("ballandstick/bondRadius", 0.1).toDouble(); multiBonds = settings.value("ballandstick/multiBonds", true).toBool(); showHydrogens = settings.value("ballandstick/showHydrogens", true).toBool(); + opacity = settings.value("ballandstick/opacity", 1.0).toDouble(); + } + + LayerBallAndStick(std::string settings) + { + widget = nullptr; + deserialize(settings); } ~LayerBallAndStick() override @@ -57,7 +68,8 @@ struct LayerBallAndStick : Core::LayerData std::string serialize() final { return boolToString(multiBonds) + " " + boolToString(showHydrogens) + " " + - std::to_string(atomScale) + " " + std::to_string(bondRadius); + std::to_string(atomScale) + " " + std::to_string(bondRadius) + " " + + std::to_string(opacity); } void deserialize(std::string text) final @@ -72,8 +84,13 @@ struct LayerBallAndStick : Core::LayerData atomScale = std::stof(aux); ss >> aux; bondRadius = std::stof(aux); + ss >> aux; + if (!aux.empty()) + opacity = std::stof(aux); // backwards compatibility } + LayerData* clone() final { return new LayerBallAndStick(serialize()); } + void setupWidget(BallAndStick* slot) { if (!widget) { @@ -98,6 +115,16 @@ struct LayerBallAndStick : Core::LayerData QObject::connect(bondRadiusSlider, &QSlider::valueChanged, slot, &BallAndStick::bondRadiusChanged); f->addRow(QObject::tr("Bond scale"), bondRadiusSlider); + + auto* opacitySlider = new QSlider(Qt::Horizontal); + opacitySlider->setMinimum(0); + opacitySlider->setMaximum(100); + opacitySlider->setTickInterval(1); + opacitySlider->setValue(static_cast(opacity * 100)); + QObject::connect(opacitySlider, &QSlider::valueChanged, slot, + &BallAndStick::opacityChanged); + f->addRow(QObject::tr("Opacity"), opacitySlider); + v->addLayout(f); auto* check = new QCheckBox(QObject::tr("Show multiple bonds")); @@ -134,11 +161,22 @@ void BallAndStick::process(const QtGui::Molecule& molecule, auto* geometry = new GeometryNode; node.addChild(geometry); auto* spheres = new SphereGeometry; - auto selectedSpheres = new SphereGeometry; - selectedSpheres->setOpacity(0.42); spheres->identifier().molecule = reinterpret_cast(&molecule); spheres->identifier().type = Rendering::AtomType; geometry->addDrawable(spheres); + + // if we have to draw any translucent spheres, we need to add a separate + // geometry node for them + auto translucentSpheres = new SphereGeometry; + translucentSpheres->setRenderPass(Rendering::TranslucentPass); + translucentSpheres->identifier().molecule = + reinterpret_cast(&molecule); + translucentSpheres->identifier().type = Rendering::AtomType; + geometry->addDrawable(translucentSpheres); + + // for the selected atoms + auto selectedSpheres = new SphereGeometry; + selectedSpheres->setOpacity(0.42); geometry->addDrawable(selectedSpheres); for (Index i = 0; i < molecule.atomCount(); ++i) { @@ -147,17 +185,26 @@ void BallAndStick::process(const QtGui::Molecule& molecule, continue; } unsigned char atomicNumber = atom.atomicNumber(); - auto& interface = m_layerManager.getSetting( + + auto* interface = m_layerManager.getSetting( m_layerManager.getLayerID(i)); - if (atomicNumber == 1 && !interface.showHydrogens) + if (atomicNumber == 1 && !interface->showHydrogens) continue; Vector3ub color = atom.color(); auto radius = static_cast(Elements::radiusVDW(atomicNumber)); - float scale = interface.atomScale; - spheres->addSphere(atom.position3d().cast(), color, radius * scale, - i); + float scale = interface->atomScale; + + if (interface->opacity < 1.0f) { + translucentSpheres->addSphere(atom.position3d().cast(), color, + radius * scale, i); + translucentSpheres->setOpacity(interface->opacity); + } else + spheres->addSphere(atom.position3d().cast(), color, radius * scale, + i); + if (atom.selected()) { + // add the selected indicator color = Vector3ub(0, 0, 255); radius *= 1.2; selectedSpheres->addSphere(atom.position3d().cast(), color, @@ -169,6 +216,14 @@ void BallAndStick::process(const QtGui::Molecule& molecule, cylinders->identifier().molecule = &molecule; cylinders->identifier().type = Rendering::BondType; geometry->addDrawable(cylinders); + + auto* translucentBonds = new CylinderGeometry; + translucentBonds->setRenderPass(Rendering::TranslucentPass); + translucentBonds->identifier().molecule = &molecule; + translucentBonds->identifier().type = Rendering::BondType; + float opacity = 1.0f; // for any translucent bonds + geometry->addDrawable(translucentBonds); + for (Index i = 0; i < molecule.bondCount(); ++i) { Core::Bond bond = molecule.bond(i); if (!m_layerManager.bondEnabled(bond.atom1().index(), @@ -176,18 +231,25 @@ void BallAndStick::process(const QtGui::Molecule& molecule, continue; } - auto& interface1 = m_layerManager.getSetting( + auto* interface1 = m_layerManager.getSetting( m_layerManager.getLayerID(bond.atom1().index())); - auto& interface2 = m_layerManager.getSetting( + auto* interface2 = m_layerManager.getSetting( m_layerManager.getLayerID(bond.atom2().index())); - if (!interface1.showHydrogens && !interface2.showHydrogens && + if (!interface1->showHydrogens && !interface2->showHydrogens && (bond.atom1().atomicNumber() == 1 || bond.atom2().atomicNumber() == 1)) { continue; } - float bondRadius = (interface1.bondRadius + interface2.bondRadius) * 0.5f; + bool doOpaque = true; + if (interface1->opacity < 1.0f || interface2->opacity < 1.0f) { + opacity = std::min(interface1->opacity, interface2->opacity); + translucentBonds->setOpacity(opacity); + doOpaque = false; + } + + float bondRadius = (interface1->bondRadius + interface2->bondRadius) * 0.5f; Vector3f pos1 = bond.atom1().position3d().cast(); Vector3f pos2 = bond.atom2().position3d().cast(); @@ -197,23 +259,36 @@ void BallAndStick::process(const QtGui::Molecule& molecule, float bondLength = bondVector.norm(); bondVector /= bondLength; - switch (interface1.multiBonds || interface2.multiBonds ? bond.order() : 1) { + switch (interface1->multiBonds || interface2->multiBonds ? bond.order() + : 1) { case 3: { Vector3f delta = bondVector.unitOrthogonal(); // Rotate 45 degrees around the bond vector. Eigen::Quaternionf q; q = Eigen::AngleAxisf(45.0f * DEG_TO_RAD_F, bondVector); delta = q * delta * 2.0f * bondRadius; - cylinders->addCylinder(pos1 + delta, pos2 + delta, bondRadius * 1.15, - color1, color2, i); - cylinders->addCylinder(pos1 - delta, pos2 - delta, bondRadius * 1.15, - color1, color2, i); + if (doOpaque) { + cylinders->addCylinder(pos1 + delta, pos2 + delta, bondRadius * 1.15, + color1, color2, i); + cylinders->addCylinder(pos1 - delta, pos2 - delta, bondRadius * 1.15, + color1, color2, i); + } else { + translucentBonds->addCylinder(pos1 + delta, pos2 + delta, + bondRadius * 1.15, color1, color2, i); + translucentBonds->addCylinder(pos1 - delta, pos2 - delta, + bondRadius * 1.15, color1, color2, i); + } // This relies upon the single bond case below for the third cylinder. [[fallthrough]]; } default: case 1: - cylinders->addCylinder(pos1, pos2, m_bondRadius, color1, color2, i); + if (doOpaque) { + cylinders->addCylinder(pos1, pos2, m_bondRadius, color1, color2, i); + } else { + translucentBonds->addCylinder(pos1, pos2, m_bondRadius, color1, + color2, i); + } break; case 2: { Vector3f delta = bondVector.unitOrthogonal(); @@ -221,10 +296,17 @@ void BallAndStick::process(const QtGui::Molecule& molecule, Eigen::Quaternionf q; q = Eigen::AngleAxisf(45.0f * DEG_TO_RAD_F, bondVector); delta = q * delta * bondRadius; - cylinders->addCylinder(pos1 + delta, pos2 + delta, bondRadius * 1.3, - color1, color2, i); - cylinders->addCylinder(pos1 - delta, pos2 - delta, bondRadius * 1.3, - color1, color2, i); + if (doOpaque) { + cylinders->addCylinder(pos1 + delta, pos2 + delta, bondRadius * 1.3, + color1, color2, i); + cylinders->addCylinder(pos1 - delta, pos2 - delta, bondRadius * 1.3, + color1, color2, i); + } else { + translucentBonds->addCylinder(pos1 + delta, pos2 + delta, + bondRadius * 1.3, color1, color2, i); + translucentBonds->addCylinder(pos1 - delta, pos2 - delta, + bondRadius * 1.3, color1, color2, i); + } } } } @@ -232,18 +314,31 @@ void BallAndStick::process(const QtGui::Molecule& molecule, QWidget* BallAndStick::setupWidget() { - auto& interface = m_layerManager.getSetting(); - interface.setupWidget(this); - return interface.widget; + auto* interface = m_layerManager.getSetting(); + interface->setupWidget(this); + return interface->widget; +} + +void BallAndStick::opacityChanged(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("ballandstick/opacity", m_opacity); } void BallAndStick::atomRadiusChanged(int value) { m_atomScale = static_cast(value) / 10.0f; - auto& interface = m_layerManager.getSetting(); - if (m_atomScale != interface.atomScale) { - interface.atomScale = m_atomScale; + auto* interface = m_layerManager.getSetting(); + if (m_atomScale != interface->atomScale) { + interface->atomScale = m_atomScale; emit drawablesChanged(); } @@ -255,9 +350,9 @@ void BallAndStick::bondRadiusChanged(int value) { m_bondRadius = static_cast(value) / 10.0f; - auto& interface = m_layerManager.getSetting(); - if (m_bondRadius != interface.bondRadius) { - interface.bondRadius = m_bondRadius; + auto* interface = m_layerManager.getSetting(); + if (m_bondRadius != interface->bondRadius) { + interface->bondRadius = m_bondRadius; emit drawablesChanged(); } @@ -267,9 +362,9 @@ void BallAndStick::bondRadiusChanged(int value) void BallAndStick::multiBonds(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.multiBonds) { - interface.multiBonds = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->multiBonds) { + interface->multiBonds = show; emit drawablesChanged(); } QSettings settings; @@ -278,9 +373,9 @@ void BallAndStick::multiBonds(bool show) void BallAndStick::showHydrogens(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.showHydrogens) { - interface.showHydrogens = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->showHydrogens) { + interface->showHydrogens = show; emit drawablesChanged(); } QSettings settings; diff --git a/avogadro/qtplugins/ballandstick/ballandstick.h b/avogadro/qtplugins/ballandstick/ballandstick.h index cecd7000a6..ded6a1c2f0 100644 --- a/avogadro/qtplugins/ballandstick/ballandstick.h +++ b/avogadro/qtplugins/ballandstick/ballandstick.h @@ -46,12 +46,14 @@ public slots: void bondRadiusChanged(int value); void multiBonds(bool show); void showHydrogens(bool show); + void opacityChanged(int value); private: Rendering::GroupNode* m_group; std::string m_name = "Ball and Stick"; float m_atomScale = 0.3f; float m_bondRadius = 0.1f; + float m_opacity = 1.0f; }; } // end namespace QtPlugins diff --git a/avogadro/qtplugins/cartoons/cartoons.cpp b/avogadro/qtplugins/cartoons/cartoons.cpp index 801bde9f05..faef3b3944 100644 --- a/avogadro/qtplugins/cartoons/cartoons.cpp +++ b/avogadro/qtplugins/cartoons/cartoons.cpp @@ -133,11 +133,19 @@ struct LayerCartoon : Core::LayerData showSimpleCartoon = settings.value("cartoon/simplecartoon", false).toBool(); } + LayerCartoon(std::string settings) + { + widget = nullptr; + deserialize(settings); + } + ~LayerCartoon() override { if (widget) widget->deleteLater(); } + + LayerData* clone() final { return new LayerCartoon(serialize()); } }; struct BackboneResidue @@ -149,7 +157,8 @@ struct BackboneResidue : pos(p), color1(c1), color2(c2), group(g), residueID(id), selected(sel), secondaryStructure(sec) - {} + { + } Vector3f pos; Vector3ub color1; Vector3ub color2; @@ -252,8 +261,7 @@ void renderBackbone(const AtomsPairList& backbone, const Molecule& molecule, geometry->addDrawable(cylinders); Index i = 0; - for (auto it = backbone.begin(); - it != backbone.end(); ++it) { + for (auto it = backbone.begin(); it != backbone.end(); ++it) { const auto& bone = *it; auto color1 = bone.color1; auto color2 = bone.color2; @@ -352,10 +360,11 @@ void Cartoons::process(const Molecule& molecule, Rendering::GroupNode& node) m_layerManager.load(); m_group = &node; for (size_t layer = 0; layer < m_layerManager.layerCount(); ++layer) { - auto& interface = m_layerManager.getSetting(layer); - if (interface.showBackbone || interface.showTrace || interface.showTube || - interface.showRibbon || interface.showSimpleCartoon || - interface.showCartoon || interface.showRope) { + auto* interface = m_layerManager.getSetting(layer); + if (interface->showBackbone || interface->showTrace || + interface->showTube || interface->showRibbon || + interface->showSimpleCartoon || interface->showCartoon || + interface->showRope) { map backbones; if (molecule.residues().size() > 0) { backbones = getBackboneByResidues(molecule, layer); @@ -366,26 +375,26 @@ void Cartoons::process(const Molecule& molecule, Rendering::GroupNode& node) size_t i = 0; for (const auto& group : backbones) { const auto& backbone = group.second; - if (interface.showBackbone) { + if (interface->showBackbone) { renderBackbone(backbone, molecule, node, 0.1f); } - if (interface.showTrace) { + if (interface->showTrace) { renderTube(backbone, molecule, node, -0.15f); } - if (interface.showTube) { + if (interface->showTube) { renderTube(backbone, molecule, node, 0.15f); } - if (interface.showRibbon) { + if (interface->showRibbon) { renderCartoon(backbone, molecule, node, -1.0f * Cartoon::ELIPSE_RATIO); } - if (interface.showSimpleCartoon) { + if (interface->showSimpleCartoon) { renderSimpleCartoon(backbone, molecule, node, 1.0f); } - if (interface.showCartoon) { + if (interface->showCartoon) { renderCartoon(backbone, molecule, node, 1.0f); } - if (interface.showRope) { + if (interface->showRope) { renderRope(backbone, molecule, node, 1.0f); } ++i; @@ -396,16 +405,16 @@ void Cartoons::process(const Molecule& molecule, Rendering::GroupNode& node) QWidget* Cartoons::setupWidget() { - auto& interface = m_layerManager.getSetting(); - interface.setupWidget(this); - return interface.widget; + auto* interface = m_layerManager.getSetting(); + interface->setupWidget(this); + return interface->widget; } void Cartoons::showBackbone(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.showBackbone) { - interface.showBackbone = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->showBackbone) { + interface->showBackbone = show; emit drawablesChanged(); } QSettings settings; @@ -414,9 +423,9 @@ void Cartoons::showBackbone(bool show) void Cartoons::showTrace(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.showTrace) { - interface.showTrace = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->showTrace) { + interface->showTrace = show; emit drawablesChanged(); } QSettings settings; @@ -425,9 +434,9 @@ void Cartoons::showTrace(bool show) void Cartoons::showTube(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.showTube) { - interface.showTube = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->showTube) { + interface->showTube = show; emit drawablesChanged(); } QSettings settings; @@ -436,9 +445,9 @@ void Cartoons::showTube(bool show) void Cartoons::showRibbon(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.showRibbon) { - interface.showRibbon = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->showRibbon) { + interface->showRibbon = show; emit drawablesChanged(); } QSettings settings; @@ -447,9 +456,9 @@ void Cartoons::showRibbon(bool show) void Cartoons::showSimpleCartoon(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.showSimpleCartoon) { - interface.showSimpleCartoon = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->showSimpleCartoon) { + interface->showSimpleCartoon = show; emit drawablesChanged(); } QSettings settings; @@ -458,9 +467,9 @@ void Cartoons::showSimpleCartoon(bool show) void Cartoons::showCartoon(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.showCartoon) { - interface.showCartoon = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->showCartoon) { + interface->showCartoon = show; emit drawablesChanged(); } QSettings settings; @@ -469,13 +478,13 @@ void Cartoons::showCartoon(bool show) void Cartoons::showRope(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.showRope) { - interface.showRope = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->showRope) { + interface->showRope = show; emit drawablesChanged(); } QSettings settings; settings.setValue("cartoon/rope", show); } -} // namespace Avogadro +} // namespace Avogadro::QtPlugins diff --git a/avogadro/qtplugins/label/label.cpp b/avogadro/qtplugins/label/label.cpp index d468cd9a01..0ee38f9f51 100644 --- a/avogadro/qtplugins/label/label.cpp +++ b/avogadro/qtplugins/label/label.cpp @@ -97,6 +97,14 @@ struct LayerLabel : Core::LayerData color[2] = static_cast(q_color.blue()); } + LayerLabel(std::string settings) + { + widget = nullptr; + deserialize(settings); + } + + LayerData* clone() final { return new LayerLabel(serialize()); } + ~LayerLabel() override { if (widget) @@ -234,11 +242,11 @@ void Label::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) { m_layerManager.load(); for (size_t layer = 0; layer < m_layerManager.layerCount(); ++layer) { - auto& interface = m_layerManager.getSetting(layer); - if (interface.residueOptions) { + auto* interface = m_layerManager.getSetting(layer); + if (interface->residueOptions) { processResidue(molecule, node, layer); } - if (interface.atomOptions) { + if (interface->atomOptions) { processAtom(molecule, node, layer); } } @@ -274,13 +282,13 @@ void Label::processResidue(const Core::Molecule& molecule, } } - auto& interface = m_layerManager.getSetting(layer); - Vector3ub color = interface.color; + auto* interface = m_layerManager.getSetting(layer); + Vector3ub color = interface->color; std::string text = ""; - if (interface.residueOptions & LayerLabel::LabelOptions::Index) { + if (interface->residueOptions & LayerLabel::LabelOptions::Index) { text = std::to_string(residue.residueId()); } - if (interface.residueOptions & LayerLabel::LabelOptions::Name) { + if (interface->residueOptions & LayerLabel::LabelOptions::Name) { text += (text == "" ? "" : " / ") + name; } TextLabel3D* residueLabel = createLabel(text, pos, radius, color); @@ -344,36 +352,36 @@ void Label::processAtom(const Core::Molecule& molecule, continue; } - auto& interface = m_layerManager.getSetting(layer); + auto* interface = m_layerManager.getSetting(layer); std::string text = ""; - if (interface.atomOptions & LayerLabel::LabelOptions::PartialCharge) { + if (interface->atomOptions & LayerLabel::LabelOptions::PartialCharge) { QString charge = partialCharge(const_cast(&molecule), i); text += charge.toStdString(); } - if (interface.atomOptions & LayerLabel::LabelOptions::Custom) { + if (interface->atomOptions & LayerLabel::LabelOptions::Custom) { text += (text == "" ? "" : " / ") + atom.label(); } - if (interface.atomOptions & LayerLabel::LabelOptions::Index) { + if (interface->atomOptions & LayerLabel::LabelOptions::Index) { text += (text == "" ? "" : " / ") + std::to_string(atom.index() + 1); } - if (interface.atomOptions & LayerLabel::LabelOptions::Name) { + if (interface->atomOptions & LayerLabel::LabelOptions::Name) { text += (text == "" ? "" : " / ") + std::string(Elements::symbol(atomicNumber)); } - if (interface.atomOptions & LayerLabel::LabelOptions::Ordinal) { + if (interface->atomOptions & LayerLabel::LabelOptions::Ordinal) { text += (text == "" ? "" : " / ") + std::string(Elements::symbol(atomicNumber) + std::to_string(atomCount[atomicNumber])); } - if (interface.atomOptions & LayerLabel::LabelOptions::UniqueID) { + if (interface->atomOptions & LayerLabel::LabelOptions::UniqueID) { text += (text == "" ? "" : " / ") + std::to_string(atom.index()); } if (text != "") { const Vector3f pos(atom.position3d().cast()); - Vector3ub color = interface.color; + Vector3ub color = interface->color; float radius = static_cast(Elements::radiusVDW(atomicNumber)) * - interface.radiusScalar; + interface->radiusScalar; TextLabel3D* atomLabel = createLabel(text, pos, radius, color); geometry->addDrawable(atomLabel); @@ -383,11 +391,11 @@ void Label::processAtom(const Core::Molecule& molecule, void Label::setColor(const QColor& color) { - auto& interface = m_layerManager.getSetting(); + auto* interface = m_layerManager.getSetting(); - interface.color[0] = static_cast(color.red()); - interface.color[1] = static_cast(color.green()); - interface.color[2] = static_cast(color.blue()); + interface->color[0] = static_cast(color.red()); + interface->color[1] = static_cast(color.green()); + interface->color[2] = static_cast(color.blue()); emit drawablesChanged(); @@ -397,39 +405,39 @@ void Label::setColor(const QColor& color) void Label::atomLabelType(int index) { - auto& interface = m_layerManager.getSetting(); - interface.atomOptions = char(setupWidget() - ->findChildren("atom")[0] - ->itemData(index) - .toInt()); + auto* interface = m_layerManager.getSetting(); + interface->atomOptions = char(setupWidget() + ->findChildren("atom")[0] + ->itemData(index) + .toInt()); emit drawablesChanged(); } void Label::residueLabelType(int index) { - auto& interface = m_layerManager.getSetting(); - interface.residueOptions = char(setupWidget() - ->findChildren("residue")[0] - ->itemData(index) - .toInt()); + auto* interface = m_layerManager.getSetting(); + interface->residueOptions = char(setupWidget() + ->findChildren("residue")[0] + ->itemData(index) + .toInt()); emit drawablesChanged(); } void Label::setRadiusScalar(double radius) { - auto& interface = m_layerManager.getSetting(); - interface.radiusScalar = float(radius); + auto* interface = m_layerManager.getSetting(); + interface->radiusScalar = float(radius); emit drawablesChanged(); QSettings settings; - settings.setValue("label/radiusScalar", interface.radiusScalar); + settings.setValue("label/radiusScalar", interface->radiusScalar); } QWidget* Label::setupWidget() { - auto& interface = m_layerManager.getSetting(); - interface.setupWidget(this); - return interface.widget; + auto* interface = m_layerManager.getSetting(); + interface->setupWidget(this); + return interface->widget; } } // namespace Avogadro::QtPlugins diff --git a/avogadro/qtplugins/vanderwaals/vanderwaals.cpp b/avogadro/qtplugins/vanderwaals/vanderwaals.cpp index b7a8bbc86d..2511a6a9e8 100644 --- a/avogadro/qtplugins/vanderwaals/vanderwaals.cpp +++ b/avogadro/qtplugins/vanderwaals/vanderwaals.cpp @@ -11,6 +11,10 @@ #include #include +#include +#include +#include + namespace Avogadro::QtPlugins { using Core::Elements; @@ -19,9 +23,66 @@ using Rendering::GeometryNode; using Rendering::GroupNode; using Rendering::SphereGeometry; +struct LayerVdW : Core::LayerData +{ + QWidget* widget; + float opacity; + + LayerVdW() + { + widget = nullptr; + QSettings settings; + opacity = settings.value("vdw/opacity", 1.0).toFloat(); + } + + LayerVdW(std::string settings) + { + widget = nullptr; + deserialize(settings); + } + + LayerData* clone() final { return new LayerVdW(*this); } + + ~LayerVdW() 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(VanDerWaals* 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, + &VanDerWaals::setOpacity); + + form->addRow(QObject::tr("Opacity:"), slider); + widget->setLayout(form); + } + } +}; + VanDerWaals::VanDerWaals(QObject* p) : ScenePlugin(p) { m_layerManager = PluginLayerManager(m_name); + + QSettings settings; } VanDerWaals::~VanDerWaals() {} @@ -29,17 +90,27 @@ VanDerWaals::~VanDerWaals() {} void VanDerWaals::process(const QtGui::Molecule& molecule, Rendering::GroupNode& node) { + m_layerManager.load(); + // Add a sphere node to contain all of the VdW spheres. auto* geometry = new GeometryNode; node.addChild(geometry); auto* spheres = new SphereGeometry; spheres->identifier().molecule = &molecule; spheres->identifier().type = Rendering::AtomType; + + auto* translucentSpheres = new SphereGeometry; + translucentSpheres->setRenderPass(Rendering::TranslucentPass); + translucentSpheres->identifier().molecule = &molecule; + translucentSpheres->identifier().type = Rendering::AtomType; + auto selectedSpheres = new SphereGeometry; selectedSpheres->setOpacity(0.42); + selectedSpheres->setRenderPass(Rendering::TranslucentPass); geometry->addDrawable(spheres); geometry->addDrawable(selectedSpheres); + geometry->addDrawable(translucentSpheres); for (Index i = 0; i < molecule.atomCount(); ++i) { Core::Atom atom = molecule.atom(i); @@ -50,7 +121,15 @@ void VanDerWaals::process(const QtGui::Molecule& molecule, Vector3ub color = atom.color(); auto radius = static_cast(Elements::radiusVDW(atomicNumber)); - spheres->addSphere(atom.position3d().cast(), color, radius, i); + float 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); radius += 0.3f; @@ -60,4 +139,24 @@ void VanDerWaals::process(const QtGui::Molecule& molecule, } } -} // namespace Avogadro +void VanDerWaals::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("vdw/opacity", m_opacity); +} + +QWidget* VanDerWaals::setupWidget() +{ + auto* interface = m_layerManager.getSetting(); + interface->setupWidget(this); + return interface->widget; +} + +} // namespace Avogadro::QtPlugins diff --git a/avogadro/qtplugins/vanderwaals/vanderwaals.h b/avogadro/qtplugins/vanderwaals/vanderwaals.h index a7708b5b60..02e3cd8064 100644 --- a/avogadro/qtplugins/vanderwaals/vanderwaals.h +++ b/avogadro/qtplugins/vanderwaals/vanderwaals.h @@ -38,8 +38,16 @@ class VanDerWaals : 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 = "Van der Waals"; + QWidget* m_setupWidget = nullptr; + float m_opacity = 1.0; }; } // namespace QtPlugins } // namespace Avogadro diff --git a/avogadro/qtplugins/vanderwaalsao/CMakeLists.txt b/avogadro/qtplugins/vanderwaalsao/CMakeLists.txt deleted file mode 100644 index 8bacbbf4ad..0000000000 --- a/avogadro/qtplugins/vanderwaalsao/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -avogadro_plugin(VanDerWaalsAO - "Van der Waals rendering scheme with ambient occlusion" - ScenePlugin - vanderwaalsao.h - VanDerWaalsAO - vanderwaalsao.cpp - "") - -target_link_libraries(VanDerWaalsAO PRIVATE Avogadro::Rendering) diff --git a/avogadro/qtplugins/vanderwaalsao/vanderwaalsao.cpp b/avogadro/qtplugins/vanderwaalsao/vanderwaalsao.cpp deleted file mode 100644 index 2d0d3c572b..0000000000 --- a/avogadro/qtplugins/vanderwaalsao/vanderwaalsao.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/****************************************************************************** - This source file is part of the Avogadro project. - This source code is released under the 3-Clause BSD License, (see "LICENSE"). -******************************************************************************/ - -#include "vanderwaalsao.h" - -#include -#include -#include -#include -#include - -namespace Avogadro::QtPlugins { - -using Core::Elements; -using QtGui::PluginLayerManager; -using Rendering::AmbientOcclusionSphereGeometry; -using Rendering::GeometryNode; -using Rendering::GroupNode; - -VanDerWaalsAO::VanDerWaalsAO(QObject* p) : ScenePlugin(p) -{ - m_layerManager = PluginLayerManager(m_name); -} - -VanDerWaalsAO::~VanDerWaalsAO() {} - -void VanDerWaalsAO::process(const QtGui::Molecule& molecule, - Rendering::GroupNode& node) -{ - // Add a sphere node to contain all of the VdW spheres. - auto* geometry = new GeometryNode; - node.addChild(geometry); - auto* spheres = new AmbientOcclusionSphereGeometry; - spheres->identifier().molecule = &molecule; - spheres->identifier().type = Rendering::AtomType; - geometry->addDrawable(spheres); - - for (size_t i = 0; i < molecule.atomCount(); ++i) { - Core::Atom atom = molecule.atom(i); - if (!m_layerManager.atomEnabled(i)) { - continue; - } - unsigned char atomicNumber = atom.atomicNumber(); - const unsigned char* c = Elements::color(atomicNumber); - Vector3ub color(c[0], c[1], c[2]); - spheres->addSphere(atom.position3d().cast(), color, - static_cast(Elements::radiusVDW(atomicNumber)), - i); - } -} - -} // namespace Avogadro diff --git a/avogadro/qtplugins/vanderwaalsao/vanderwaalsao.h b/avogadro/qtplugins/vanderwaalsao/vanderwaalsao.h deleted file mode 100644 index 7d7953eac1..0000000000 --- a/avogadro/qtplugins/vanderwaalsao/vanderwaalsao.h +++ /dev/null @@ -1,47 +0,0 @@ -/****************************************************************************** - This source file is part of the Avogadro project. - This source code is released under the 3-Clause BSD License, (see "LICENSE"). -******************************************************************************/ - -#ifndef AVOGADRO_QTPLUGINS_VANDERWAALSAO_H -#define AVOGADRO_QTPLUGINS_VANDERWAALSAO_H - -#include - -namespace Avogadro { -namespace QtPlugins { - -/** - * @brief Render the molecule as Van der Waals spheres with ambient occlusion. - * @author Tim Vandermeersch - */ -class VanDerWaalsAO : public QtGui::ScenePlugin -{ - Q_OBJECT - -public: - explicit VanDerWaalsAO(QObject* parent = nullptr); - ~VanDerWaalsAO() override; - - void process(const QtGui::Molecule& molecule, - Rendering::GroupNode& node) override; - - QString name() const override { return tr("Van der Waals (AO)", "ambient occlusion"); } - - QString description() const override - { - return tr("Simple display of VdW spheres with ambient occlusion."); - } - - DefaultBehavior defaultBehavior() const override - { - return DefaultBehavior::False; - } - -private: - std::string m_name = "Van der Waals (AO)"; -}; -} // namespace QtPlugins -} // namespace Avogadro - -#endif // AVOGADRO_QTPLUGINS_VANDERWAALSAO_H diff --git a/avogadro/qtplugins/wireframe/wireframe.cpp b/avogadro/qtplugins/wireframe/wireframe.cpp index e34f084e63..f1c1a5fd62 100644 --- a/avogadro/qtplugins/wireframe/wireframe.cpp +++ b/avogadro/qtplugins/wireframe/wireframe.cpp @@ -47,6 +47,14 @@ struct LayerWireframe : Core::LayerData lineWidth = settings.value("wireframe/lineWidth", 1.0).toDouble(); } + LayerWireframe(std::string settings) + { + widget = nullptr; + deserialize(settings); + } + + LayerData* clone() final { return new LayerWireframe(*this); } + ~LayerWireframe() override { if (widget) @@ -143,11 +151,11 @@ void Wireframe::process(const QtGui::Molecule& molecule, bond.atom2().index())) { continue; } - auto& interface1 = m_layerManager.getSetting( + auto* interface1 = m_layerManager.getSetting( m_layerManager.getLayerID(bond.atom1().index())); - auto& interface2 = m_layerManager.getSetting( + auto* interface2 = m_layerManager.getSetting( m_layerManager.getLayerID(bond.atom2().index())); - if (!interface1.showHydrogens && !interface2.showHydrogens && + if (!interface1->showHydrogens && !interface2->showHydrogens && (bond.atom1().atomicNumber() == 1 || bond.atom2().atomicNumber() == 1)) { continue; @@ -162,9 +170,9 @@ void Wireframe::process(const QtGui::Molecule& molecule, points.push_back(pos2); colors.push_back(color1); colors.push_back(color2); - float lineWidth = interface1.lineWidth; + float lineWidth = interface1->lineWidth; - if (interface1.multiBonds || interface2.multiBonds) + if (interface1->multiBonds || interface2->multiBonds) lineWidth *= bond.order(); lines->addLineStrip(points, colors, lineWidth); // add small spheres to allow the selection tool to work @@ -180,16 +188,16 @@ void Wireframe::process(const QtGui::Molecule& molecule, QWidget* Wireframe::setupWidget() { - auto& interface = m_layerManager.getSetting(); - interface.setupWidget(this); - return interface.widget; + auto* interface = m_layerManager.getSetting(); + interface->setupWidget(this); + return interface->widget; } void Wireframe::multiBonds(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.multiBonds) { - interface.multiBonds = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->multiBonds) { + interface->multiBonds = show; emit drawablesChanged(); } QSettings settings; @@ -198,9 +206,9 @@ void Wireframe::multiBonds(bool show) void Wireframe::showHydrogens(bool show) { - auto& interface = m_layerManager.getSetting(); - if (show != interface.showHydrogens) { - interface.showHydrogens = show; + auto* interface = m_layerManager.getSetting(); + if (show != interface->showHydrogens) { + interface->showHydrogens = show; emit drawablesChanged(); } QSettings settings; @@ -209,12 +217,12 @@ void Wireframe::showHydrogens(bool show) void Wireframe::setWidth(double width) { - auto& interface = m_layerManager.getSetting(); - interface.lineWidth = float(width); + auto* interface = m_layerManager.getSetting(); + interface->lineWidth = float(width); emit drawablesChanged(); QSettings settings; - settings.setValue("wireframe/lineWidth", interface.lineWidth); + settings.setValue("wireframe/lineWidth", width); } -} // namespace Avogadro +} // namespace Avogadro::QtPlugins diff --git a/avogadro/rendering/cylindergeometry.cpp b/avogadro/rendering/cylindergeometry.cpp index c56d15ac4d..29d244aa68 100644 --- a/avogadro/rendering/cylindergeometry.cpp +++ b/avogadro/rendering/cylindergeometry.cpp @@ -17,7 +17,7 @@ namespace { #include "cylinders_fs.h" #include "cylinders_vs.h" -} +} // namespace #include "avogadrogl.h" @@ -111,9 +111,8 @@ void CylinderGeometry::update() // Cylinder ColorNormalVertex vert(itCylinder->color, -direction, position1); ColorNormalVertex vert2(itCylinder->color2, -direction, position1); - const auto tubeStart = - static_cast(cylinderVertices.size()); - for (auto & radial : radials) { + const auto tubeStart = static_cast(cylinderVertices.size()); + for (auto& radial : radials) { vert.normal = radial; vert.vertex = position1 + radial; cylinderVertices.push_back(vert); @@ -163,12 +162,6 @@ void CylinderGeometry::update() d->program->attachShader(*d->fragmentShader); if (!d->program->link()) cout << d->program->error() << endl; - -/* d->program->detachShader(d->vertexShader); - d->program->detachShader(d->fragmentShader); - d->vertexShader.cleanup(); - d->fragmentShader.cleanup(); - */ } } @@ -189,23 +182,23 @@ void CylinderGeometry::render(const Camera& camera) // Set up our attribute arrays. if (!d->program->enableAttributeArray("vertex")) cout << d->program->error() << endl; - if (!d->program->useAttributeArray("vertex", ColorNormalVertex::vertexOffset(), - sizeof(ColorNormalVertex), FloatType, 3, - ShaderProgram::NoNormalize)) { + if (!d->program->useAttributeArray( + "vertex", ColorNormalVertex::vertexOffset(), sizeof(ColorNormalVertex), + FloatType, 3, ShaderProgram::NoNormalize)) { cout << d->program->error() << endl; } if (!d->program->enableAttributeArray("color")) cout << d->program->error() << endl; if (!d->program->useAttributeArray("color", ColorNormalVertex::colorOffset(), - sizeof(ColorNormalVertex), UCharType, 3, - ShaderProgram::Normalize)) { + sizeof(ColorNormalVertex), UCharType, 3, + ShaderProgram::Normalize)) { cout << d->program->error() << endl; } if (!d->program->enableAttributeArray("normal")) cout << d->program->error() << endl; - if (!d->program->useAttributeArray("normal", ColorNormalVertex::normalOffset(), - sizeof(ColorNormalVertex), FloatType, 3, - ShaderProgram::NoNormalize)) { + if (!d->program->useAttributeArray( + "normal", ColorNormalVertex::normalOffset(), sizeof(ColorNormalVertex), + FloatType, 3, ShaderProgram::NoNormalize)) { cout << d->program->error() << endl; } @@ -213,7 +206,11 @@ void CylinderGeometry::render(const Camera& camera) if (!d->program->setUniformValue("modelView", camera.modelView().matrix())) { cout << d->program->error() << endl; } - if (!d->program->setUniformValue("projection", camera.projection().matrix())) { + if (!d->program->setUniformValue("projection", + camera.projection().matrix())) { + cout << d->program->error() << endl; + } + if (!d->program->setUniformValue("opacity", m_opacity)) { cout << d->program->error() << endl; } Matrix3f normalMatrix = camera.modelView().linear().inverse().transpose(); @@ -330,4 +327,4 @@ void CylinderGeometry::clear() m_indexMap.clear(); } -} // End namespace Avogadro +} // namespace Avogadro::Rendering diff --git a/avogadro/rendering/cylindergeometry.h b/avogadro/rendering/cylindergeometry.h index b11963f4bd..a16402314a 100644 --- a/avogadro/rendering/cylindergeometry.h +++ b/avogadro/rendering/cylindergeometry.h @@ -17,12 +17,9 @@ struct CylinderColor { CylinderColor(const Vector3f& pos1, const Vector3f& pos2, float r, const Vector3ub& c, const Vector3ub& c2 = Vector3ub::Zero()) - : end1(pos1) - , end2(pos2) - , radius(r) - , color(c) - , color2(c2) - {} + : end1(pos1), end2(pos2), radius(r), color(c), color2(c2) + { + } Vector3f end1; Vector3f end2; @@ -136,10 +133,21 @@ class AVOGADRORENDERING_EXPORT CylinderGeometry : public Drawable */ size_t size() const { return m_cylinders.size(); } + /** + * Set the opacity of the cylinders in this group. + */ + void setOpacity(float o) + { + m_opacity = o; + if (o < 1.0f) + setRenderPass(TranslucentPass); + } + private: std::vector m_cylinders; std::vector m_indices; std::map m_indexMap; + float m_opacity = 1.0f; bool m_dirty; diff --git a/avogadro/rendering/cylinders_fs.glsl b/avogadro/rendering/cylinders_fs.glsl index e8dddf5f51..e6b2802f97 100644 --- a/avogadro/rendering/cylinders_fs.glsl +++ b/avogadro/rendering/cylinders_fs.glsl @@ -1,5 +1,7 @@ varying vec3 fnormal; +uniform float opacity; + void main() { vec3 N = normalize(fnormal); @@ -12,5 +14,5 @@ void main() vec4 diffuse = 0.55 * gl_Color; vec4 specular = 0.5 * (vec4(1, 1, 1, 1) - gl_Color); gl_FragColor = ambient + df * diffuse + pow(sf, 20.0) * specular; - gl_FragColor.a = 1.0; + gl_FragColor.a = opacity; }