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