/* * 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 . */ /* * GLMoleculeObject_atom.cpp * * Created on: Aug 17, 2011 * Author: heber */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "GLMoleculeObject_atom.hpp" #include #include "CodePatterns/MemDebug.hpp" #include "CodePatterns/Assert.hpp" #include "CodePatterns/Log.hpp" #include "CodePatterns/Observer/Notification.hpp" #include #include #include "Atom/atom.hpp" #include "Bond/bond.hpp" #include "Descriptors/AtomIdDescriptor.hpp" #include "Element/element.hpp" #include "Element/periodentafel.hpp" #include "LinearAlgebra/Vector.hpp" #include "UIElements/Views/Qt4/Qt3D/GLMoleculeObject_bond.hpp" #include "UIElements/Qt4/InstanceBoard/QtObservedInstanceBoard.hpp" #include "World.hpp" #include "WorldTime.hpp" #include "ObservedValue_wCallback.hpp" using namespace boost::assign; static const Observable::channels_t getAtomBondsChannels() { Observable::channels_t channels; channels += AtomObservable::BondsAdded, AtomObservable::BondsRemoved; return channels; } static const Observable::channels_t getAllObservedChannels() { Observable::channels_t channels; channels += AtomObservable::IndexChanged, AtomObservable::PositionChanged, AtomObservable::ElementChanged, AtomObservable::BondsAdded, AtomObservable::BondsRemoved; return channels; } // static entities const Observable::channels_t GLMoleculeObject_atom::AtomIndexChannels(1, AtomObservable::IndexChanged); const Observable::channels_t GLMoleculeObject_atom::AtomPositionChannels(1, AtomObservable::PositionChanged); const Observable::channels_t GLMoleculeObject_atom::AtomElementChannels(1, AtomObservable::ElementChanged); const Observable::channels_t GLMoleculeObject_atom::AtomBondsChannels(getAtomBondsChannels()); GLMoleculeObject_atom::GLMoleculeObject_atom( QGLSceneNode *mesh[], QObject *parent, const atomId_t _id, QtObservedInstanceBoard * _board, std::vector _ObservedValues) : GLMoleculeObject(mesh, parent), Observer(std::string("GLMoleculeObject_atom")+toString(_id)), atomref(getAtom(_id)), ObservedValues(_ObservedValues), subjectKilledCount(0), owner(NULL), AllsignedOnChannels(getAllObservedChannels().size()), signedOnChannels(0), board(_board) { init(_id); } void GLMoleculeObject_atom::init(const atomId_t _id) { setObjectId(_id); resetPosition(); resetElement(); m_selected = const_cast(World::getInstance()).isAtomSelected(_id); // sign On activateObserver(); // atomref is only used for caching the ref, it must be used elswhere const_cast(atomref) = NULL; connect( this, SIGNAL(clicked()), this, SLOT(wasClicked())); connect( this, SIGNAL(idChanged()), this, SLOT(resetIndex()), Qt::QueuedConnection); connect( this, SIGNAL(elementChanged()), this, SLOT(resetElement()), Qt::QueuedConnection); connect( this, SIGNAL(positionChanged()), this, SLOT(resetPosition()), Qt::QueuedConnection); connect( this, SIGNAL(bondsChanged()), this, SLOT(resetPosition()), Qt::QueuedConnection); // use that ObservedValues::AtomBonds is always up-to-date resetBonds(); } void GLMoleculeObject_atom::activateObserver() { if (atomref != NULL) { Observable::channels_t channels = getAllObservedChannels(); owner = static_cast(atomref); for (Observable::channels_t::const_iterator iter = channels.begin(); iter != channels.end(); ++iter) owner->signOn(this, *iter); } } void GLMoleculeObject_atom::deactivateObserver() { // sign Off if (owner != NULL) { Observable::channels_t channels = getAllObservedChannels(); for (Observable::channels_t::const_iterator iter = channels.begin(); iter != channels.end(); ++iter) owner->signOff(this, *iter); owner = NULL; signedOnChannels = 0; } } GLMoleculeObject_atom::~GLMoleculeObject_atom() { deactivateObserver(); board->returnAtomObservedValues(getAtomIndex(), ObservedValues); } void GLMoleculeObject_atom::resetIndex() { const atomId_t newId = getAtomIndex(); const size_t oldId = objectId(); ASSERT( newId != oldId, "GLMoleculeObject_atom::updateIndex() - index "+toString(newId)+" did not change."); LOG(4, "INFO: GLMoleculeObject_atom::resetIndex() - new index is "+toString(newId)+"."); setObjectId(newId); emit indexChanged(this, oldId, newId); } void GLMoleculeObject_atom::resetPosition() { const Vector Position = getAtomPosition(); LOG(4, "INFO: GLMoleculeObject_atom::resetIndex() - new position is "+toString(Position)+"."); setPosition(QVector3D(Position[0], Position[1], Position[2])); } void GLMoleculeObject_atom::resetElement() { size_t elementno = 0; const element * const _type = World::getInstance(). getPeriode()->FindElement(getAtomElement()); if (_type != NULL) { elementno = _type->getAtomicNumber(); } else { // if no element yet, set to hydrogen elementno = 1; } LOG(4, "INFO: GLMoleculeObject_atom::resetIndex() - new element number is "+toString(elementno)+"."); // set materials QGLMaterial *elementmaterial = getMaterial(elementno); ASSERT(elementmaterial != NULL, "GLMoleculeObject_atom::GLMoleculeObject_atom() - QGLMaterial ref from getter function is NULL."); setMaterial(elementmaterial); // set scale double radius = 0.; if (_type != NULL) { radius = _type->getVanDerWaalsRadius(); } else { radius = 0.5; } setScale( radius / 4. ); } void GLMoleculeObject_atom::resetBonds() { ListOfBonds_t ListOfBonds_new = getAtomBonds(); std::sort(ListOfBonds_new.begin(), ListOfBonds_new.end()); ListOfBonds_t BondsToAdd; std::set_difference( ListOfBonds_new.begin(), ListOfBonds_new.end(), ListOfBonds.begin(), ListOfBonds.end(), std::back_inserter(BondsToAdd)); ListOfBonds_t BondsToRemove; std::set_difference( ListOfBonds.begin(), ListOfBonds.end(), ListOfBonds_new.begin(), ListOfBonds_new.end(), std::back_inserter(BondsToRemove)); for (ListOfBonds_t::const_iterator iter = BondsToAdd.begin(); iter != BondsToAdd.end(); ++iter) { const GLMoleculeObject_bond::SideOfBond side = (iter->first == getAtomIndex()) ? GLMoleculeObject_bond::left : GLMoleculeObject_bond::right; emit BondsAdded(iter->first, iter->second, side); } for (ListOfBonds_t::const_iterator iter = BondsToRemove.begin(); iter != BondsToRemove.end(); ++iter) { emit BondsRemoved(iter->first, iter->second); } ListOfBonds = ListOfBonds_new; } void GLMoleculeObject_atom::Selected() { ASSERT( !m_selected, "GLMoleculeObject_atom::Selected() - 3D rep of atom is already selected."); m_selected = true; emit changed(); } void GLMoleculeObject_atom::Unselected() { ASSERT( m_selected, "GLMoleculeObject_atom::Unselected() - 3D rep of atom is already unselected."); m_selected = false; emit changed(); } void GLMoleculeObject_atom::draw(QGLPainter *painter, const QVector4D &cameraPlane) { // call old hook to do the actual paining GLMoleculeObject::draw(painter, cameraPlane); } void GLMoleculeObject_atom::wasClicked() { LOG(4, "INFO: GLMoleculeObject_atom: atom " << getAtomIndex() << " has been clicked"); emit clicked(getAtomIndex()); } const atom * const GLMoleculeObject_atom::getAtomConst(const atomId_t _id) { const atom * const _atom = const_cast(World::getInstance()). getAtom(AtomById(_id)); return _atom; } atom * const GLMoleculeObject_atom::getAtom(const atomId_t _id) { atom * const _atom = World::getInstance().getAtom(AtomById(_id)); return _atom; } atomId_t GLMoleculeObject_atom::updateIndex() { return const_cast(World::getInstance()).lastChangedAtomId(); } Vector GLMoleculeObject_atom::updatePosition( const boost::function &_getAtomIndex) { const atom * const _atom = getAtomConst(_getAtomIndex()); if (_atom != NULL) { return _atom->getPosition(); } else { return zeroVec; } } atomicNumber_t GLMoleculeObject_atom::updateElement( const boost::function &_getAtomIndex) { const atom * const _atom = getAtomConst(_getAtomIndex()); if (_atom != NULL) { return _atom->getElementNo(); } else { return (atomicNumber_t)-1; } } GLMoleculeObject_atom::ListOfBonds_t GLMoleculeObject_atom::updateBonds( const boost::function &_getAtomIndex) { ListOfBonds_t ListOfBonds; const atom * const _atom = getAtomConst(_getAtomIndex()); if (_atom != NULL) { // make sure bonds is up-to-date const BondList ListBonds = _atom->getListOfBonds(); for (BondList::const_iterator iter = ListBonds.begin(); iter != ListBonds.end(); ++iter) ListOfBonds.insert( ListOfBonds.end(), std::make_pair( (*iter)->leftatom->getId(), (*iter)->rightatom->getId()) ); } else { ELOG(2, "Atom with id "+toString(_getAtomIndex())+" is already gone."); } return ListOfBonds; } void GLMoleculeObject_atom::update(Observable *publisher) { ASSERT(0, "GLMoleculeObject_atom::update() - we are not signed on for global updates."); } void GLMoleculeObject_atom::subjectKilled(Observable *publisher) { ++signedOnChannels; if (signedOnChannels == AllsignedOnChannels) { // remove owner: no more signOff needed owner = NULL; board->atomcountsubjectKilled(getAtomIndex()); } } void GLMoleculeObject_atom::recieveNotification(Observable *publisher, Notification_ptr notification) { // ObservedValues have been updated before, hence convert updates to Qt's signals atom * const _atom = dynamic_cast(publisher); if (_atom != NULL) { switch (notification->getChannelNo()) { case AtomObservable::IndexChanged: emit idChanged(); break; case AtomObservable::PositionChanged: emit positionChanged(); break; case AtomObservable::ElementChanged: emit elementChanged(); break; case AtomObservable::BondsAdded: case AtomObservable::BondsRemoved: emit bondsChanged(); break; default: ASSERT(0, "GLMoleculeObject_atom::recieveNotification() - we are not signed on to channel " +toString(notification->getChannelNo())+" of the atom."); break; } } else ASSERT(0, "GLMoleculeObject_atom::recieveNotification() - received notification from unexpected source."); } void GLMoleculeObject_atom::initObservedValues( std::vector &_ObservedValues, const atomId_t _id, const atom * const _atomref, const boost::function &_subjectKilled) { /* This is an old note from when the code was still part of cstor's initializer body. * TODO: Probably does not apply anymore but has not yet been tested. * * We must not use boost::cref(this) as "this" has not been properly constructed and seemingly * boost::cref tries to do some magic to grasp the inheritance hierarchy which fails because * the class has not been fully constructed yet. "This" itself seems to be working fine. */ ASSERT( _ObservedValues.size() == MAX_ObservedTypes, "GLMoleculeObject_atom::initObservedValues() - given ObservedValues has not correct size."); // fill ObservedValues: index first const boost::function AtomIndexUpdater( boost::bind(&GLMoleculeObject_atom::updateIndex)); ObservedValue_wCallback * const IndexObservable = new ObservedValue_wCallback( _atomref, boost::bind(&GLMoleculeObject_atom::updateIndex), "AtomIndex_"+toString(_id), _id, AtomIndexChannels, _subjectKilled); _ObservedValues[AtomIndex] = IndexObservable; const boost::function AtomIndexGetter = boost::bind(&ObservedValue_wCallback::get, IndexObservable); // fill ObservedValues: then all the other that need index const boost::function AtomPositionUpdater( boost::bind(&GLMoleculeObject_atom::updatePosition, AtomIndexGetter)); const boost::function AtomElementUpdater( boost::bind(&GLMoleculeObject_atom::updateElement, AtomIndexGetter)); const boost::function AtomBondsUpdater( boost::bind(&GLMoleculeObject_atom::updateBonds, AtomIndexGetter)); _ObservedValues[AtomPosition] = new ObservedValue_wCallback( _atomref, AtomPositionUpdater, "AtomPosition_"+toString(_id), AtomPositionUpdater(), AtomPositionChannels, _subjectKilled, AtomIndexGetter); _ObservedValues[AtomElement] = new ObservedValue_wCallback( _atomref, AtomElementUpdater, "AtomElement"+toString(_id), AtomElementUpdater(), AtomElementChannels, _subjectKilled, AtomIndexGetter); _ObservedValues[AtomBonds] = new ObservedValue_wCallback( _atomref, AtomBondsUpdater, "AtomBonds_"+toString(_id), AtomBondsUpdater(), AtomBondsChannels, _subjectKilled, AtomIndexGetter); } void GLMoleculeObject_atom::destroyObservedValues( std::vector &_ObservedValues) { delete boost::any_cast *>(_ObservedValues[AtomIndex]); delete boost::any_cast *>(_ObservedValues[AtomPosition]); delete boost::any_cast *>(_ObservedValues[AtomElement]); delete boost::any_cast *>(_ObservedValues[AtomBonds]); _ObservedValues.clear(); } atomId_t GLMoleculeObject_atom::getAtomIndex() const { return boost::any_cast *>(ObservedValues[AtomIndex])->get(); } Vector GLMoleculeObject_atom::getAtomPosition() const { return boost::any_cast *>(ObservedValues[AtomPosition])->get(); } atomicNumber_t GLMoleculeObject_atom::getAtomElement() const { return boost::any_cast *>(ObservedValues[AtomElement])->get(); } GLMoleculeObject_atom::ListOfBonds_t GLMoleculeObject_atom::getAtomBonds() const { return boost::any_cast *>(ObservedValues[AtomBonds])->get(); }