/* * 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 "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 "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) { 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())); 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(); for (std::vector::const_iterator moliter = molecules.begin(); moliter != molecules.end(); moliter++) { // create molecule objects in scene moleculeInserted(*moliter); } } /** Update the WorldScene with molecules and atoms from 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 = 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); } } void GLWorldScene::atomClicked(atomId_t no) { LOG(3, "INFO: GLMoleculeObject_molecule - 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::moleculeClicked(moleculeId_t no) { LOG(3, "INFO: GLMoleculeObject_molecule - mol " << no << " has been clicked."); const molecule *mol= World::getInstance().getMolecule(MoleculeById(no)); ASSERT(mol, "Atom without molecule has been clicked."); if (!World::getInstance().isSelected(mol)) SelectionMoleculeById(mol->getId()); else SelectionNotMoleculeById(mol->getId()); emit clicked(no); } /** Adds an atom to the scene. * * @param _atom atom to add */ void GLWorldScene::atomInserted(const atomId_t _id) { LOG(3, "INFO: GLWorldScene: Received signal atomInserted for atom "+toString(_id)+"."); // find associated molecule const moleculeId_t molid = World::getInstance().getAtom(AtomById(_id))->getMolecule()->getId(); MoleculeNodeMap::const_iterator moliter = MoleculesinSceneMap.find(molid ); ASSERT(moliter != MoleculesinSceneMap.end(), "GLWorldScene::atomAdded() - molecule with id of "+toString(molid) +" atom with id "+toString(_id)+" is unknown."); GLMoleculeObject_molecule *molObject = moliter->second; // add atom to internal list #ifndef NDEBUG AtomMoleculeMap::const_iterator atomiter = AtomsinSceneMap.find(_id); ASSERT(atomiter == AtomsinSceneMap.end(), "GLWorldScene::atomRemoved() - atom "+toString(_id)+" already known."); #endif AtomsinSceneMap.insert( std::make_pair( _id, molObject )); // inform its GlMoleculeObject_molecule. molObject->atomInserted(_id); // emit change emit changeOccured(); } /** Removes an atom from the scene. * * We just the id as the atom might have already been destroyed. * * @param _id id of atom to remove */ void GLWorldScene::atomRemoved(const atomId_t _id) { LOG(3, "INFO: GLWorldScene: Received signal atomRemoved for atom "+toString(_id)+"."); // find associated molecule AtomMoleculeMap::iterator iter = AtomsinSceneMap.find(_id); ASSERT(iter != AtomsinSceneMap.end(), "GLWorldScene::atomRemoved() - atom "+toString(_id)+" not on display."); GLMoleculeObject_molecule *molObject = iter->second; // remove from internal list AtomsinSceneMap.erase(iter); // inform its GlMoleculeObject_molecule. molObject->atomRemoved(_id); // emit change 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++) { // molecule selected but not in scene? const bool isSelected = World::getInstance().isSelected(*Runner); if (isSelected){ MoleculeNodeMap::iterator iter = MoleculesinSceneMap.find((*Runner)->getId()); ASSERT( iter != MoleculesinSceneMap.end(), "GLWorldScene::worldSelectionChanged() - selected molecule is unknown."); GLMoleculeObject_molecule *molObject = iter->second; // inform molecule object molObject->selected(isSelected); } } } } /** Inserts a molecule into the scene. * * @param _mol molecule to insert */ void GLWorldScene::moleculeInserted(const molecule * _mol) { LOG(3, "INFO: GLWorldScene: Received signal moleculeRemoved for molecule "+toString(_mol->getId())+"."); MoleculeNodeMap::const_iterator iter = MoleculesinSceneMap.find(_mol->getId()); ASSERT( iter == MoleculesinSceneMap.end(), "GLWorldScene::moleculeInserted() - molecule's id "+toString(_mol->getId())+" already present."); // add new object GLMoleculeObject_molecule *molObject = new GLMoleculeObject_molecule(GLMoleculeObject::meshEmpty, this, _mol); MoleculesinSceneMap.insert( make_pair(_mol->getId(), molObject) ); 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(changeAtomId(GLMoleculeObject_atom *, int, int)), this, SLOT(changeAtomId(GLMoleculeObject_atom *, int, int))); connect (molObject, SIGNAL(selectionChanged()), this, SIGNAL(changed())); connect (molObject, SIGNAL(selectionChanged()), this, SIGNAL(changed())); connect (molObject, SIGNAL(hoverChanged(const atom &)), this, SIGNAL(hoverChanged(const atom &))); connect (molObject, SIGNAL(hoverChanged(const molecule &, int)), this, SIGNAL(hoverChanged(const molecule &, int))); 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); ASSERT( iter != MoleculesinSceneMap.end(), "GLWorldScene::moleculeInserted() - molecule's id "+toString(_id)+" is unknown."); GLMoleculeObject_molecule *molObject = iter->second; molObject->disconnect(); MoleculesinSceneMap.erase(iter); delete molObject; 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 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 << "."); // bond partners must both belong to same atom ASSERT( _bond->leftatom->getMolecule() == _bond->rightatom->getMolecule(), "GLWorldScene::bondInserted() - bond partners do not belong to same molecule."); // find associated molecule object const moleculeId_t molid = _bond->leftatom->getMolecule()->getId(); MoleculeNodeMap::const_iterator moliter = MoleculesinSceneMap.find(molid ); ASSERT(moliter != MoleculesinSceneMap.end(), "GLWorldScene::bondInserted() - molecule with id of "+toString(molid) +" atom with id "+toString(_bond->leftatom->getId())+" is unknown."); GLMoleculeObject_molecule *molObject = moliter->second; // add to internal list const GLMoleculeObject_molecule::BondIds ids = GLMoleculeObject_molecule::getBondIds(_bond, _side); BondMoleculeMap::const_iterator iter = BondsinSceneMap.find(ids); ASSERT(iter == BondsinSceneMap.end(), "GLWorldScene::bondInserted() - bond with ids "+toString(ids.first) +","+toString(ids.second)+" already present."); BondsinSceneMap.insert( std::make_pair( ids, molObject )); // inform its GlMoleculeObject_molecule. molObject->bondInserted(_bond, _side); // emit change 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 GLMoleculeObject_molecule::BondIds Leftids( make_pair(leftnr, rightnr) ); BondMoleculeMap::iterator leftiter = BondsinSceneMap.find( Leftids ); ASSERT(leftiter != BondsinSceneMap.end(), "GLWorldScene::bondRemoved() - bond "+toString(leftnr)+"-" +toString(rightnr)+" not on display."); GLMoleculeObject_molecule *molObject = leftiter->second; // remove from internal list BondsinSceneMap.erase(leftiter); // inform its GlMoleculeObject_molecule. molObject->bondRemoved( leftnr, rightnr ); } // emit change emit changeOccured(); } /** Adds a shape to the scene. * * uses ShapeRegistry::lastChanged() * */ void GLWorldScene::addShape() { Shape &shape = *ShapeRegistry::getInstance().lastChanged(); 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 = *ShapeRegistry::getInstance().lastChanged(); 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::setSelectionMode(SelectionModeType mode) { selectionMode = mode; // TODO send update to toolbar } void GLWorldScene::setSelectionModeAtom() { setSelectionMode(SelectAtom); } void GLWorldScene::setSelectionModeMolecule() { setSelectionMode(SelectMolecule); } void GLWorldScene::changeAtomId(GLMoleculeObject_atom *ob, int oldId, int newId) { LOG(3, "INFO: GLWorldScene - change atom id " << oldId << " to " << newId << "."); // Remove from map. AtomMoleculeMap::iterator iter = AtomsinSceneMap.find(oldId); ASSERT(iter != AtomsinSceneMap.end(), "GLWorldScene::changeAtomId() - atom with old id "+toString(oldId)+" not on display."); GLMoleculeObject_molecule *molObject = iter->second; // erase by signalling removal molObject->atomRemoved(oldId); // remove from internal list AtomsinSceneMap.erase(iter); // Reinsert with new id. { AtomMoleculeMap::iterator iter = AtomsinSceneMap.find(newId); ASSERT(iter == AtomsinSceneMap.end(), "GLWorldScene::changeAtomId() - atom with new id "+toString(newId)+" already known."); } AtomsinSceneMap.insert( make_pair(newId, molObject) ); // inform molecule object molObject->atomInserted(oldId); }