• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdeedu
  • Sitemap
  • Contact Us
 

kalzium

aligntool.cpp

Go to the documentation of this file.
00001 /**********************************************************************
00002   AlignTool - AlignTool Tool for Avogadro
00003 
00004   Copyright (C) 2008 Marcus D. Hanwell
00005 
00006   This file is part of the Avogadro molecular editor project.
00007   For more information, see <http://avogadro.sourceforge.net/>
00008 
00009   Avogadro is free software; you can redistribute it and/or modify
00010   it under the terms of the GNU General Public License as published by
00011   the Free Software Foundation; either version 2 of the License, or
00012   (at your option) any later version.
00013 
00014   Avogadro is distributed in the hope that it will be useful,
00015   but WITHOUT ANY WARRANTY; without even the implied warranty of
00016   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017   GNU General Public License for more details.
00018 
00019   You should have received a copy of the GNU General Public License
00020   along with this program; if not, write to the Free Software
00021   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00022   02110-1301, USA.
00023  **********************************************************************/
00024 
00025 #include "aligntool.h"
00026 
00027 #include <avogadro/navigate.h>
00028 #include <avogadro/primitive.h>
00029 #include <avogadro/color.h>
00030 #include <avogadro/glwidget.h>
00031 
00032 #include <openbabel/obiter.h>
00033 #include <openbabel/generic.h>
00034 
00035 #include <cmath>
00036 
00037 #include <QDebug>
00038 #include <QtPlugin>
00039 #include <QLabel>
00040 #include <QPushButton>
00041 #include <QComboBox>
00042 #include <QHBoxLayout>
00043 #include <QVBoxLayout>
00044 
00045 using namespace std;
00046 using namespace OpenBabel;
00047 using namespace Eigen;
00048 
00049 namespace Avogadro {
00050 
00051   AlignTool::AlignTool(QObject *parent) : Tool(parent),  m_molecule(0),
00052   m_numSelectedAtoms(0), m_axis(2), m_alignType(0), m_settingsWidget(0)
00053   {
00054     QAction *action = activateAction();
00055     action->setIcon(QIcon(QString::fromUtf8(":/align/align.png")));
00056     action->setToolTip(tr("Align Molecules\n\n"
00057           "Left Mouse: \tSelect up to two atoms.\n"
00058           "\tThe first atom is centered at the origin.\n"
00059           "\tThe second atom is aligned to the selected axis.\n"
00060           "Right Mouse: \tReset alignment."));
00061     action->setShortcut(Qt::Key_F12);
00062 
00063     // clear the selected atoms
00064     int size = m_selectedAtoms.size();
00065     for(int i=0; i<size; i++)
00066     {
00067       m_selectedAtoms[i] = NULL;
00068     }
00069   }
00070 
00071   AlignTool::~AlignTool()
00072   {
00073   }
00074 
00075   QUndoCommand* AlignTool::mousePress(GLWidget *widget, const QMouseEvent *event)
00076   {
00077     m_molecule = widget->molecule();
00078     if(!m_molecule)
00079       return 0;
00080 
00082     QList<GLHit> m_hits = widget->hits(event->pos().x()-2, event->pos().y()-2, 5, 5);
00083 
00084     // If there's a left button (and no modifier keys) continue adding to the list
00085     if(m_hits.size() && (event->buttons() & Qt::LeftButton && event->modifiers() == Qt::NoModifier))
00086     {
00087       if(m_hits[0].type() != Primitive::AtomType)
00088         return 0;
00089 
00090       Atom *atom = (Atom *)m_molecule->GetAtom(m_hits[0].name());
00091 
00092       if(m_numSelectedAtoms < 2)
00093       {
00094         // Select another atom
00095         m_selectedAtoms[m_numSelectedAtoms++] = atom;
00096         widget->update();
00097       }
00098     }
00099     // Right button or Left Button + modifier (e.g., Mac)
00100     else
00101     {
00102       m_numSelectedAtoms = 0;
00103       widget->update();
00104     }
00105     return 0;
00106   }
00107 
00108   QUndoCommand* AlignTool::mouseMove(GLWidget*, const QMouseEvent *)
00109   {
00110     return 0;
00111   }
00112 
00113   QUndoCommand* AlignTool::mouseRelease(GLWidget*, const QMouseEvent*)
00114   {
00115     return 0;
00116   }
00117 
00118   QUndoCommand* AlignTool::wheel(GLWidget*widget, const QWheelEvent*event)
00119   {
00120     // let's set the reference to be the center of the visible
00121     // part of the molecule.
00122     Eigen::Vector3d atomsBarycenter(0., 0., 0.);
00123     double sumOfWeights = 0.;
00124     std::vector<OpenBabel::OBNodeBase*>::iterator i;
00125     for ( Atom *atom = static_cast<Atom*>(widget->molecule()->BeginAtom(i));
00126           atom; atom = static_cast<Atom*>(widget->molecule()->NextAtom(i))) {
00127       Eigen::Vector3d transformedAtomPos = widget->camera()->modelview() * atom->pos();
00128       double atomDistance = transformedAtomPos.norm();
00129       double dot = transformedAtomPos.z() / atomDistance;
00130       double weight = exp(-30. * (1. + dot));
00131       sumOfWeights += weight;
00132       atomsBarycenter += weight * atom->pos();
00133     }
00134     atomsBarycenter /= sumOfWeights;
00135 
00136     Navigate::zoom(widget, atomsBarycenter, - MOUSE_WHEEL_SPEED * event->delta());
00137     widget->update();
00138 
00139     return NULL;
00140   }
00141 
00142   bool AlignTool::paint(GLWidget *widget)
00143   {
00144     if(m_numSelectedAtoms > 0)
00145     {
00146       Vector3d xAxis = widget->camera()->backTransformedXAxis();
00147       Vector3d zAxis = widget->camera()->backTransformedZAxis();
00148       // Check the atom is still around...
00149       if (m_selectedAtoms[0])
00150       {
00151         glColor3f(1.0,0.0,0.0);
00152         widget->painter()->setColor(1.0, 0.0, 0.0);
00153         Vector3d pos = m_selectedAtoms[0]->pos();
00154 
00155         // relative position of the text on the atom
00156         double radius = widget->radius(m_selectedAtoms[0]) + 0.05;
00157         Vector3d textRelPos = radius * (zAxis + xAxis);
00158 
00159         Vector3d textPos = pos+textRelPos;
00160         widget->painter()->drawText(textPos, "*1");
00161         widget->painter()->drawSphere(pos, radius);
00162       }
00163 
00164       if(m_numSelectedAtoms >= 2)
00165       {
00166         // Check the atom is still around...
00167         if (m_selectedAtoms[1])
00168         {
00169           glColor3f(0.0,1.0,0.0);
00170           widget->painter()->setColor(0.0, 1.0, 0.0);
00171           Vector3d pos = m_selectedAtoms[1]->pos();
00172           double radius = widget->radius(m_selectedAtoms[1]) + 0.05;
00173           widget->painter()->drawSphere(pos, radius);
00174           Vector3d textRelPos = radius * (zAxis + xAxis);
00175           Vector3d textPos = pos+textRelPos;
00176           widget->painter()->drawText(textPos, "*2");
00177         }
00178       }
00179     }
00180 
00181     return true;
00182   }
00183 
00184   void AlignTool::align()
00185   {
00186     // Check we have a molecule, otherwise we can't do anything
00187     if (m_molecule.isNull())
00188       return;
00189 
00190     QList<Primitive*> neighborList;
00191     if (m_numSelectedAtoms)
00192     {
00193       // Check the first atom still exists, return if not
00194       if (m_selectedAtoms[0].isNull())
00195         return;
00196 
00197       // If m_alignType is 0 we want everything, otherwise just the fragment
00198       if (m_alignType) {
00199         OBMolAtomDFSIter iter(m_molecule, m_selectedAtoms[0]->GetIdx());
00200         Atom *tmpNeighbor;
00201         do {
00202           tmpNeighbor = static_cast<Atom*>(&*iter);
00203           neighborList.append(tmpNeighbor);
00204         } while ((iter++).next()); // this returns false when we are done
00205       }
00206       else {
00207         FOR_ATOMS_OF_MOL(atom, m_molecule)
00208           neighborList.append(static_cast<const Atom *>(&(*atom)));
00209       }
00210     }
00211     // Align the molecule along the selected axis
00212     if (m_numSelectedAtoms >= 1)
00213     {
00214       // Translate the first selected atom to the origin
00215       MatrixP3d atomTranslation;
00216       atomTranslation.loadTranslation(-m_selectedAtoms[0]->pos());
00217       foreach(Primitive *p, neighborList)
00218       {
00219         if (!p) continue;
00220         Atom *a = static_cast<Atom *>(p);
00221         a->setPos(atomTranslation * a->pos());
00222         a->update();
00223       }
00224     }
00225     if (m_numSelectedAtoms >= 2)
00226     {
00227       // Check the second atom still exists, return if not
00228       if (m_selectedAtoms[1].isNull())
00229         return;
00230       // Now line up the line from atom[0] to atom[1] with the axis selected
00231       double alpha, beta, gamma;
00232       alpha = beta = gamma = 0.0;
00233 
00234       Vector3d pos = m_selectedAtoms[1]->pos();
00235       pos.normalize();
00236       Vector3d axis;
00237 
00238       if (m_axis == 0) // x-axis
00239         axis = Vector3d(1., 0., 0.);
00240       else if (m_axis == 1) // y-axis
00241         axis = Vector3d(0., 1., 0.);
00242       else if (m_axis == 2) // z-axis
00243         axis = Vector3d(0., 0., 1.);
00244 
00245       // Calculate the angle of the atom from the axis
00246       double angle = acos(axis.dot(pos));
00247 
00248       // If the angle is zero then we don't need to do anything here
00249       if (angle > 0)
00250       {
00251         // Get the axis for the rotation
00252         axis = axis.cross(pos);
00253         axis.normalize();
00254 
00255         // Now to load up the rotation matrix and rotate the molecule
00256         MatrixP3d atomRotation;
00257         atomRotation.loadRotation3(-angle, axis);
00258 
00259         // Now to rotate the fragment
00260         foreach(Primitive *p, neighborList)
00261         {
00262           Atom *a = static_cast<Atom *>(p);
00263           a->setPos(atomRotation * a->pos());
00264           a->update();
00265         }
00266       }
00267     }
00268     m_numSelectedAtoms = 0;
00269   }
00270 
00271   QWidget* AlignTool::settingsWidget()
00272   {
00273     if(!m_settingsWidget) {
00274       m_settingsWidget = new QWidget;
00275 
00276       QLabel *labelAxis = new QLabel(tr("Axis:"));
00277       labelAxis->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
00278       labelAxis->setMaximumHeight(15);
00279 
00280       // Combo box to select desired aixs to align to
00281       QComboBox *comboAxis = new QComboBox(m_settingsWidget);
00282       comboAxis->addItem("x");
00283       comboAxis->addItem("y");
00284       comboAxis->addItem("z");
00285       comboAxis->setCurrentIndex(2);
00286 
00287       QLabel *labelAlign = new QLabel(tr("Align:"));
00288       labelAlign->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
00289       labelAlign->setMaximumHeight(15);
00290       // Combo to choose what should be aligned
00291       QComboBox *comboAlign = new QComboBox(m_settingsWidget);
00292       comboAlign->addItem(tr("Everything"));
00293       comboAlign->addItem(tr("Molecule"));
00294 
00295       // Button to actually perform actions
00296       QPushButton *buttonAlign = new QPushButton(m_settingsWidget);
00297       buttonAlign->setText(tr("Align"));
00298       connect(buttonAlign, SIGNAL(clicked()), this, SLOT(align()));
00299 
00300       QGridLayout *gridLayout = new QGridLayout();
00301       gridLayout->addWidget(labelAxis,0, 0, 1, 1, Qt::AlignRight);
00302       QHBoxLayout *hLayout = new QHBoxLayout;
00303       hLayout->addWidget(comboAxis);
00304       hLayout->addStretch(1);
00305       gridLayout->addLayout(hLayout, 0, 1);
00306       gridLayout->addWidget(labelAlign, 1, 0, 1, 1, Qt::AlignRight);
00307       QHBoxLayout *hLayout2 = new QHBoxLayout;
00308       hLayout2->addWidget(comboAlign);
00309       hLayout2->addStretch(1);
00310       gridLayout->addLayout(hLayout2, 1, 1);
00311       QHBoxLayout *hLayout3 = new QHBoxLayout();
00312       hLayout3->addStretch(1);
00313       hLayout3->addWidget(buttonAlign);
00314       hLayout3->addStretch(1);
00315       QVBoxLayout *layout = new QVBoxLayout();
00316       layout->addLayout(gridLayout);
00317       layout->addLayout(hLayout3);
00318       layout->addStretch(1);
00319       m_settingsWidget->setLayout(layout);
00320 
00321       connect(comboAxis, SIGNAL(currentIndexChanged(int)),
00322               this, SLOT(axisChanged(int)));
00323       connect(comboAlign, SIGNAL(currentIndexChanged(int)),
00324               this, SLOT(alignChanged(int)));
00325 
00326       connect(m_settingsWidget, SIGNAL(destroyed()),
00327               this, SLOT(settingsWidgetDestroyed()));
00328     }
00329 
00330     return m_settingsWidget;
00331   }
00332 
00333   void AlignTool::axisChanged(int axis)
00334   {
00335     // Axis to use - x=0, y=1, z=2
00336     m_axis = axis;
00337   }
00338 
00339   void AlignTool::alignChanged(int align)
00340   {
00341     // Type of alignment - 0=everything, 1=molecule
00342     m_alignType = align;
00343   }
00344 
00345   void AlignTool::settingsWidgetDestroyed()
00346   {
00347     m_settingsWidget = 0;
00348   }
00349 
00350 }
00351 
00352 #include "aligntool.moc"
00353 
00354 Q_EXPORT_PLUGIN2(aligntool, Avogadro::AlignToolFactory)

kalzium

Skip menu "kalzium"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdeedu

Skip menu "kdeedu"
  • kalzium
  • kanagram
  • kig
  •   lib
  • klettres
  • kstars
  • libkdeedu
  •   keduvocdocument
  •   docs
  •   src
  • parley
  •   stepcore
Generated for kdeedu by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal