/* * 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_bond.cpp * * Created on: Aug 17, 2011 * Author: heber */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "GLMoleculeObject_bond.hpp" #include #include #include "UIElements/Qt4/InstanceBoard/QtObservedInstanceBoard.hpp" #include "CodePatterns/MemDebug.hpp" #include #include "CodePatterns/Assert.hpp" #include "CodePatterns/Log.hpp" #include "CodePatterns/Observer/Notification.hpp" #include "CodePatterns/Observer/ObserverLog.hpp" #include "Descriptors/AtomIdDescriptor.hpp" #include "Atom/atom.hpp" #include "Bond/bond.hpp" #include "Element/element.hpp" #include "Helpers/defs.hpp" #include "LinearAlgebra/Line.hpp" #include "LinearAlgebra/Vector.hpp" #include "UIElements/Qt4/InstanceBoard/ObservedValue_wCallback.hpp" #include "World.hpp" // static entities const Observable::channels_t GLMoleculeObject_bond::IndexChannels(1, AtomObservable::IndexChanged); const Observable::channels_t GLMoleculeObject_bond::BondPositionChannels(1, AtomObservable::PositionChanged); const Observable::channels_t GLMoleculeObject_bond::BondDegreeChannels(1, BondObservable::DegreeChanged); const Observable::channels_t GLMoleculeObject_bond::BondElementChannels(1, AtomObservable::ElementChanged); static GLMoleculeObject_bond::bondIds_t getBondIdsForIds( const boost::function &_left, const boost::function &_right) { return std::make_pair(_left(), _right()); } GLMoleculeObject_bond::GLMoleculeObject_bond( QGLSceneNode *mesh[], QObject *parent, const bondIds_t bondIds, const enum SideOfBond side) : GLMoleculeObject(mesh, parent), Observer(std::string("GLMoleculeObject_bond") +toString(bondIds.first) +std::string("-") +toString(bondIds.second)), leftowner(getAtomConst(bondIds.first)), rightowner(getAtomConst(bondIds.second)), bondowner(getAtomConst(bondIds.first)->getBond(getAtomConst(bondIds.second)).get()), BondSide(side), ObservedValues(MAX_ObservedTypes), subjectKilledCount(0), leftobservable_enabled(false), rightobservable_enabled(false), board_subjectKilled( boost::bind( static_cast( &GLMoleculeObject_bond::countsubjectKilled), this)) { boost::function leftsubjectKilled = boost::bind( static_cast( &GLMoleculeObject_bond::countsubjectKilled), this, _1); boost::function rightsubjectKilled = boost::bind( static_cast( &GLMoleculeObject_bond::countsubjectKilled) , this, _1); boost::function bondsubjectKilled = boost::bind( static_cast( &GLMoleculeObject_bond::countsubjectKilled) , this, _1); initObservedValues( ObservedValues, bondIds.first, bondIds.second, leftowner, rightowner, bondowner, leftsubjectKilled, rightsubjectKilled, bondsubjectKilled); init(); } GLMoleculeObject_bond::GLMoleculeObject_bond( QGLSceneNode *mesh[], QObject *parent, const bondIds_t bondIds, const enum SideOfBond side, std::vector &_ObservedValues, const boost::function &_subjectKilled) : GLMoleculeObject(mesh, parent), Observer(std::string("GLMoleculeObject_bond") +toString(bondIds.first) +std::string("-") +toString(bondIds.second)), leftowner(getAtomConst(bondIds.first)), rightowner(getAtomConst(bondIds.second)), bondowner(getAtomConst(bondIds.first)->getBond(getAtomConst(bondIds.second)).get()), BondSide(side), ObservedValues(_ObservedValues), subjectKilledCount(0), leftobservable_enabled(false), rightobservable_enabled(false), board_subjectKilled(_subjectKilled) { init(); } void GLMoleculeObject_bond::init() { // sign on as observer (obtain non-const instance before) bondowner->signOn(this, BondObservable::BondRemoved); bondowner->signOn(this, BondObservable::DegreeChanged); bond_enabled = true; leftowner->signOn(this, AtomObservable::PositionChanged); leftowner->signOn(this, AtomObservable::ElementChanged); leftobservable_enabled = true; rightowner->signOn(this, AtomObservable::PositionChanged); rightowner->signOn(this, AtomObservable::ElementChanged); rightobservable_enabled = true; resetElement(); resetPosition(); resetWidth(); connect(this, SIGNAL(elementChanged()), this, SLOT(resetElement()), Qt::QueuedConnection); connect(this, SIGNAL(positionChanged()), this, SLOT(resetPosition()), Qt::QueuedConnection); connect(this, SIGNAL(degreeChanged()), this, SLOT(resetWidth()), Qt::QueuedConnection); } GLMoleculeObject_bond::~GLMoleculeObject_bond() { LOG(3, "DEBUG: Destroying GLMoleculeObject_bond to bond [" << getleftIndex() << "," << getrightIndex() << "] and side " << BondSide << "."); // signOff() if not already done removeChannels(); destroyObservedValues(ObservedValues); } void GLMoleculeObject_bond::removeChannels() { // at this point both atoms should still be alive, hence we may safely sign off // from the AtomObservable itself if (bond_enabled) { if (bondowner != NULL) { bondowner->signOff(this, BondObservable::BondRemoved); bondowner->signOff(this, BondObservable::DegreeChanged); } bond_enabled = false; } if (leftobservable_enabled) { if (leftowner != NULL) { leftowner->signOff(this, AtomObservable::PositionChanged); leftowner->signOff(this, AtomObservable::ElementChanged); } leftobservable_enabled = false; } if (rightobservable_enabled) { if (rightowner != NULL) { rightowner->signOff(this, AtomObservable::PositionChanged); rightowner->signOff(this, AtomObservable::ElementChanged); } rightobservable_enabled = false; } } void GLMoleculeObject_bond::update(Observable *publisher) { ASSERT(0, "GLMoleculeObject_bond::update() - we are not signed on for any global updates."); } void GLMoleculeObject_bond::subjectKilled(Observable *publisher) { // we signOff from all other sources removeChannels(); // check whether we should be removed board_subjectKilled(std::make_pair(getleftIndex(), getrightIndex())); } void GLMoleculeObject_bond::recieveNotification(Observable *publisher, Notification_ptr notification) { #ifdef LOG_OBSERVER if (publisher == static_cast(bondowner)) { observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " received notification from bond for channel " << notification->getChannelNo() << "."; } else if (publisher == static_cast(leftowner)) { observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " received notification from leftatom " << getleftIndex() << " for channel " << notification->getChannelNo() << "."; } else if (publisher == static_cast(rightowner)) { observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " received notification from rightatom " << getrightIndex() << " for channel " << notification->getChannelNo() << "."; } #endif if (publisher == static_cast(bondowner)){ switch (notification->getChannelNo()) { case BondObservable::BondRemoved: // removeMe(); break; case BondObservable::DegreeChanged: emit degreeChanged(); break; default: ASSERT(0, "GLMoleculeObject_bond::recieveNotification() - unknown signal."); break; } } else { // from an atom switch (notification->getChannelNo()) { case AtomObservable::PositionChanged: LOG(2, "INFO: Received notification of PositionChanged."); emit positionChanged(); break; case AtomObservable::ElementChanged: LOG(2, "INFO: Received notification of ElementChanged."); emit elementChanged(); break; default: break; } } } atomId_t GLMoleculeObject_bond::updateIndex() { return const_cast(World::getInstance()).lastChangedAtomId(); } Vector GLMoleculeObject_bond::updateLeftPosition( const boost::function &_getLeftAtomIndex) { const atom * const _atom = getAtomConst(_getLeftAtomIndex()); return _atom->getPosition(); } Vector GLMoleculeObject_bond::updateRightPosition( const boost::function &_getRightAtomIndex) { const atom * const _atom = getAtomConst(_getRightAtomIndex()); return _atom->getPosition(); } atomicNumber_t GLMoleculeObject_bond::updateLeftElement( const boost::function &_getLeftAtomIndex) { const atom * const _atom = getAtomConst(_getLeftAtomIndex()); return _atom->getElementNo(); } atomicNumber_t GLMoleculeObject_bond::updateRightElement( const boost::function &_getRightAtomIndex) { const atom * const _atom = getAtomConst(_getRightAtomIndex()); return _atom->getElementNo(); } int GLMoleculeObject_bond::updateDegree( const boost::function &_getLeftAtomIndex, const boost::function &_getRightAtomIndex) { const atom * const _leftatom = const_cast(World::getInstance()). getAtom(AtomById(_getLeftAtomIndex())); const atom * const _rightatom = const_cast(World::getInstance()). getAtom(AtomById(_getRightAtomIndex())); if ((_leftatom != NULL) && (_rightatom != NULL)) { bond::ptr _bond = _leftatom->getBond(_rightatom); return _bond->getDegree(); } else { return 1; } } void GLMoleculeObject_bond::resetElement() { size_t elementno = getrightElement(); QGLMaterial *elementmaterial = getMaterial(elementno); setMaterial(elementmaterial); } void GLMoleculeObject_bond::resetWidth() { const double factor = 1.0f+.5f*(getDegree()-1); LOG(2, "DEBUG: GLMoleculeObject_bond::resetWidth() - setting bond's width to " << factor << "."); setScaleX(factor); setScaleY(factor); emit changed(); } void GLMoleculeObject_bond::resetPosition() { Vector Position = getleftPosition(); Vector OtherPosition = getrightPosition(); const double distance = Position.distance(OtherPosition)/2.; setScaleZ(distance); // calculate position Vector Z(unitVec[2]); // cylinder are initially aligned along the Z axis Vector zeroVec(0.,0.,0.); Vector a,b; Vector OtherAxis; double alpha; a = Position - OtherPosition; // construct rotation axis b = a; b.VectorProduct(Z); Line axis(zeroVec, b); // calculate rotation angle alpha = a.Angle(Z); // construct other axis to check right-hand rule OtherAxis = b; OtherAxis.VectorProduct(Z); // assure right-hand rule for the rotation if (a.ScalarProduct(OtherAxis) < MYEPSILON) alpha = M_PI-alpha; // check Vector a_rotated = axis.rotateVector(a, alpha); LOG(3, "INFO: Created cylinder from "// << Position << " to " << OtherPosition << a << " to " << a_rotated << " around " << b << " by " << alpha/M_PI*180. << ", respectively."); // set position (cylinder offset is in its barymetric center) Vector OneFourth(Position - 0.75 * a); setPosition(QVector3D(OneFourth[0], OneFourth[1], OneFourth[2])); setRotationVector(QVector3D(b[0], b[1], b[2])); setRotationAngle(alpha/M_PI*180.); emit changed(); } atom * const GLMoleculeObject_bond::getAtom(const atomId_t _id) { atom * const _atom = World::getInstance().getAtom(AtomById(_id)); return _atom; } const atom * const GLMoleculeObject_bond::getAtomConst(const atomId_t _id) { const atom * const _atom = const_cast(World::getInstance()). getAtom(AtomById(_id)); return _atom; } void GLMoleculeObject_bond::countsubjectKilled() { ++subjectKilledCount; if (subjectKilledCount > ObservedValues.size()) emit BondRemoved(getleftIndex(), getrightIndex()); } void GLMoleculeObject_bond::initObservedValues( std::vector &_ObservedValues, const atomId_t _leftatomId, const atomId_t _rightatomId, const Observable * const _leftowner, const Observable * const _rightowner, const Observable * const _bondowner, const boost::function &_leftsubjectKilled, const boost::function &_rightsubjectKilled, const boost::function &_bondsubjectKilled) { /* 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_bond::initObservedValues() - given ObservedValues has not correct size."); // fill ObservedValues: index first // Note that we only need one as the function just checks on the last changed id // and ids cannot be changed simultaneously const boost::function AtomIndexUpdater( boost::bind(&GLMoleculeObject_bond::updateIndex)); ObservedValue_wCallback * const LeftIndexObservable = new ObservedValue_wCallback( _leftowner, AtomIndexUpdater, "Bonds_LeftAtomIndex_"+toString(_leftatomId), _leftatomId, IndexChannels, _leftsubjectKilled); _ObservedValues[leftIndex] = LeftIndexObservable; ObservedValue_wCallback * const RightIndexObservable = new ObservedValue_wCallback( _rightowner, AtomIndexUpdater, "Bonds_RightAtomIndex_"+toString(_rightatomId), _rightatomId, IndexChannels, _rightsubjectKilled); _ObservedValues[rightIndex] = RightIndexObservable; const boost::function LeftIndexGetter = boost::bind(&ObservedValue_wCallback::get, LeftIndexObservable); const boost::function RightIndexGetter = boost::bind(&ObservedValue_wCallback::get, RightIndexObservable); // fill ObservedValues: then all the other that need index const boost::function LeftPositionUpdater( boost::bind(&GLMoleculeObject_bond::updateLeftPosition, LeftIndexGetter)); const boost::function RightPositionUpdater( boost::bind(&GLMoleculeObject_bond::updateRightPosition, RightIndexGetter)); const boost::function LeftElementUpdater( boost::bind(&GLMoleculeObject_bond::updateLeftElement, LeftIndexGetter)); const boost::function RightElementUpdater( boost::bind(&GLMoleculeObject_bond::updateRightElement, RightIndexGetter)); const boost::function DegreeUpdater( boost::bind(&GLMoleculeObject_bond::updateDegree, LeftIndexGetter, RightIndexGetter)); const boost::function BondIdGetter( boost::bind(&getBondIdsForIds, LeftIndexGetter, RightIndexGetter)); _ObservedValues[leftPosition] = new ObservedValue_wCallback( _leftowner, LeftPositionUpdater, "BondleftPosition_"+toString(_leftatomId), LeftPositionUpdater(), BondPositionChannels, _leftsubjectKilled, LeftIndexGetter); _ObservedValues[rightPosition] = new ObservedValue_wCallback( _rightowner, RightPositionUpdater, "BondrightPosition_"+toString(_rightatomId), RightPositionUpdater(), BondPositionChannels, _rightsubjectKilled, RightIndexGetter); _ObservedValues[leftElement] = new ObservedValue_wCallback( _leftowner, LeftElementUpdater, "BondleftElement"+toString(_leftatomId), LeftElementUpdater(), BondElementChannels, _leftsubjectKilled, LeftIndexGetter); _ObservedValues[rightElement] = new ObservedValue_wCallback( _rightowner, RightElementUpdater, "BondrightElement"+toString(_rightatomId), RightElementUpdater(), BondElementChannels, _rightsubjectKilled, RightIndexGetter); _ObservedValues[Degree] = new ObservedValue_wCallback( _bondowner, DegreeUpdater, "BondDegree"+toString(_leftatomId)+"_"+toString(_rightatomId), DegreeUpdater(), BondDegreeChannels, _bondsubjectKilled, BondIdGetter); } void GLMoleculeObject_bond::destroyObservedValues( std::vector &_ObservedValues) { delete boost::any_cast *>(_ObservedValues[leftIndex]); delete boost::any_cast *>(_ObservedValues[rightIndex]); delete boost::any_cast *>(_ObservedValues[leftPosition]); delete boost::any_cast *>(_ObservedValues[rightPosition]); delete boost::any_cast *>(_ObservedValues[leftElement]); delete boost::any_cast *>(_ObservedValues[rightElement]); delete boost::any_cast *>(_ObservedValues[Degree]); _ObservedValues.clear(); } const atomId_t& GLMoleculeObject_bond::getleftIndex() const { return boost::any_cast *>(ObservedValues[leftIndex])->get(); } const atomId_t& GLMoleculeObject_bond::getrightIndex() const { return boost::any_cast *>(ObservedValues[rightIndex])->get(); } const Vector& GLMoleculeObject_bond::getleftPosition() const { return boost::any_cast *>(ObservedValues[leftPosition])->get(); } const Vector& GLMoleculeObject_bond::getrightPosition() const { return boost::any_cast *>(ObservedValues[rightPosition])->get(); } const atomicNumber_t& GLMoleculeObject_bond::getleftElement() const { return boost::any_cast *>(ObservedValues[leftElement])->get(); } const atomicNumber_t& GLMoleculeObject_bond::getrightElement() const { return boost::any_cast *>(ObservedValues[rightElement])->get(); } const int& GLMoleculeObject_bond::getDegree() const { return boost::any_cast *>(ObservedValues[Degree])->get(); }