/*
* 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 "GLMoleculeObject_bond.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 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) :
GLMoleculeObject(mesh, parent),
Observer(std::string("GLMoleculeObject_atom")+toString(_id)),
atomref(getAtom(_id)),
ObservedValues(MAX_ObservedTypes),
subjectKilledCount(0),
owner(NULL)
{
initObservedValues(_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);
}
void GLMoleculeObject_atom::activateObserver()
{
if (atomref != NULL) {
owner = static_cast(atomref);
owner->signOn(this, AtomObservable::IndexChanged);
owner->signOn(this, AtomObservable::PositionChanged);
owner->signOn(this, AtomObservable::ElementChanged);
owner->signOn(this, AtomObservable::BondsAdded);
owner->signOn(this, AtomObservable::BondsRemoved);
}
}
void GLMoleculeObject_atom::deactivateObserver()
{
// sign Off
if (owner != NULL) {
owner->signOff(this, AtomObservable::IndexChanged);
owner->signOff(this, AtomObservable::PositionChanged);
owner->signOff(this, AtomObservable::ElementChanged);
owner->signOff(this, AtomObservable::BondsAdded);
owner->signOff(this, AtomObservable::BondsRemoved);
owner = NULL;
}
}
GLMoleculeObject_atom::~GLMoleculeObject_atom()
{
deactivateObserver();
destroyObservedValues();
}
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() const
{
return const_cast(World::getInstance()).lastChangedAtomId();
}
Vector GLMoleculeObject_atom::updatePosition() const
{
const atom * const _atom = getAtom(getAtomIndex());
if (_atom != NULL) {
return _atom->getPosition();
} else {
return zeroVec;
}
}
atomicNumber_t GLMoleculeObject_atom::updateElement() const
{
const atom * const _atom = getAtom(getAtomIndex());
if (_atom != NULL) {
return _atom->getElementNo();
} else {
return (atomicNumber_t)-1;
}
}
GLMoleculeObject_atom::ListOfBonds_t GLMoleculeObject_atom::updateBonds() const
{
ListOfBonds_t ListOfBonds;
const atom * const _atom = getAtom(getAtomIndex());
if (_atom != NULL) {
// make sure position 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)
{
deactivateObserver();
countsubjectKilled();
}
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::countsubjectKilled()
{
++subjectKilledCount;
if (subjectKilledCount > ObservedValues.size())
emit InstanceRemoved(getAtomIndex());
}
void GLMoleculeObject_atom::initObservedValues(const atomId_t _id)
{
// fill ObservedValues
boost::function subjectKilled =
boost::bind(&GLMoleculeObject_atom::countsubjectKilled, this);
ObservedValues[AtomIndex] = new ObservedValue_wCallback(
atomref,
boost::bind(&GLMoleculeObject_atom::updateIndex, this),
"AtomIndex_"+toString(_id),
_id,
AtomIndexChannels,
subjectKilled);
ObservedValues[AtomPosition] = new ObservedValue_wCallback(
atomref,
boost::bind(&GLMoleculeObject_atom::updatePosition, this),
"AtomPosition_"+toString(_id),
updatePosition(),
AtomPositionChannels,
subjectKilled);
ObservedValues[AtomElement] = new ObservedValue_wCallback(
atomref,
boost::bind(&GLMoleculeObject_atom::updateElement, this),
"AtomElement"+toString(_id),
updateElement(),
AtomElementChannels,
subjectKilled);
ObservedValues[AtomBonds] = new ObservedValue_wCallback(
atomref,
boost::bind(&GLMoleculeObject_atom::updateBonds, this),
"AtomBonds_"+toString(_id),
updateBonds(),
AtomBondsChannels,
subjectKilled);
}
void GLMoleculeObject_atom::destroyObservedValues()
{
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();
}