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