1 /******************************************************************************
2 
3   This source file is part of the Avogadro project.
4 
5   Copyright 2012 Kitware, Inc.
6 
7   This source code is released under the New BSD License, (the "License").
8 
9   Unless required by applicable law or agreed to in writing, software
10   distributed under the License is distributed on an "AS IS" BASIS,
11   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   See the License for the specific language governing permissions and
13   limitations under the License.
14 
15 ******************************************************************************/
16 
17 #include "ballandstick.h"
18 
19 #include <avogadro/core/elements.h>
20 #include <avogadro/core/molecule.h>
21 #include <avogadro/qtgui/rwmolecule.h>
22 #include <avogadro/rendering/cylindergeometry.h>
23 #include <avogadro/rendering/geometrynode.h>
24 #include <avogadro/rendering/groupnode.h>
25 #include <avogadro/rendering/spheregeometry.h>
26 
27 #include <QtWidgets/QCheckBox>
28 #include <QtWidgets/QDoubleSpinBox>
29 #include <QtWidgets/QHBoxLayout>
30 #include <QtWidgets/QLabel>
31 #include <QtWidgets/QVBoxLayout>
32 #include <QtWidgets/QWidget>
33 
34 namespace Avogadro {
35 namespace QtPlugins {
36 
37 using Core::Elements;
38 using Core::Molecule;
39 using Rendering::GeometryNode;
40 using Rendering::GroupNode;
41 using Rendering::SphereGeometry;
42 using Rendering::CylinderGeometry;
43 
BallAndStick(QObject * p)44 BallAndStick::BallAndStick(QObject* p)
45   : ScenePlugin(p), m_enabled(true), m_group(nullptr), m_setupWidget(nullptr),
46     m_multiBonds(true), m_showHydrogens(true)
47 {
48 }
49 
~BallAndStick()50 BallAndStick::~BallAndStick()
51 {
52   if (m_setupWidget)
53     m_setupWidget->deleteLater();
54 }
55 
process(const Molecule & molecule,Rendering::GroupNode & node)56 void BallAndStick::process(const Molecule& molecule, Rendering::GroupNode& node)
57 {
58   // Add a sphere node to contain all of the spheres.
59   m_group = &node;
60   GeometryNode* geometry = new GeometryNode;
61   node.addChild(geometry);
62   SphereGeometry* spheres = new SphereGeometry;
63   auto selectedSpheres = new SphereGeometry;
64   selectedSpheres->setOpacity(0.42);
65   spheres->identifier().molecule = reinterpret_cast<const void*>(&molecule);
66   spheres->identifier().type = Rendering::AtomType;
67   geometry->addDrawable(spheres);
68   geometry->addDrawable(selectedSpheres);
69 
70   for (Index i = 0; i < molecule.atomCount(); ++i) {
71     Core::Atom atom = molecule.atom(i);
72     unsigned char atomicNumber = atom.atomicNumber();
73     if (atomicNumber == 1 && !m_showHydrogens)
74       continue;
75     Vector3ub color = atom.color();
76     float radius = static_cast<float>(Elements::radiusVDW(atomicNumber));
77     spheres->addSphere(atom.position3d().cast<float>(), color, radius * 0.3f);
78     if (atom.selected()) {
79       color = Vector3ub(0, 0, 255);
80       radius *= 1.2;
81       selectedSpheres->addSphere(atom.position3d().cast<float>(), color,
82                                  radius * 0.3f);
83     }
84   }
85 
86   float bondRadius = 0.1f;
87   CylinderGeometry* cylinders = new CylinderGeometry;
88   cylinders->identifier().molecule = &molecule;
89   cylinders->identifier().type = Rendering::BondType;
90   geometry->addDrawable(cylinders);
91   for (Index i = 0; i < molecule.bondCount(); ++i) {
92     Core::Bond bond = molecule.bond(i);
93     if (!m_showHydrogens && (bond.atom1().atomicNumber() == 1 ||
94                              bond.atom2().atomicNumber() == 1)) {
95       continue;
96     }
97     Vector3f pos1 = bond.atom1().position3d().cast<float>();
98     Vector3f pos2 = bond.atom2().position3d().cast<float>();
99     Vector3ub color1 = bond.atom1().color();
100     Vector3ub color2 = bond.atom2().color();
101     Vector3f bondVector = pos2 - pos1;
102     float bondLength = bondVector.norm();
103     bondVector /= bondLength;
104     switch (m_multiBonds ? bond.order() : 1) {
105       case 3: {
106         Vector3f delta = bondVector.unitOrthogonal() * (2.0f * bondRadius);
107         cylinders->addCylinder(pos1 + delta, pos2 + delta, bondRadius, color1,
108                                color2, i);
109         cylinders->addCylinder(pos1 - delta, pos2 - delta, bondRadius, color1,
110                                color2, i);
111       }
112       default:
113       case 1:
114         cylinders->addCylinder(pos1, pos2, bondRadius, color1, color2, i);
115         break;
116       case 2: {
117         Vector3f delta = bondVector.unitOrthogonal() * bondRadius;
118         cylinders->addCylinder(pos1 + delta, pos2 + delta, bondRadius, color1,
119                                color2, i);
120         cylinders->addCylinder(pos1 - delta, pos2 - delta, bondRadius, color1,
121                                color2, i);
122       }
123     }
124   }
125 }
126 
processEditable(const QtGui::RWMolecule & molecule,Rendering::GroupNode & node)127 void BallAndStick::processEditable(const QtGui::RWMolecule& molecule,
128                                    Rendering::GroupNode& node)
129 {
130   // Add a sphere node to contain all of the spheres.
131   m_group = &node;
132   GeometryNode* geometry = new GeometryNode;
133   node.addChild(geometry);
134   SphereGeometry* spheres = new SphereGeometry;
135   spheres->identifier().molecule = &molecule;
136   spheres->identifier().type = Rendering::AtomType;
137   geometry->addDrawable(spheres);
138 
139   for (Index i = 0; i < molecule.atomCount(); ++i) {
140     QtGui::RWAtom atom = molecule.atom(i);
141     unsigned char atomicNumber = atom.atomicNumber();
142     if (atomicNumber == 1 && !m_showHydrogens)
143       continue;
144 
145     Vector3ub color = atom.color();
146     spheres->addSphere(atom.position3d().cast<float>(), color,
147                        static_cast<float>(Elements::radiusVDW(atomicNumber)) *
148                          0.3f);
149   }
150 
151   float bondRadius = 0.1f;
152   CylinderGeometry* cylinders = new CylinderGeometry;
153   cylinders->identifier().molecule = &molecule;
154   cylinders->identifier().type = Rendering::BondType;
155   geometry->addDrawable(cylinders);
156   for (Index i = 0; i < molecule.bondCount(); ++i) {
157     QtGui::RWBond bond = molecule.bond(i);
158     if (!m_showHydrogens && (bond.atom1().atomicNumber() == 1 ||
159                              bond.atom2().atomicNumber() == 1)) {
160       continue;
161     }
162     Vector3f pos1 = bond.atom1().position3d().cast<float>();
163     Vector3f pos2 = bond.atom2().position3d().cast<float>();
164     Vector3ub color1 = bond.atom1().color();
165     Vector3ub color2 = bond.atom2().color();
166     Vector3f bondVector = pos2 - pos1;
167     float bondLength = bondVector.norm();
168     bondVector /= bondLength;
169     switch (m_multiBonds ? bond.order() : 1) {
170       case 3: {
171         Vector3f delta = bondVector.unitOrthogonal() * (2.0f * bondRadius);
172         cylinders->addCylinder(pos1 + delta, pos2 + delta, bondRadius, color1,
173                                color2, i);
174         cylinders->addCylinder(pos1 - delta, pos2 - delta, bondRadius, color1,
175                                color2, i);
176       }
177       default:
178       case 1:
179         cylinders->addCylinder(pos1, pos2, bondRadius, color1, color2, i);
180         break;
181       case 2: {
182         Vector3f delta = bondVector.unitOrthogonal() * bondRadius;
183         cylinders->addCylinder(pos1 + delta, pos2 + delta, bondRadius, color1,
184                                color2, i);
185         cylinders->addCylinder(pos1 - delta, pos2 - delta, bondRadius, color1,
186                                color2, i);
187       }
188     }
189   }
190 }
191 
isEnabled() const192 bool BallAndStick::isEnabled() const
193 {
194   return m_enabled;
195 }
196 
setEnabled(bool enable)197 void BallAndStick::setEnabled(bool enable)
198 {
199   m_enabled = enable;
200 }
201 
setupWidget()202 QWidget* BallAndStick::setupWidget()
203 {
204   if (!m_setupWidget) {
205     m_setupWidget = new QWidget(qobject_cast<QWidget*>(parent()));
206     QVBoxLayout* v = new QVBoxLayout;
207     QCheckBox* check = new QCheckBox(tr("Show multiple bonds?"));
208     check->setChecked(m_multiBonds);
209     connect(check, SIGNAL(clicked(bool)), SLOT(multiBonds(bool)));
210     v->addWidget(check);
211     check = new QCheckBox(tr("Show hydrogens?"));
212     check->setChecked(m_showHydrogens);
213     connect(check, SIGNAL(toggled(bool)), SLOT(showHydrogens(bool)));
214     v->addWidget(check);
215     m_setupWidget->setLayout(v);
216   }
217   return m_setupWidget;
218 }
219 
multiBonds(bool show)220 void BallAndStick::multiBonds(bool show)
221 {
222   if (show != m_multiBonds) {
223     m_multiBonds = show;
224     emit drawablesChanged();
225   }
226 }
227 
showHydrogens(bool show)228 void BallAndStick::showHydrogens(bool show)
229 {
230   if (show != m_showHydrogens) {
231     m_showHydrogens = show;
232     emit drawablesChanged();
233   }
234 }
235 }
236 }
237