source: src/UIElements/Views/Qt4/Qt3D/GLWorldView.cpp@ 16bd37

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 16bd37 was 7c7c4a, checked in by Frederik Heber <heber@…>, 9 years ago

FIX: All ObservedValue's of GLMoleculeObject_atom/bond/molecule wrapped into vector.

  • the idea is that a GLMoleculeObject may only remove itself _after_ each and every contained Observer has gotten the subjectKilled() signal from the Observables. Only then will destroying the Object and its members thereby not cause any signOff() which try to access Observables or their channels which are no longer present. This can be imagined as a graph where we have to start destroying object at the very bottom.
  • This is the avoid the following conflict: A superior object gets note of a molecule to be removed. It sends the visual representation a signal to remove itself, which causes it to use signOff(). On a parallel track (in another thread) we have the observed object calling subjectKilled() to inform any Observer about its immediate destruction. These two tracks collide. Now, we let first pass all subjectKilled() and when the last Observable has gotten its signal, we begin destroying the visual rep.
  • rerouted signal/slots accordingly.
  • Property mode set to 100644
File size: 28.7 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010-2012 University of Bonn. All rights reserved.
5 * Copyright (C) 2013 Frederik Heber. All rights reserved.
6 *
7 *
8 * This file is part of MoleCuilder.
9 *
10 * MoleCuilder is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * MoleCuilder is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24/*
25 * GLWorldView.cpp
26 *
27 * Created on: Aug 1, 2010
28 * Author: heber
29 */
30
31// include config.h
32#ifdef HAVE_CONFIG_H
33#include <config.h>
34#endif
35
36#include "GLWorldView.hpp"
37
38#include <Qt/qevent.h>
39#include <Qt/qaction.h>
40#include <QtGui/QMenu>
41#include <QtGui/QToolBar>
42#include <QtGui/QToolButton>
43#include <Qt/qtimer.h>
44#include <Qt/qsettings.h>
45#include <Qt3D/qglbuilder.h>
46#include <Qt3D/qglscenenode.h>
47#include <Qt3D/qglsphere.h>
48#include <Qt3D/qglcylinder.h>
49#include <Qt3D/qglcube.h>
50
51#include "GLWorldScene.hpp"
52
53#include "CodePatterns/MemDebug.hpp"
54
55#include "Atom/AtomObserver.hpp"
56#include "Atom/atom_observable.hpp"
57#include "Box.hpp"
58#include "CodePatterns/Log.hpp"
59#include "CodePatterns/Observer/Notification.hpp"
60#include "CodePatterns/Observer/ObserverLog.hpp"
61#include "Descriptors/MoleculeIdDescriptor.hpp"
62#include "molecule.hpp"
63#include "Shapes/ShapeRegistry.hpp"
64#include "World.hpp"
65#include "WorldTime.hpp"
66
67GLWorldView::GLWorldView(QWidget *parent)
68 : QGLView(parent), Observer("GLWorldView"), worldscene(NULL), changesPresent(false), needsRedraw(false)
69{
70 worldscene = new GLWorldScene(this);
71
72 setOption(QGLView::ObjectPicking, true);
73 setOption(QGLView::CameraNavigation, false);
74 setFocusPolicy(Qt::StrongFocus);
75 setCameraControlMode(Rotate);
76 defaultEyeSeparation = 4.0;
77
78 createDomainBox();
79 createDreiBein();
80 //changeMaterials(false);
81
82 qRegisterMetaType<atomId_t>("atomId_t");
83 qRegisterMetaType<moleculeId_t>("moleculeId_t");
84
85 connect(this, SIGNAL(ShapeAdded(const std::string &)), worldscene, SLOT(addShape(const std::string &)));
86 connect(this, SIGNAL(ShapeRemoved(const std::string &)), worldscene, SLOT(removeShape(const std::string &)));
87// connect(this, SIGNAL(TimeChanged()), worldscene, SIGNAL(updated()));
88 connect(worldscene, SIGNAL(changeOccured()), this, SLOT(changeSignalled()));
89 connect(worldscene, SIGNAL(changed()), this, SIGNAL(changed()));
90 connect(worldscene, SIGNAL(hoverChanged(const atomId_t)), this, SLOT(sceneHoverSignalled(const atomId_t)));
91 connect(worldscene, SIGNAL(hoverChanged(const moleculeId_t, int)), this, SLOT(sceneHoverSignalled(const moleculeId_t, int)));
92 connect(this, SIGNAL(atomRemoved(const moleculeId_t, const atomId_t)), worldscene, SLOT(atomRemoved(const moleculeId_t, const atomId_t)), Qt::DirectConnection);
93 connect(this, SIGNAL(atomInserted(const moleculeId_t, const atomId_t)), worldscene, SLOT(atomInserted(const moleculeId_t, const atomId_t)), Qt::DirectConnection);
94 connect(this, SIGNAL(moleculeInserted(const moleculeId_t)), worldscene, SLOT(moleculePrepareInserted(const moleculeId_t)), Qt::DirectConnection);
95 //connect(this, SIGNAL(changed()), this, SLOT(updateGL()));
96 connect(this, SIGNAL(changed()), this, SLOT(sceneChangeSignalled()));
97 connect(this, SIGNAL(moleculesVisibilityChanged(const moleculeId_t,bool)), worldscene, SLOT(moleculesVisibilityChanged(const moleculeId_t,bool)));
98
99 // sign on to changes in the world
100 World::getInstance().signOn(this);
101 World::getInstance().signOn(this, World::MoleculeInserted);
102 World::getInstance().signOn(this, World::SelectionChanged);
103// WorldTime::getInstance().signOn(this, WorldTime::TimeChanged);
104 AtomObserver::getInstance().signOn(this, AtomObservable::PositionChanged);
105
106 ShapeRegistry::getInstance().signOn(this);
107 ShapeRegistry::getInstance().signOn(this, ShapeRegistry::ShapeInserted);
108 ShapeRegistry::getInstance().signOn(this, ShapeRegistry::ShapeRemoved);
109 ShapeRegistry::getInstance().signOn(this, ShapeRegistry::SelectionChanged);
110
111 redrawTimer = new QTimer(this);
112}
113
114GLWorldView::~GLWorldView()
115{
116 // remove me from all observed molecules
117 for (ObservedMolecules_t::iterator iter = ObservedMolecules.begin();
118 !ObservedMolecules.empty();
119 iter = ObservedMolecules.begin())
120 signOffFromMolecule(*iter);
121
122 QSettings settings;
123 settings.beginGroup("WorldView");
124 settings.setValue("domainBoxEnabled", (meshDomainBox->options() & QGLSceneNode::HideNode) == 0);
125 settings.setValue("dreiBeinEnabled", (meshDreiBein->options() & QGLSceneNode::HideNode) == 0);
126 settings.endGroup();
127
128
129 World::getInstance().signOff(this);
130 World::getInstance().signOff(this, World::MoleculeInserted);
131 World::getInstance().signOff(this, World::SelectionChanged);
132// WorldTime::getInstance().signOff(this, WorldTime::TimeChanged);
133 AtomObserver::getInstance().signOff(this, AtomObservable::PositionChanged);
134 ShapeRegistry::getInstance().signOff(this);
135 ShapeRegistry::getInstance().signOff(this, ShapeRegistry::ShapeInserted);
136 ShapeRegistry::getInstance().signOff(this, ShapeRegistry::ShapeRemoved);
137 ShapeRegistry::getInstance().signOff(this, ShapeRegistry::SelectionChanged);
138 delete worldscene;
139
140 delete(domainBoxMaterial);
141 for (int i=0;i<3;i++)
142 delete(dreiBeinMaterial[i]);
143}
144
145
146/**
147 * Add some widget specific actions to the toolbar:
148 * - camera rotation/translation mode
149 * - camera fit to domain
150 */
151void GLWorldView::addToolBarActions(QToolBar *toolbar)
152{
153 // camera control mode
154 toolbar->addSeparator();
155 QAction *transAction = new QAction(QIcon::fromTheme("forward"), tr("camera translation mode"), this);
156 connect(transAction, SIGNAL(triggered()), this, SLOT(setCameraControlModeTranslation()));
157 toolbar->addAction(transAction);
158 QAction *rotAction = new QAction(QIcon::fromTheme("object-rotate-left"), tr("camera rotation mode"), this);
159 connect(rotAction, SIGNAL(triggered()), this, SLOT(setCameraControlModeRotation()));
160 toolbar->addAction(rotAction);
161 QAction *fitAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("camera fit to domain"), this);
162 connect(fitAction, SIGNAL(triggered()), this, SLOT(fitCameraToDomain()));
163 toolbar->addAction(fitAction);
164
165 // stereo mode
166 QToolButton *stereoButton = new QToolButton(toolbar);
167 QMenu *stereoMenu = new QMenu();
168 QAction *stereoDisableAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("disable"), this);
169 connect(stereoDisableAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeDisable()));
170 stereoMenu->addAction(stereoDisableAction);
171 QAction *stereoHardwareAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("hardware"), this);
172 connect(stereoHardwareAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeHardware()));
173 stereoMenu->addAction(stereoHardwareAction);
174 QAction *stereoLeftRightAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("left right"), this);
175 connect(stereoLeftRightAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeLeftRight()));
176 stereoMenu->addAction(stereoLeftRightAction);
177 QAction *stereoRightLeftAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("right left"), this);
178 connect(stereoRightLeftAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeRightLeft()));
179 stereoMenu->addAction(stereoRightLeftAction);
180 QAction *stereoTopBottomAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("top bottom"), this);
181 connect(stereoTopBottomAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeTopBottom()));
182 stereoMenu->addAction(stereoTopBottomAction);
183 QAction *stereoBottomTopAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("bottom top"), this);
184 connect(stereoBottomTopAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeBottomTop()));
185 stereoMenu->addAction(stereoBottomTopAction);
186 QAction *stereoAnaglyphAction = new QAction(QIcon::fromTheme("zoom-best-fit"), tr("anaglyph"), this);
187 connect(stereoAnaglyphAction, SIGNAL(triggered()), this, SLOT(setCameraStereoModeAnaglyph()));
188 stereoMenu->addAction(stereoAnaglyphAction);
189 stereoButton->setMenu(stereoMenu);
190 stereoButton->setIcon(QIcon(QPixmap(":/icon_view_stereo.png")));
191 stereoButton->setPopupMode(QToolButton::InstantPopup);
192 toolbar->addWidget(stereoButton);
193
194 // selection mode
195 toolbar->addSeparator();
196 QAction *selAtomAction = new QAction(QIcon(QPixmap(":/icon_select_atom.png")), tr("select atom by clicking"), this);
197 connect(selAtomAction, SIGNAL(triggered()), worldscene, SLOT(setSelectionModeAtom()));
198 toolbar->addAction(selAtomAction);
199 QAction *selMolAction = new QAction(QIcon(QPixmap(":/icon_select_molecule.png")), tr("select molecule by clicking"), this);
200 connect(selMolAction, SIGNAL(triggered()), worldscene, SLOT(setSelectionModeMolecule()));
201 toolbar->addAction(selMolAction);
202
203 // dreiBein/domain enabler
204 toolbar->addSeparator();
205 QAction *seldreiBein = new QAction(QIcon(QPixmap(":/icon_dreiBein.png")), tr("enable/disable dreiBein"), this);
206 connect(seldreiBein, SIGNAL(triggered()), this, SLOT(changeDreiBein()));
207 toolbar->addAction(seldreiBein);
208 QAction *seldomain = new QAction(QIcon(QPixmap(":/icon_domain.png")), tr("enable/disable domain box"), this);
209 connect(seldomain, SIGNAL(triggered()), this, SLOT(changeDomain()));
210 toolbar->addAction(seldomain);
211}
212
213void GLWorldView::createDomainBox()
214{
215 QSettings settings;
216 settings.beginGroup("WorldView");
217 QColor colorFrame = settings.value("domainBoxColorFrame", QColor(150,160,200,255)).value<QColor>();
218 QColor colorAmbient = settings.value("domainBoxColorAmbient", QColor(50,60,100,255)).value<QColor>();
219 QColor colorDiffuse = settings.value("domainBoxColorDiffuse", QColor(150,160,200,180)).value<QColor>();
220 settings.setValue("domainBoxColorFrame", colorFrame);
221 settings.setValue("domainBoxColorAmbient", colorAmbient);
222 settings.setValue("domainBoxColorDiffuse", colorDiffuse);
223 const bool status = settings.value("domainBoxEnabled").toBool();
224 settings.endGroup();
225
226 domainBoxMaterial = new QGLMaterial;
227 domainBoxMaterial->setAmbientColor(QColor(0,0,0,255));
228 domainBoxMaterial->setDiffuseColor(QColor(0,0,0,255));
229 domainBoxMaterial->setEmittedLight(colorFrame);
230
231
232 QGLMaterial *material = new QGLMaterial;
233 material->setAmbientColor(colorAmbient);
234 material->setDiffuseColor(colorDiffuse);
235
236 QGLBuilder builder;
237 builder << QGL::Faceted;
238 builder << QGLCube(-1.0); // "inverted" => inside faces are used as front.
239 meshDomainBox = builder.finalizedSceneNode();
240 QMatrix4x4 mat;
241 mat.translate(0.5f, 0.5f, 0.5f);
242 meshDomainBox->setLocalTransform(mat);
243 meshDomainBox->setMaterial(material);
244
245 setDomainStatus( status );
246}
247
248void GLWorldView::createDreiBein()
249{
250 QSettings settings;
251 settings.beginGroup("WorldView");
252 QColor colorX = settings.value("dreiBeinColorX", QColor(255,50,50,255)).value<QColor>();
253 QColor colorY = settings.value("dreiBeinColorY", QColor(50,255,50,255)).value<QColor>();
254 QColor colorZ = settings.value("dreiBeinColorZ", QColor(50,50,255,255)).value<QColor>();
255 settings.setValue("dreiBeinColorX", colorX);
256 settings.setValue("dreiBeinColorY", colorY);
257 settings.setValue("dreiBeinColorZ", colorZ);
258 const bool status = settings.value("dreiBeinEnabled").toBool();
259 settings.endGroup();
260
261 // Create 3 color for the 3 axes.
262 dreiBeinMaterial[0] = new QGLMaterial;
263 dreiBeinMaterial[0]->setColor(colorX);
264 dreiBeinMaterial[1] = new QGLMaterial;
265 dreiBeinMaterial[1]->setColor(colorY);
266 dreiBeinMaterial[2] = new QGLMaterial;
267 dreiBeinMaterial[2]->setColor(colorZ);
268
269 // Create the basic meshes (cylinder and cone).
270 QGLBuilder builderCyl;
271 builderCyl << QGLCylinder(.15,.15,1.6,16);
272 QGLSceneNode *cyl = builderCyl.finalizedSceneNode();
273
274 QGLBuilder builderCone;
275 builderCone << QGLCylinder(0,.4,0.4,16);
276 QGLSceneNode *cone = builderCone.finalizedSceneNode();
277 {
278 QMatrix4x4 mat;
279 mat.translate(0.0f, 0.0f, 1.0f);
280 cone->setLocalTransform(mat);
281 }
282
283 // Create a scene node from the 3 axes.
284 meshDreiBein = new QGLSceneNode(this);
285
286 // X-direction
287 QGLSceneNode *node = new QGLSceneNode(meshDreiBein);
288 node->setMaterial(dreiBeinMaterial[0]);
289 node->addNode(cyl);
290 node->setPosition(QVector3D(.8f, 0.f, 0.f));
291 node->addNode(cone);
292 {
293 QMatrix4x4 mat;
294 mat.rotate(90, 0.0f, 1.0f, 0.0f);
295 node->setLocalTransform(mat);
296 }
297
298 // Y-direction
299 node = new QGLSceneNode(meshDreiBein);
300 node->setMaterial(dreiBeinMaterial[1]);
301 node->addNode(cyl);
302 node->addNode(cone);
303 {
304 QMatrix4x4 mat;
305 mat.rotate(-90, 1.0f, 0.0f, 0.0f);
306 node->setLocalTransform(mat);
307 }
308 node->setPosition(QVector3D(0.f, .8f, 0.f));
309
310 // Z-direction
311 node = new QGLSceneNode(meshDreiBein);
312 node->setMaterial(dreiBeinMaterial[2]);
313 node->addNode(cyl);
314 node->addNode(cone);
315 node->setPosition(QVector3D(0.f, 0.f, .8f));
316
317 setdreiBeinStatus( status );
318}
319
320void GLWorldView::setSelectionChangedAgent(QtSelectionChangedAgent *agent)
321{
322 worldscene->setSelectionChangedAgent(agent);
323}
324
325/**
326 * Update operation which can be invoked by the observable (which should be the
327 * change tracker here).
328 */
329void GLWorldView::update(Observable *publisher)
330{
331// emit changed();
332}
333
334void GLWorldView::signOnToMolecule(const molecule *_mol)
335{
336 ASSERT( _mol != NULL,
337 "GLWorldView::signOnToMolecule() - molecule ref is NULL.");
338 _mol->signOn(this, molecule::AtomInserted);
339 _mol->signOn(this, molecule::AtomRemoved);
340
341 ObservedMolecules.insert(const_cast<molecule *>(_mol));
342}
343
344void GLWorldView::signOffFromMolecule(const molecule *_mol)
345{
346 ObservedMolecules_t::const_iterator iter = ObservedMolecules.find(
347 const_cast<molecule *>(_mol));
348 ASSERT( iter != ObservedMolecules.end(),
349 "GLWorldView::signOffFromMolecule() - molecule "+toString(_mol)
350 +" gave subjectKilled we are not signed on.");
351// LOG(1, "INFO: Erasing " << mol << " from ObservedMolecules.");
352 ObservedMolecules.erase(iter);
353
354 ASSERT( _mol != NULL,
355 "GLWorldView::signOffFromMolecule() - molecule ref is NULL.");
356 _mol->signOff(this, molecule::AtomInserted);
357 _mol->signOff(this, molecule::AtomRemoved);
358}
359
360/**
361 * The observable can tell when it dies.
362 */
363void GLWorldView::subjectKilled(Observable *publisher)
364{
365 molecule * mol = static_cast<molecule *>(publisher);
366
367// std::copy(ObservedMolecules.begin(), ObservedMolecules.end(),
368// std::ostream_iterator<molecule *>(std::cout, "\n"));
369
370 if (mol != NULL) {
371 ObservedMolecules.erase(mol);
372//
373// // sign off
374// signOffFromMolecule(mol);
375
376 // emit removed signal
377 const moleculeId_t _id = mol->getId();
378 emit moleculeRemoved(_id);
379 }
380}
381
382/** Listen to specific changes to the world.
383 *
384 * @param publisher ref to observable.
385 * @param notification type of notification
386 */
387void GLWorldView::recieveNotification(Observable *publisher, Notification_ptr notification)
388{
389 if (static_cast<World *>(publisher) == World::getPointer()) {
390 switch (notification->getChannelNo()) {
391 case World::SelectionChanged:
392 {
393 #ifdef LOG_OBSERVER
394 observerLog().addMessage() << "++ Observer " << observerLog().getName(static_cast<Observer *>(this)) << " received notification that selection has changed.";
395 #endif
396 emit worldSelectionChanged();
397 break;
398 }
399 case World::MoleculeInserted:
400 {
401 const moleculeId_t _id = const_cast<const World &>(World::getInstance()).lastChangedMolId();
402 #ifdef LOG_OBSERVER
403 observerLog().addMessage() << "++ Observer " << observerLog().getName(static_cast<Observer *>(this)) << " received notification that molecule "+toString(_id)+" has been inserted.";
404 #endif
405 const molecule * const _molecule = const_cast<const World &>(World::getInstance()).
406 getMolecule(MoleculeById(_id));
407 if (_molecule != NULL) {
408 signOnToMolecule(_molecule);
409
410 emit moleculeInserted(_id);
411 }
412 break;
413 }
414 default:
415 ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for World.");
416 break;
417 }
418 } else if (static_cast<WorldTime *>(publisher) == WorldTime::getPointer()) {
419 switch (notification->getChannelNo()) {
420 case WorldTime::TimeChanged:
421 {
422#ifdef LOG_OBSERVER
423 observerLog().addMessage() << "++ Observer " << observerLog().getName(static_cast<Observer *>(this)) << " received notification that WorldTime's time has changed.";
424#endif
425 emit changed();
426 emit TimeChanged();
427 break;
428 }
429 default:
430 ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for WorldTime.");
431 break;
432 }
433 } else if (dynamic_cast<molecule *>(publisher) != NULL) {
434 const molecule * mol = const_cast<const molecule * const>(dynamic_cast<molecule *>(publisher));
435 const moleculeId_t molid = mol->getId();
436 const atomId_t atomid = mol->lastChangedAtomId();
437 switch (notification->getChannelNo()) {
438 case molecule::AtomInserted:
439 {
440#ifdef LOG_OBSERVER
441 observerLog().addMessage() << "++ Observer " << observerLog().getName(static_cast<Observer *>(this))
442 << " received notification that atom "+toString(atomid)+" has been inserted into molecule "+toString(molid)+".";
443#endif
444 emit atomInserted(molid, atomid);
445 break;
446 }
447 case World::AtomRemoved:
448 {
449#ifdef LOG_OBSERVER
450 observerLog().addMessage() << "++ Observer " << observerLog().getName(static_cast<Observer *>(this))
451 << " received notification that atom "+toString(atomid)+" has been removed from molecule "+toString(molid)+".";
452#endif
453 emit atomRemoved(molid, atomid);
454 break;
455 }
456 default:
457 ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for molecule.");
458 break;
459 }
460 } else if (dynamic_cast<AtomObservable *>(publisher) != NULL) {
461 switch (notification->getChannelNo()) {
462 case AtomObservable::PositionChanged:
463 {
464 #ifdef LOG_OBSERVER
465 const atom *_atom = dynamic_cast<const atom *>(publisher);
466 observerLog().addMessage() << "++ Observer " << observerLog().getName(static_cast<Observer *>(this)) << " received notification that atom "+toString(_atom->getId())+" has changed its position.";
467 #endif
468 emit changed();
469 break;
470 }
471 default:
472 ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for AtomObservable.");
473 break;
474 }
475 } else if (static_cast<ShapeRegistry*>(publisher) == ShapeRegistry::getPointer()) {
476 switch (notification->getChannelNo()) {
477 case ShapeRegistry::ShapeInserted:
478 {
479 emit ShapeAdded(ShapeRegistry::getInstance().lastChanged()->getName());
480 break;
481 }
482 case ShapeRegistry::ShapeRemoved:
483 {
484 emit ShapeRemoved(ShapeRegistry::getInstance().lastChanged()->getName());
485 break;
486 }
487 case ShapeRegistry::SelectionChanged:
488 {
489 worldscene->updateSelectedShapes();
490 break;
491 }
492 default:
493 ASSERT(0, "GLWorldView::recieveNotification() - we cannot get here for ShapeRegistry.");
494 break;
495 }
496 }
497}
498
499void GLWorldView::checkChanges()
500{
501 updateGL();
502 needsRedraw = false;
503}
504
505void GLWorldView::changeDreiBein()
506{
507 // invert to new status
508 const bool status = ((meshDreiBein->options() & QGLSceneNode::HideNode) == 0);
509 setdreiBeinStatus(!status);
510 // realize
511 updateGL();
512 needsRedraw = true;
513}
514
515void GLWorldView::setdreiBeinStatus(const bool status)
516{
517 if (status)
518 meshDreiBein->setOptions( meshDreiBein->options() & (255-QGLSceneNode::HideNode) );
519 else
520 meshDreiBein->setOptions( meshDreiBein->options() | QGLSceneNode::HideNode );
521}
522
523void GLWorldView::changeDomain()
524{
525 // invert to new status
526 const bool status = ((meshDomainBox->options() & QGLSceneNode::HideNode) == 0);
527 setDomainStatus(!status);
528 // realize
529 updateGL();
530 needsRedraw = true;
531}
532
533void GLWorldView::setDomainStatus(const bool status)
534{
535 if (status)
536 meshDomainBox->setOptions( meshDomainBox->options() & (255-QGLSceneNode::HideNode) );
537 else
538 meshDomainBox->setOptions( meshDomainBox->options() | QGLSceneNode::HideNode );
539}
540
541void GLWorldView::sceneChangeSignalled()
542{
543 if (!needsRedraw){
544 redrawTimer->singleShot(0, this, SLOT(checkChanges()));
545 needsRedraw = true;
546 redrawTimer->start();
547 }
548}
549
550void GLWorldView::initializeGL(QGLPainter *painter)
551{
552 worldscene->initialize(this, painter);
553 changesPresent = false;
554}
555
556void GLWorldView::paintGL(QGLPainter *painter)
557{
558 if (changesPresent) {
559 initializeGL(painter);
560 changesPresent = false;
561 }
562
563 QVector3D cameraDir = camera()->center() - camera()->eye();
564 cameraDir.normalize();
565 QVector4D cameraPlane(cameraDir, QVector3D::dotProduct(cameraDir, camera()->eye()));
566 worldscene->draw(painter, cameraPlane);
567
568 drawDreiBein(painter);
569
570 // Domain box has to be last because of its transparency.
571 drawDomainBox(painter);
572}
573
574void GLWorldView::keyPressEvent(QKeyEvent *e)
575{
576 // Find the distance between the eye and focus point.
577 QVector3D d = camera()->eye() - camera()->center();
578// LOG(1, "Distance vector eye and center is "
579// << d.x() << "," << d.y() << "," << d.z());
580 // scale the move unit by the eye <-> domain center distance
581 const double key_move_unit = 0.04*(d.length()/50.);
582
583 if (e->key() == Qt::Key_Tab) {
584 // The Tab key turns the ShowPicking option on and off,
585 // which helps show what the pick buffer looks like.
586 setOption(QGLView::ShowPicking, ((options() & QGLView::ShowPicking) == 0));
587 updateGL();
588 } else if ((e->key() == Qt::Key_Minus) || (e->key() == Qt::Key_Plus)) {
589 // Scale the distance.
590 if (e->key() == Qt::Key_Minus)
591 d *= 1.2;
592 else if (e->key() == Qt::Key_Plus)
593 d /= 1.2;
594 // Set new eye position.
595 camera()->setEye(camera()->center() + d);
596 } else if ((e->key() == Qt::Key_Left) || (e->key() == Qt::Key_Right)) {
597 // Translate the camera.
598 const double d = (e->key() == Qt::Key_Left) ? -key_move_unit : key_move_unit;
599 camera()->translateCenter( d, 0., 0);
600 camera()->translateEye( d, 0., 0);
601 } else if ((e->key() == Qt::Key_Up) || (e->key() == Qt::Key_Down)) {
602 // Translate the camera.
603 const double d = (e->key() == Qt::Key_Up) ? -key_move_unit : key_move_unit;
604 camera()->translateCenter( 0., d, 0);
605 camera()->translateEye( 0., d, 0);
606 } else if ((e->key() == Qt::Key_PageUp) || (e->key() == Qt::Key_PageDown)) {
607 // Translate the camera.
608 const double d = (e->key() == Qt::Key_PageUp) ? -key_move_unit : key_move_unit;
609 camera()->translateCenter( 0., 0., d);
610 camera()->translateEye( 0., 0., d);
611 }
612 QGLView::keyPressEvent(e);
613}
614
615void GLWorldView::changeSignalled()
616{
617 changesPresent = true;
618}
619
620
621/**
622 * Set the current camera control mode.
623 */
624void GLWorldView::setCameraControlMode(GLWorldView::CameraControlModeType mode)
625{
626 cameraControlMode = mode;
627}
628
629void GLWorldView::setCameraControlModeRotation()
630{
631 setCameraControlMode(Rotate);
632}
633
634void GLWorldView::setCameraControlModeTranslation()
635{
636 setCameraControlMode(Translate);
637}
638
639/**
640 * Returns the current camera control mode.
641 * This needs to be invertable (rotation - translation), if the shift key is pressed.
642 */
643GLWorldView::CameraControlModeType GLWorldView::getCameraControlMode(bool inverted)
644{
645 if (inverted){
646 if (cameraControlMode == Rotate)
647 return Translate;
648 if (cameraControlMode == Translate)
649 return Rotate;
650 return Rotate;
651 }else
652 return cameraControlMode;
653}
654
655/**
656 * Set the camera so it can oversee the whole domain.
657 */
658void GLWorldView::fitCameraToDomain()
659{
660 // Move the camera focus point to the center of the domain box.
661 Vector v = World::getInstance().getDomain().translateIn(Vector(0.5, 0.5, 0.5));
662 camera()->setCenter(QVector3D(v[0], v[1], v[2]));
663
664 // Guess some eye distance.
665 double dist = v.Norm() * 3;
666 camera()->setEye(QVector3D(v[0], v[1], v[2] + dist));
667 camera()->setUpVector(QVector3D(0, 1, 0));
668}
669
670void GLWorldView::setCameraStereoModeDisable()
671{
672 setStereoType(QGLView::Hardware);
673 camera()->setEyeSeparation(0.0);
674 updateGL();
675}
676
677void GLWorldView::setCameraStereoModeHardware()
678{
679 setStereoType(QGLView::Hardware);
680 camera()->setEyeSeparation(defaultEyeSeparation);
681 updateGL();
682}
683
684void GLWorldView::setCameraStereoModeLeftRight()
685{
686 setStereoType(QGLView::LeftRight);
687 camera()->setEyeSeparation(defaultEyeSeparation);
688 updateGL();
689}
690
691void GLWorldView::setCameraStereoModeRightLeft()
692{
693 setStereoType(QGLView::RightLeft);
694 camera()->setEyeSeparation(defaultEyeSeparation);
695 updateGL();
696}
697
698void GLWorldView::setCameraStereoModeTopBottom()
699{
700 setStereoType(QGLView::TopBottom);
701 camera()->setEyeSeparation(defaultEyeSeparation);
702 updateGL();
703}
704
705void GLWorldView::setCameraStereoModeBottomTop()
706{
707 setStereoType(QGLView::BottomTop);
708 camera()->setEyeSeparation(defaultEyeSeparation);
709 updateGL();
710}
711
712void GLWorldView::setCameraStereoModeAnaglyph()
713{
714 setStereoType(QGLView::RedCyanAnaglyph);
715 camera()->setEyeSeparation(defaultEyeSeparation);
716 updateGL();
717}
718
719void GLWorldView::mousePressEvent(QMouseEvent *event)
720{
721 QGLView::mousePressEvent(event);
722
723 // Reset the saved mouse position.
724 lastMousePos = event->posF();
725}
726
727/**
728 * Handle a mouse move event.
729 * This is used to control the camera (rotation and translation) when the left button is being pressed.
730 */
731void GLWorldView::mouseMoveEvent(QMouseEvent *event)
732{
733 if (event->buttons() & Qt::LeftButton){
734 // Find the mouse distance since the last event.
735 QPointF d = event->posF() - lastMousePos;
736 lastMousePos = event->posF();
737
738 // Rotate or translate? (inverted by shift key)
739 CameraControlModeType mode = getCameraControlMode(event->modifiers() & Qt::ShiftModifier);
740
741 if (mode == Rotate){
742 // Rotate the camera.
743 d *= 0.3;
744 camera()->tiltPanRollCenter(- d.y(), - d.x(), 0);
745 }else if (mode == Translate){
746 // Translate the camera.
747 d *= 0.02;
748 camera()->translateCenter(- d.x(), d.y(), 0);
749 camera()->translateEye(- d.x(), d.y(), 0);
750 }
751 }else{
752 // Without this Qt would not test for hover events (i.e. mouse over an atom).
753 QGLView::mouseMoveEvent(event);
754 }
755}
756
757/**
758 * When the mouse wheel is used, zoom in or out.
759 */
760void GLWorldView::wheelEvent(QWheelEvent *event)
761{
762 // Find the distance between the eye and focus point.
763 QVector3D d = camera()->eye() - camera()->center();
764
765 // Scale the distance.
766 if (event->delta() < 0)
767 d *= 1.2;
768 else if (event->delta() > 0)
769 d /= 1.2;
770
771 // Set new eye position.
772 camera()->setEye(camera()->center() + d);
773}
774
775/**
776 * Draw a transparent cube representing the domain.
777 */
778void GLWorldView::drawDomainBox(QGLPainter *painter) const
779{
780 // Apply the domain matrix.
781 RealSpaceMatrix m = World::getInstance().getDomain().getM();
782 painter->modelViewMatrix().push();
783 painter->modelViewMatrix() *= QMatrix4x4(m.at(0,0), m.at(0,1), m.at(0,2), 0.0,
784 m.at(1,0), m.at(1,1), m.at(1,2), 0.0,
785 m.at(2,0), m.at(2,1), m.at(2,2), 0.0,
786 0.0, 0.0, 0.0, 1.0);
787
788 // Draw the transparent cube.
789 painter->setStandardEffect(QGL::LitMaterial);
790 glCullFace(GL_BACK);
791 glEnable(GL_CULL_FACE);
792 glEnable(GL_BLEND);
793 glDepthMask(0);
794 //glDisable(GL_DEPTH_TEST);
795 meshDomainBox->draw(painter);
796 //glEnable(GL_DEPTH_TEST);
797 glDepthMask(1);
798 glDisable(GL_BLEND);
799 glDisable(GL_CULL_FACE);
800
801 // Draw the outlines (if we have drawn the box itself)
802 if ((meshDomainBox->options() & QGLSceneNode::HideNode) == 0) {
803 painter->setFaceMaterial(QGL::AllFaces, domainBoxMaterial);
804 //glEnable(GL_LINE_SMOOTH);
805 QVector3DArray array;
806 array.append(0, 0, 0); array.append(1, 0, 0);
807 array.append(1, 0, 0); array.append(1, 1, 0);
808 array.append(1, 1, 0); array.append(0, 1, 0);
809 array.append(0, 1, 0); array.append(0, 0, 0);
810
811 array.append(0, 0, 1); array.append(1, 0, 1);
812 array.append(1, 0, 1); array.append(1, 1, 1);
813 array.append(1, 1, 1); array.append(0, 1, 1);
814 array.append(0, 1, 1); array.append(0, 0, 1);
815
816 array.append(0, 0, 0); array.append(0, 0, 1);
817 array.append(1, 0, 0); array.append(1, 0, 1);
818 array.append(0, 1, 0); array.append(0, 1, 1);
819 array.append(1, 1, 0); array.append(1, 1, 1);
820 painter->clearAttributes();
821 painter->setVertexAttribute(QGL::Position, array);
822 painter->draw(QGL::Lines, 24);
823 }
824
825 painter->modelViewMatrix().pop();
826}
827
828void GLWorldView::drawDreiBein(QGLPainter *painter)
829{
830 painter->modelViewMatrix().push();
831 painter->modelViewMatrix().translate(camera()->center());
832 painter->setStandardEffect(QGL::LitMaterial);
833 painter->setFaceMaterial(QGL::FrontFaces, NULL);
834 meshDreiBein->draw(painter);
835 painter->modelViewMatrix().pop();
836}
837
838void GLWorldView::sceneHoverSignalled(const atomId_t _id)
839{
840 emit hoverChanged(_id);
841}
842
843void GLWorldView::sceneHoverSignalled(const moleculeId_t _id, int _i)
844{
845 emit hoverChanged(_id, _i);
846}
Note: See TracBrowser for help on using the repository browser.