/*
* 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.);
}