/*
* Project: MoleCuilder
* Description: creates and alters molecular systems
* Copyright (C) 2010-2012 University of Bonn. All rights reserved.
* Copyright (C) 2013 Frederik Heber. All rights reserved.
*
*
* This file is part of MoleCuilder.
*
* MoleCuilder is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* MoleCuilder is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MoleCuilder. If not, see .
*/
/*
* GLWorldScene.cpp
*
* This is based on the Qt3D example "teaservice", specifically parts of teaservice.cpp.
*
* Created on: Aug 17, 2011
* Author: heber
*/
// include config.h
#ifdef HAVE_CONFIG_H
#include
#endif
#include "GLWorldScene.hpp"
#include
#include
#include
#include
#include
#include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject.hpp"
#include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject_atom.hpp"
#include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject_bond.hpp"
#include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject_molecule.hpp"
#include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject_shape.hpp"
#include "UIElements/Views/Qt4/QtSelectionChangedAgent.hpp"
#include "CodePatterns/MemDebug.hpp"
#include "CodePatterns/Log.hpp"
#include "Actions/SelectionAction/Atoms/AtomByIdAction.hpp"
#include "Actions/SelectionAction/Atoms/NotAtomByIdAction.hpp"
#include "Actions/SelectionAction/Molecules/MoleculeByIdAction.hpp"
#include "Actions/SelectionAction/Molecules/NotMoleculeByIdAction.hpp"
#include "Atom/atom.hpp"
#include "Bond/bond.hpp"
#include "Descriptors/AtomIdDescriptor.hpp"
#include "Descriptors/MoleculeIdDescriptor.hpp"
#include "Helpers/helpers.hpp"
#include "Shapes/ShapeRegistry.hpp"
#include "molecule.hpp"
#include "World.hpp"
#include
using namespace MoleCuilder;
GLWorldScene::GLWorldScene(QObject *parent) :
QObject(parent),
selectionMode(SelectAtom)
{
int sphereDetails[] = {5, 3, 2, 0};
int cylinderDetails[] = {16, 8, 6, 3};
for (int i=0;isetOption(QGLSceneNode::CullBoundingBox, true);
QGLBuilder cylinderBuilder;
cylinderBuilder << QGLCylinder(.25,.25,1.0,cylinderDetails[i]);
GLMoleculeObject::meshCylinder[i] = cylinderBuilder.finalizedSceneNode();
GLMoleculeObject::meshCylinder[i]->setOption(QGLSceneNode::CullBoundingBox, true);
}
// connect(this, SIGNAL(updated()), this, SLOT(update()));
}
GLWorldScene::~GLWorldScene()
{
// remove all elements
GLMoleculeObject::cleanMaterialMap();
}
///** Update the WorldScene with molecules and atomsfrom World.
// *
// * This function should be called after e.g. WorldTime::TimeChanged was
// * received or after another molecule has been loaded.
// *
// */
//void GLWorldScene::update()
//{
// const std::vector &molecules =
// const_cast(World::getInstance()).getAllMolecules();
//
// for (std::vector::const_iterator moliter = molecules.begin();
// moliter != molecules.end();
// moliter++) {
// // check whether molecule already exists
// const moleculeId_t molid = (*moliter)->getId();
// const bool mol_present = MoleculesinSceneMap.count(molid);
// if (!mol_present)
// moleculeInserted((*moliter)->getId());
// }
//
// MoleculeNodeMap::iterator iter = MoleculesinSceneMap.begin();
// for (;iter != MoleculesinSceneMap.end();) {
// const moleculeId_t molid = iter->first;
// const molecule * const mol = const_cast(World::getInstance()).
// getMolecule(MoleculeById(molid));
// const bool mol_absent = (mol == NULL);
// // step on to next molecule before possibly removing entry and invalidating iter
// ++iter;
// if (mol_absent)
// moleculeRemoved(molid);
// }
//
//}
void GLWorldScene::atomClicked(atomId_t no)
{
LOG(3, "INFO: GLMoleculeObject_molecule - atom " << no << " has been clicked.");
const atom * const Walker = const_cast(World::getInstance()).
getAtom(AtomById(no));
ASSERT( Walker != NULL,
"GLWorldScene::atomClicked() - clicked atom has disappeared.");
if (selectionMode == SelectAtom){
if (!World::getInstance().isSelected(Walker))
SelectionAtomById(std::vector(1,no));
else
SelectionNotAtomById(std::vector(1,no));
}else if (selectionMode == SelectMolecule){
const molecule *mol = Walker->getMolecule();
ASSERT(mol, "Atom without molecule has been clicked.");
molids_t ids(1, mol->getId());
if (!World::getInstance().isSelected(mol))
SelectionMoleculeById(ids);
else
SelectionNotMoleculeById(ids);
}
emit clicked(no);
}
void GLWorldScene::moleculeClicked(moleculeId_t no)
{
LOG(3, "INFO: GLMoleculeObject_molecule - mol " << no << " has been clicked.");
const molecule * const mol= const_cast(World::getInstance()).
getMolecule(MoleculeById(no));
ASSERT(mol, "Atom without molecule has been clicked.");
molids_t ids(1, mol->getId());
if (!World::getInstance().isSelected(mol))
SelectionMoleculeById(ids);
else
SelectionNotMoleculeById(ids);
emit clicked(no);
}
/** Inserts an atom into the scene before molecule is present.
*
* @param _molid molecule to insert atom for
* @param _atomid atom to insert
*/
void GLWorldScene::atomInserted(const moleculeId_t _molid, const atomId_t _atomid)
{
LOG(3, "INFO: GLWorldScene: Received signal atomInserted for atom "+toString(_atomid)+".");
boost::recursive_mutex::scoped_lock lock(MoleculeMissedStateMap_mutex);
// check of molecule is already present
if (MoleculesinSceneMap.count(_molid) != 0) {
// pass signal through
} else {
// store signal for when it is instantiated
if (MoleculeMissedStateMap.count(_molid) == 0)
MoleculeMissedStateMap.insert( std::make_pair(_molid ,StateChangeMap_t()) );
MoleculeMissedStateMap[_molid].insert( std::make_pair(_atomid, atomInsertedState) );
}
}
/** Removes an atom into the scene before molecule is present.
*
* @param _molid molecule to insert atom for
* @param _atomid atom to insert
*/
void GLWorldScene::atomRemoved(const moleculeId_t _molid, const atomId_t _atomid)
{
LOG(3, "INFO: GLWorldScene: Received signal atomRemoved for atom "+toString(_atomid)+".");
boost::recursive_mutex::scoped_lock lock(MoleculeMissedStateMap_mutex);
// check of molecule is already present
if (MoleculesinSceneMap.count(_molid) != 0) {
// pass signal through
} else {
// store signal for when it is instantiated
if (MoleculeMissedStateMap.count(_molid) == 0)
MoleculeMissedStateMap.insert( std::make_pair(_molid ,StateChangeMap_t()) );
MoleculeMissedStateMap[_molid].insert( std::make_pair(_atomid, atomRemovedState) );
}
}
/** Inserts a molecule into the scene.
*
* @param _mol molecule to insert
*/
void GLWorldScene::moleculeInserted(const moleculeId_t _id)
{
LOG(3, "INFO: GLWorldScene: Received signal moleculeInserted for molecule "+toString(_id)+".");
MoleculeNodeMap::const_iterator iter = MoleculesinSceneMap.find(_id);
ASSERT( iter == MoleculesinSceneMap.end(),
"GLWorldScene::moleculeInserted() - molecule's id "+toString(_id)+" already present.");
// check whether molecule is still present
if (RemovalMolecules.count(_id) != 0) {
RemovalMolecules.erase(_id);
return;
}
if (const_cast(World::getInstance()).getMolecule(MoleculeById(_id)) == NULL)
return;
// add new object
GLMoleculeObject_molecule *molObject =
new GLMoleculeObject_molecule(GLMoleculeObject::meshEmpty, this, _id);
ASSERT( molObject != NULL,
"GLWorldScene::moleculeInserted - could not create molecule object for "+toString(_id));
MoleculesinSceneMap.insert( make_pair(_id, molObject) );
// now handle all state changes that came up before the instantiation
while (MoleculeMissedStateMap.count(_id) != 0) {
ASSERT( !MoleculeMissedStateMap[_id].empty(),
"GLWorldScene::moleculeInserted() - we have an empty state change map for molecule with id "
+toString(_id));
boost::recursive_mutex::scoped_lock lock(MoleculeMissedStateMap_mutex);
for (StateChangeMap_t::iterator iter = MoleculeMissedStateMap[_id].begin();
!MoleculeMissedStateMap[_id].empty();
iter = MoleculeMissedStateMap[_id].begin()) {
std::pair rangeiter =
MoleculeMissedStateMap[_id].equal_range(iter->first);
const size_t StateCounts = std::distance(rangeiter.first, rangeiter.second);
if (StateCounts > 1) {
// more than one state change, have to combine
typedef std::map StateChangeAmounts_t;
StateChangeAmounts_t StateChangeAmounts;
for (StateChangeMap_t::const_iterator stateiter = rangeiter.first;
stateiter != rangeiter.second; ++stateiter)
++StateChangeAmounts[stateiter->second];
ASSERT( StateChangeAmounts[atomInsertedState] >= StateChangeAmounts[atomRemovedState],
"GLWorldScene::moleculeInserted() - more atomRemoved states than atomInserted for atom "
+toString(iter->first));
if (StateChangeAmounts[atomInsertedState] > StateChangeAmounts[atomRemovedState]) {
LOG(1, "INFO: invoking atomInserted for atom " << iter->first);
QMetaObject::invokeMethod(molObject, // pointer to a QObject
"atomInserted", // member name (no parameters here)
Qt::DirectConnection, // connection type
Q_ARG(const atomId_t, iter->first)); // parameters
} else {
LOG(1, "INFO: Atom " << iter->first << " has been inserted and removed already.");
}
} else {
// can only be an insertion
switch (rangeiter.first->second) {
case atomRemovedState:
ASSERT( 0,
"GLWorldScene::moleculeInserted() - atomRemoved state without atomInserted for atom "
+toString(iter->first));
break;
case atomInsertedState:
LOG(1, "INFO: invoking atomInserted for atom " << iter->first);
QMetaObject::invokeMethod(molObject, // pointer to a QObject
"atomInserted", // member name (no parameters here)
Qt::DirectConnection, // connection type
Q_ARG(const atomId_t, iter->first)); // parameters
break;
default:
ASSERT( 0,
"GLWorldScene::moleculeInserted() - there are unknown change states.");
break;
}
}
// removed state changes for this atom
MoleculeMissedStateMap[_id].erase(iter);
}
// remove state change map for the molecule
MoleculeMissedStateMap.erase(_id);
}
// now let the molObject sign on to molecule
molObject->activateObserver();
connect (molObject, SIGNAL(changed()), this, SIGNAL(changed()));
connect (molObject, SIGNAL(changeOccured()), this, SIGNAL(changeOccured()));
connect (molObject, SIGNAL(atomClicked(atomId_t)), this, SLOT(atomClicked(atomId_t)));
connect (molObject, SIGNAL(moleculeClicked(moleculeId_t)), this, SLOT(moleculeClicked(moleculeId_t)));
connect (molObject, SIGNAL(selectionChanged()), this, SIGNAL(changed()));
connect (molObject, SIGNAL(selectionChanged()), this, SIGNAL(changed()));
connect (molObject, SIGNAL(hoverChanged(const atomId_t)), this, SIGNAL(hoverChanged(const atomId_t)));
connect (molObject, SIGNAL(hoverChanged(const moleculeId_t, int)), this, SIGNAL(hoverChanged(const moleculeId_t, int)));
connect (molObject, SIGNAL(indexChanged(GLMoleculeObject_molecule *, const moleculeId_t, const moleculeId_t)), this, SLOT(changeMoleculeId(GLMoleculeObject_molecule *, const moleculeId_t, const moleculeId_t)));
emit changed();
emit changeOccured();
}
/** Removes a molecule from the scene.
*
* @param _id id of molecule to remove
*/
void GLWorldScene::moleculeRemoved(const moleculeId_t _id)
{
LOG(3, "INFO: GLWorldScene: Received signal moleculeRemoved for molecule "+toString(_id)+".");
MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(_id);
if ( iter == MoleculesinSceneMap.end())
RemovalMolecules.insert(_id);
else {
GLMoleculeObject_molecule *molObject = iter->second;
molObject->disconnect();
MoleculesinSceneMap.erase(iter);
delete molObject;
// remove any possible state changes left
{
boost::recursive_mutex::scoped_lock lock(MoleculeMissedStateMap_mutex);
MoleculeMissedStateMap.erase(_id);
}
emit changed();
emit changeOccured();
}
}
void GLWorldScene::moleculesVisibilityChanged(const moleculeId_t _id, bool _visible)
{
MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(_id);
ASSERT( iter != MoleculesinSceneMap.end(),
"GLWorldScene::moleculeInserted() - molecule's id "+toString(_id)+" is unknown.");
GLMoleculeObject_molecule *molObject = iter->second;
molObject->setVisible(_visible);
emit changed();
emit changeOccured();
}
/** Adds a shape to the scene.
*
*/
void GLWorldScene::addShape(const std::string &_name)
{
Shape * const shape = ShapeRegistry::getInstance().getByName(_name);
if (shape != NULL) {
GLMoleculeObject_shape *shapeObject = new GLMoleculeObject_shape(*shape, this);
ShapeNodeMap::iterator iter = ShapesinSceneMap.find(_name);
ASSERT(iter == ShapesinSceneMap.end(),
"GLWorldScene::addShape() - same shape "+_name+" added again.");
ShapesinSceneMap.insert( make_pair(_name, shapeObject) );
} else
ELOG(2, "GLWorldScene::addShape() - shape disappeared before we could draw it.");
emit changed();
}
void GLWorldScene::removeShape(const std::string &_name)
{
ShapeNodeMap::iterator iter = ShapesinSceneMap.find(_name);
ASSERT(iter != ShapesinSceneMap.end(),
"GLWorldScene::removeShape() - shape "+_name+" not in scene.");
ShapesinSceneMap.erase(iter);
delete(iter->second);
emit changed();
}
void GLWorldScene::updateSelectedShapes()
{
foreach (QObject *obj, children()) {
GLMoleculeObject_shape *shapeobj = qobject_cast(obj);
if (shapeobj){
shapeobj->enable(ShapeRegistry::getInstance().isSelected(shapeobj->getShape()));
}
}
emit changed();
}
void GLWorldScene::initialize(QGLView *view, QGLPainter *painter) const
{
// Initialize all of the mesh objects that we have as children.
foreach (QObject *obj, children()) {
GLMoleculeObject *meshobj = qobject_cast(obj);
if (meshobj)
meshobj->initialize(view, painter);
}
}
void GLWorldScene::draw(QGLPainter *painter, const QVector4D &cameraPlane) const
{
// Draw all of the mesh objects that we have as children.
foreach (QObject *obj, children()) {
GLMoleculeObject *meshobj = qobject_cast(obj);
if (meshobj)
meshobj->draw(painter, cameraPlane);
}
}
void GLWorldScene::setSelectionMode(SelectionModeType mode)
{
selectionMode = mode;
// TODO send update to toolbar
}
void GLWorldScene::setSelectionChangedAgent(QtSelectionChangedAgent *agent)
{
connect(agent, SIGNAL(atomSelected(const moleculeId_t, const atomId_t)),
this, SLOT(AtomSelected(const moleculeId_t, const atomId_t)));
connect(agent, SIGNAL(atomUnselected(const moleculeId_t, const atomId_t)),
this, SLOT(AtomUnselected(const moleculeId_t, const atomId_t)));
connect(agent, SIGNAL(moleculeSelected(const moleculeId_t)),
this, SLOT(MoleculeSelected(const moleculeId_t)));
connect(agent, SIGNAL(moleculeUnselected(const moleculeId_t)),
this, SLOT(MoleculeUnselected(const moleculeId_t)));
}
void GLWorldScene::setSelectionModeAtom()
{
setSelectionMode(SelectAtom);
}
void GLWorldScene::setSelectionModeMolecule()
{
setSelectionMode(SelectMolecule);
}
void GLWorldScene::changeMoleculeId(
GLMoleculeObject_molecule *ob,
const moleculeId_t oldId,
const moleculeId_t newId)
{
LOG(3, "INFO: GLWorldScene - change molecule id " << oldId << " to " << newId << ".");
{
// Remove from map.
MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(oldId);
ASSERT(iter != MoleculesinSceneMap.end(),
"GLWorldScene::changeMoleculeId() - molecule with old id "+toString(oldId)+" not on display.");
ASSERT(iter->second == ob,
"GLWorldScene::changeMoleculeId() - molecule with id "
+toString(oldId)+" does not match with object in MoleculesinSceneMap.");
MoleculesinSceneMap.erase(iter);
// Reinsert with new id.
{
MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(newId);
ASSERT(iter == MoleculesinSceneMap.end(),
"GLWorldScene::changeMoleculeId() - moleculewith new id "+toString(newId)+" already known.");
}
MoleculesinSceneMap.insert( make_pair(newId, ob) );
}
{
// Remove and re-insert from map if present.
MoleculeMissedStateMap_t::iterator iter = MoleculeMissedStateMap.find(oldId);
if (iter != MoleculeMissedStateMap.end()) {
StateChangeMap_t changemap = iter->second;
MoleculeMissedStateMap.erase(iter);
MoleculeMissedStateMap.insert( std::make_pair(newId, changemap) );
}
}
}
void GLWorldScene::AtomSelected(const moleculeId_t _molid, const atomId_t _id)
{
MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(_molid);
if (iter != MoleculesinSceneMap.end())
QMetaObject::invokeMethod(iter->second, // pointer to a QObject
"AtomSelected", // member name (no parameters here)
Qt::QueuedConnection, // connection type
Q_ARG(const atomId_t, _id)); // parameters
else
ELOG(2, "DEBUG: GLWorldScene::AtomSelected() - molecule " <<
_molid << " unknown to GLWorldScene.");
}
void GLWorldScene::AtomUnselected(const moleculeId_t _molid, const atomId_t _id)
{
MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(_molid);
if (iter != MoleculesinSceneMap.end())
QMetaObject::invokeMethod(iter->second, // pointer to a QObject
"AtomUnselected", // member name (no parameters here)
Qt::QueuedConnection, // connection type
Q_ARG(const atomId_t, _id)); // parameters
else
ELOG(2, "GLWorldScene::AtomUnselected() - molecule "
<< _molid << " unknown to GLWorldScene.");
}
void GLWorldScene::MoleculeSelected(const moleculeId_t _molid)
{
MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(_molid);
if (iter != MoleculesinSceneMap.end())
QMetaObject::invokeMethod(iter->second, // pointer to a QObject
"Selected", // member name (no parameters here)
Qt::QueuedConnection); // connection type
else
ELOG(2, "GLWorldScene::MoleculeSelected() - molecule "
<< _molid << " unknown to GLWorldScene.");
}
void GLWorldScene::MoleculeUnselected(const moleculeId_t _molid)
{
MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(_molid);
if (iter != MoleculesinSceneMap.end())
QMetaObject::invokeMethod(iter->second, // pointer to a QObject
"Unselected", // member name (no parameters here)
Qt::QueuedConnection); // connection type
else
ELOG(2, "GLWorldScene::MoleculeUnselected() - molecule "
<< _molid << " unknown to GLWorldScene.");
}