/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2010-2012 University of Bonn. 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 "GLMoleculeObject.hpp" #include "GLMoleculeObject_atom.hpp" #include "GLMoleculeObject_bond.hpp" #include "GLMoleculeObject_molecule.hpp" #include "GLMoleculeObject_shape.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 "Helpers/helpers.hpp" #include "Shapes/ShapeRegistry.hpp" #include "molecule.hpp" #include "World.hpp" #include using namespace MoleCuilder; std::ostream &operator<<(std::ostream &ost, const GLWorldScene::BondIds &t) { ost << t.first << "," << t.second; return ost; } GLWorldScene::GLWorldScene(QObject *parent) : QObject(parent), hoverAtom(NULL) { 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]); meshCylinder[i] = cylinderBuilder.finalizedSceneNode(); meshCylinder[i]->setOption(QGLSceneNode::CullBoundingBox, true); } setSelectionMode(SelectAtom); init(); } GLWorldScene::~GLWorldScene() { // remove all elements GLMoleculeObject::cleanMaterialMap(); } /** Initialise the WorldScene with molecules and atoms from World. * */ void GLWorldScene::init() { const std::vector &molecules = World::getInstance().getAllMolecules(); if (molecules.size() > 0) { for (std::vector::const_iterator Runner = molecules.begin(); Runner != molecules.end(); Runner++) { for (molecule::const_iterator atomiter = (*Runner)->begin(); atomiter != (*Runner)->end(); ++atomiter) { // create atom objects in scene atomInserted(*atomiter); // create bond objects in scene const BondList &bondlist = (*atomiter)->getListOfBonds(); for (BondList::const_iterator bonditer = bondlist.begin(); bonditer != bondlist.end(); ++bonditer) { const bond::ptr _bond = *bonditer; const GLMoleculeObject_bond::SideOfBond side = (_bond->leftatom == *atomiter) ? GLMoleculeObject_bond::left : GLMoleculeObject_bond::right; bondInserted(_bond, side); } } } } } /** Adds an atom to the scene. * * @param _atom atom to add */ void GLWorldScene::atomInserted(const atom *_atom) { LOG(3, "INFO: GLWorldScene: Received signal atomInserted for atom "+toString(_atom->getId())+"."); GLMoleculeObject_atom *atomObject = new GLMoleculeObject_atom(meshSphere, this, _atom); AtomNodeMap::iterator iter = AtomsinSceneMap.find(_atom->getId()); ASSERT(iter == AtomsinSceneMap.end(), "GLWorldScene::atomAdded() - same atom "+_atom->getName()+" added again."); AtomsinSceneMap.insert( make_pair(_atom->getId(), atomObject) ); connect (atomObject, SIGNAL(clicked(atomId_t)), this, SLOT(atomClicked(atomId_t))); connect (atomObject, SIGNAL(changed()), this, SIGNAL(changed())); connect (atomObject, SIGNAL(hoverChanged(GLMoleculeObject *)), this, SIGNAL(changed())); connect (atomObject, SIGNAL(hoverChanged(GLMoleculeObject *)), this, SLOT(hoverChangedSignalled(GLMoleculeObject *))); connect (atomObject, SIGNAL(selectionChanged()), this, SIGNAL(changed())); connect (atomObject, SIGNAL(BondsInserted(const bond::ptr , const GLMoleculeObject_bond::SideOfBond)), this, SLOT(bondInserted(const bond::ptr , const GLMoleculeObject_bond::SideOfBond))); connect (atomObject, SIGNAL(indexChanged(GLMoleculeObject_atom*, int, int)), this, SLOT(changeAtomId(GLMoleculeObject_atom*, int, int))); //bondsChanged(_atom); emit changeOccured(); } /** Removes an atom from the scene. * * @param _atom atom to remove */ void GLWorldScene::atomRemoved(const atom *_atom) { LOG(3, "INFO: GLWorldScene: Received signal atomRemoved for atom "+toString(_atom->getId())+"."); // bonds are removed by signal coming from ~bond // remove atoms AtomNodeMap::iterator iter = AtomsinSceneMap.find(_atom->getId()); ASSERT(iter != AtomsinSceneMap.end(), "GLWorldScene::atomRemoved() - atom "+_atom->getName()+" not on display."); GLMoleculeObject_atom *atomObject = iter->second; atomObject->disconnect(); AtomsinSceneMap.erase(iter); delete atomObject; emit changeOccured(); } /** .... * */ void GLWorldScene::worldSelectionChanged() { LOG(3, "INFO: GLWorldScene: Received signal selectionChanged."); const std::vector &molecules = World::getInstance().getAllMolecules(); if (molecules.size() > 0) { for (std::vector::const_iterator Runner = molecules.begin(); Runner != molecules.end(); Runner++) { MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find((*Runner)->getId()); bool isSelected = World::getInstance().isSelected(*Runner); // molecule selected but not in scene? if (isSelected && (iter == MoleculesinSceneMap.end())){ // -> create new mesh GLMoleculeObject_molecule *molObject = new GLMoleculeObject_molecule(meshEmpty, this, *Runner); MoleculesinSceneMap.insert( make_pair((*Runner)->getId(), molObject) ); connect (molObject, SIGNAL(changed()), this, SIGNAL(changed())); connect (molObject, SIGNAL(selectionChanged()), this, SIGNAL(changed())); connect (molObject, SIGNAL(selectionChanged()), this, SIGNAL(changed())); emit changed(); emit changeOccured(); } // molecule not selected but in scene? if (!isSelected && (iter != MoleculesinSceneMap.end())){ // -> remove from scene moleculeRemoved(*Runner); } } } } /** Removes a molecule from the scene. * * @param _molecule molecule to remove */ void GLWorldScene::moleculeRemoved(const molecule *_molecule) { LOG(3, "INFO: GLWorldScene: Received signal moleculeRemoved for molecule "+toString(_molecule->getId())+"."); MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find(_molecule->getId()); // only remove if the molecule is in the scene // (= is selected) if (iter != MoleculesinSceneMap.end()){ GLMoleculeObject_molecule *molObject = iter->second; molObject->disconnect(); MoleculesinSceneMap.erase(iter); delete molObject; emit changed(); emit changeOccured(); } } /** Adds a bond to the scene. * * @param _bond bond to add * @param side which side of the bond (left or right) */ void GLWorldScene::bondInserted(const bond::ptr _bond, const enum GLMoleculeObject_bond::SideOfBond side) { LOG(3, "INFO: GLWorldScene::bondInserted() - Adding bond "+toString(*_bond)+"."); //LOG(4, "INFO: Currently present bonds " << BondsinSceneMap << "."); BondIds ids; switch (side) { case GLMoleculeObject_bond::left: ids = std::make_pair(_bond->leftatom->getId(), _bond->rightatom->getId()); break; case GLMoleculeObject_bond::right: ids = std::make_pair(_bond->rightatom->getId(), _bond->leftatom->getId()); break; } #ifndef NDEBUG BondNodeMap::iterator iter = BondsinSceneMap.find(ids); ASSERT(iter == BondsinSceneMap.end(), "GLWorldScene::bondAdded() - same left-sided bond "+toString(*_bond)+" added again."); #endif GLMoleculeObject_bond * bondObject = new GLMoleculeObject_bond(meshCylinder, this, _bond, side); connect ( bondObject, SIGNAL(BondRemoved(const atomId_t, const atomId_t)), this, SLOT(bondRemoved(const atomId_t, const atomId_t))); connect (bondObject, SIGNAL(changed()), this, SIGNAL(changed())); BondsinSceneMap.insert( make_pair(ids, bondObject) ); // BondIdsinSceneMap.insert( Leftids ); emit changeOccured(); } /** Removes a bond from the scene. * * @param _bond bond to remove */ void GLWorldScene::bondRemoved(const atomId_t leftnr, const atomId_t rightnr) { LOG(3, "INFO: GLWorldScene::bondRemoved() - Removing bond between "+toString(leftnr)+" and "+toString(rightnr)+"."); { // left bond const BondIds Leftids( make_pair(leftnr, rightnr) ); BondNodeMap::iterator leftiter = BondsinSceneMap.find( Leftids ); ASSERT(leftiter != BondsinSceneMap.end(), "GLWorldScene::bondRemoved() - bond "+toString(leftnr)+"-" +toString(rightnr)+" not on display."); //GLMoleculeObject_bond::ptr bondObject = leftiter->second; BondsinSceneMap.erase(leftiter); //delete bondObject; // is done by signal from bond itself //LOG(4, "INFO: Still present bonds " << BondsinSceneMap << "."); } emit changeOccured(); } /** Adds a shape to the scene. * * @param shape shape to be added */ void GLWorldScene::addShape(Shape &shape) { GLMoleculeObject_shape *shapeObject = new GLMoleculeObject_shape(shape, this); ShapeNodeMap::iterator iter = ShapesinSceneMap.find(shape.getName()); ASSERT(iter == ShapesinSceneMap.end(), "GLWorldScene::addShape() - same shape "+shape.getName()+" added again."); ShapesinSceneMap.insert( make_pair(shape.getName(), shapeObject) ); } void GLWorldScene::removeShape(Shape &shape) { ShapeNodeMap::iterator iter = ShapesinSceneMap.find(shape.getName()); ASSERT(iter != ShapesinSceneMap.end(), "GLWorldScene::removeShape() - shape "+shape.getName()+" not in scene."); ShapesinSceneMap.erase(iter); delete(iter->second); } void GLWorldScene::updateSelectedShapes() { foreach (QObject *obj, children()) { GLMoleculeObject_shape *shapeobj = qobject_cast(obj); if (shapeobj){ shapeobj->enable(ShapeRegistry::getInstance().isSelected(shapeobj->getShape())); } } } 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::atomClicked(atomId_t no) { LOG(3, "INFO: GLWorldScene - atom " << no << " has been clicked."); const atom *Walker = World::getInstance().getAtom(AtomById(no)); 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."); if (!World::getInstance().isSelected(mol)) SelectionMoleculeById(mol->getId()); else SelectionNotMoleculeById(mol->getId()); } emit clicked(no); } void GLWorldScene::setSelectionMode(SelectionModeType mode) { selectionMode = mode; // TODO send update to toolbar } void GLWorldScene::setSelectionModeAtom() { setSelectionMode(SelectAtom); } void GLWorldScene::setSelectionModeMolecule() { setSelectionMode(SelectMolecule); } void GLWorldScene::hoverChangedSignalled(GLMoleculeObject *ob) { // Find the atom, ob corresponds to. hoverAtom = NULL; GLMoleculeObject_atom *atomObject = dynamic_cast(ob); if (atomObject){ for (AtomNodeMap::iterator iter = AtomsinSceneMap.begin();iter != AtomsinSceneMap.end(); ++ iter){ if (iter->second == atomObject) hoverAtom = World::getInstance().getAtom(AtomById(iter->first)); } } // Propagate signal. emit hoverChanged(hoverAtom); } void GLWorldScene::changeAtomId(GLMoleculeObject_atom *ob, int oldId, int newId) { LOG(3, "INFO: GLWorldScene - change atom id " << oldId << " to " << newId << "."); // Remove from map. AtomNodeMap::iterator iter = AtomsinSceneMap.find(oldId); ASSERT(iter != AtomsinSceneMap.end(), "GLWorldScene::objectIdChangedSignalled() - atom with id "+toString(oldId)+" not on display."); GLMoleculeObject_atom *atomObject = iter->second; AtomsinSceneMap.erase(iter); // Reinsert with new id. AtomsinSceneMap.insert( make_pair(newId, atomObject) ); }