/* * 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 "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 "World.hpp" GLMoleculeObject_bond::GLMoleculeObject_bond(QGLSceneNode *mesh[], QObject *parent, const bond::ptr bondref, const enum SideOfBond side) : GLMoleculeObject(mesh, parent), Observer(std::string("GLMoleculeObject_bond") +toString(bondref->leftatom->getId()) +std::string("-") +toString(bondref->rightatom->getId())), _bond(*bondref), leftobservable(bondref->leftatom), rightobservable(bondref->rightatom), leftatomId(bondref->leftatom->getId()), rightatomId(bondref->rightatom->getId()), BondSide(side), leftobservable_enabled(false), rightobservable_enabled(false), bond_enabled(false) { // sign on as observer (obtain non-const instance before) _bond.signOn(this); _bond.signOn(this, BondObservable::BondRemoved); _bond.signOn(this, BondObservable::DegreeChanged); bond_enabled = true; leftobservable->signOn(this); leftobservable->signOn(this, AtomObservable::PositionChanged); leftobservable->signOn(this, AtomObservable::ElementChanged); leftobservable_enabled = true; rightobservable->signOn(this); rightobservable->signOn(this, AtomObservable::PositionChanged); rightobservable->signOn(this, AtomObservable::ElementChanged); rightobservable_enabled = true; size_t elementno = 0; switch (BondSide) { case left: { const atom *_rightatom = World::getInstance().getAtom(AtomById(rightatomId)); if (_rightatom->getType() != NULL) { elementno = _rightatom->getType()->getAtomicNumber(); } else { // if not element yet set, set to hydrogen elementno = 1; } break; } case right: { const atom *_leftatom = World::getInstance().getAtom(AtomById(leftatomId)); if (_leftatom->getType() != NULL) { elementno = _leftatom->getType()->getAtomicNumber(); } else { // if not element yet set, set to hydrogen elementno = 1; } break; } default: ASSERT(0, "GLMoleculeObject_bond::GLMoleculeObject_bond() - side is not a valid argument: "+toString(BondSide)+"."); break; } QGLMaterial *elementmaterial = getMaterial(elementno); setMaterial(elementmaterial); resetPosition(); resetWidth(); } GLMoleculeObject_bond::~GLMoleculeObject_bond() { LOG(3, "DEBUG: Destroying GLMoleculeObject_bond to bond " << &_bond << " and side " << BondSide << "."); // signOff() if not already done removeLeftAtom(); removeRightAtom(); removeBond(); removeChannels(); } void GLMoleculeObject_bond::removeLeftAtom() { // at this point both atoms should still be alive, hence we may safely sign off // from the AtomObservable itself if (leftobservable_enabled) { const atom *_leftatom = World::getInstance().getAtom(AtomById(leftatomId)); if (_leftatom != NULL) { _leftatom->signOff(this); } else { ELOG(2, "Left atom of bond with id "+toString(leftatomId)+" is already gone."); } } } void GLMoleculeObject_bond::removeRightAtom() { // at this point both atoms should still be alive, hence we may safely sign off // from the AtomObservable itself if (rightobservable_enabled) { const atom *_rightatom = World::getInstance().getAtom(AtomById(rightatomId)); if (_rightatom != NULL) { _rightatom->signOff(this); } else { ELOG(2, "Right atom of bond with id "+toString(rightatomId)+" is already gone."); } } } void GLMoleculeObject_bond::removeBond() { if (bond_enabled) _bond.signOff(this); } void GLMoleculeObject_bond::removeChannels() { // at this point both atoms should still be alive, hence we may safely sign off // from the AtomObservable itself if (leftobservable_enabled) { const atom *_leftatom = World::getInstance().getAtom(AtomById(leftatomId)); if (_leftatom != NULL) { _leftatom->signOff(this, AtomObservable::PositionChanged); _leftatom->signOff(this, AtomObservable::ElementChanged); } else { ELOG(2, "Left atom of bond with id "+toString(leftatomId)+" is already gone."); } leftobservable_enabled = false; } if (rightobservable_enabled) { const atom *_rightatom = World::getInstance().getAtom(AtomById(rightatomId)); if (_rightatom != NULL) { _rightatom->signOff(this, AtomObservable::PositionChanged); _rightatom->signOff(this, AtomObservable::ElementChanged); } else { ELOG(2, "Right atom of bond with id "+toString(rightatomId)+" is already gone."); } rightobservable_enabled = false; } if (bond_enabled) { _bond.signOff(this, BondObservable::BondRemoved); _bond.signOff(this, BondObservable::DegreeChanged); bond_enabled = false; } } void GLMoleculeObject_bond::removeMe() { // sign off switch (BondSide) { case left: emit BondRemoved(leftatomId, rightatomId); break; case right: emit BondRemoved(rightatomId, leftatomId); break; default: ASSERT(0, "GLMoleculeObject_bond::subjectKilled() - side is not a valid argument: " +toString(BondSide)+"."); break; } } void GLMoleculeObject_bond::update(Observable *publisher) { #ifdef LOG_OBSERVER if (publisher == static_cast(&_bond)) { observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " from bond."; } else if (publisher == leftobservable) { observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " from leftatom " << leftatomId << "."; } else if (publisher == rightobservable) { observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " from rightatom " << rightatomId << "."; } else observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " from unknown source."; #endif } void GLMoleculeObject_bond::subjectKilled(Observable *publisher) { // assume subjectKilled() is from Observable's own subjectKilled(), not notifications // but we must always signOff from all other sources! if (publisher == static_cast(&_bond)) { #ifdef LOG_OBSERVER observerLog().addMessage() << "++ subjectKilled of Observer " << observerLog().getName(static_cast(this)) << " from bond."; #endif removeLeftAtom(); removeRightAtom(); } else if (publisher == leftobservable) { #ifdef LOG_OBSERVER observerLog().addMessage() << "++ subjectKilled of Observer " << observerLog().getName(static_cast(this)) << " from leftatom " << leftatomId << "."; #endif removeRightAtom(); removeBond(); } else if (publisher == rightobservable) { #ifdef LOG_OBSERVER observerLog().addMessage() << "++ subjectKilled of Observer " << observerLog().getName(static_cast(this)) << " from rightatom " << rightatomId << "."; #endif removeLeftAtom(); removeBond(); } else { #ifdef LOG_OBSERVER observerLog().addMessage() << "++ subjectKilled of Observer " << observerLog().getName(static_cast(this)) << " from unknown source."; #endif } // then indicate to remove us removeChannels(); removeMe(); } void GLMoleculeObject_bond::recieveNotification(Observable *publisher, Notification_ptr notification) { #ifdef LOG_OBSERVER if (publisher == static_cast(&_bond)) { observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " received notification from bond for channel " << notification->getChannelNo() << "."; } else if (publisher == leftobservable) { observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " received notification from leftatom " << leftatomId << " for channel " << notification->getChannelNo() << "."; } else if (publisher == rightobservable) { observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " received notification from rightatom " << rightatomId << " for channel " << notification->getChannelNo() << "."; } else observerLog().addMessage() << "++ Update of Observer " << observerLog().getName(static_cast(this)) << " received notification from unknown source."; #endif bool DoResetPosition = false; bool DoResetWidth = false; if (publisher == static_cast(&_bond)){ switch (notification->getChannelNo()) { case BondObservable::BondRemoved: // removeMe(); break; case BondObservable::DegreeChanged: DoResetWidth = true; 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."); DoResetPosition = true; break; case AtomObservable::ElementChanged: LOG(2, "INFO: Received notification of ElementChanged."); DoResetPosition = true; break; default: break; } } if (DoResetPosition) { resetPosition(); emit changed(); } if (DoResetWidth) { resetWidth(); emit changed(); } } void GLMoleculeObject_bond::resetWidth() { const double factor = 1.0f+.5f*(_bond.getDegree()-1); LOG(2, "DEBUG: GLMoleculeObject_bond::resetWidth() - setting bond's width to " << factor << "."); setScaleX(factor); setScaleY(factor); } void GLMoleculeObject_bond::resetPosition() { Vector Position; Vector OtherPosition; const atom *_leftatom = World::getInstance().getAtom(AtomById(leftatomId)); Vector LeftPos = _leftatom->getPosition(); const atom *_rightatom = World::getInstance().getAtom(AtomById(rightatomId)); Vector RightPos = _rightatom->getPosition(); switch (BondSide) { case left: Position = LeftPos; OtherPosition = RightPos; break; case right: Position = RightPos; OtherPosition = LeftPos; break; default: ASSERT(0, "GLMoleculeObject_bond::resetPosition() - side is not a valid argument: "+toString(BondSide)+"."); break; } 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.); }