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

kalzium

clickmeasuretool.cpp

Go to the documentation of this file.
00001 /**********************************************************************
00002   ClickMeasureTool - ClickMeasureTool Tool for Avogadro
00003 
00004   Copyright (C) 2007 Donald Ephraim Curtis
00005   Copyright (C) 2008 Marcus D. Hanwell
00006 
00007   This file is part of the Avogadro molecular editor project.
00008   For more information, see <http://avogadro.sourceforge.net/>
00009 
00010   Avogadro is free software; you can redistribute it and/or modify
00011   it under the terms of the GNU General Public License as published by
00012   the Free Software Foundation; either version 2 of the License, or
00013   (at your option) any later version.
00014 
00015   Avogadro is distributed in the hope that it will be useful,
00016   but WITHOUT ANY WARRANTY; without even the implied warranty of
00017   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018   GNU General Public License for more details.
00019 
00020   You should have received a copy of the GNU General Public License
00021   along with this program; if not, write to the Free Software
00022   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00023   02110-1301, USA.
00024  **********************************************************************/
00025 
00026 #include "clickmeasuretool.h"
00027 
00028 #include <avogadro/navigate.h>
00029 #include <avogadro/primitive.h>
00030 #include <avogadro/color.h>
00031 #include <avogadro/glwidget.h>
00032 
00033 #include <openbabel/obiter.h>
00034 #include <openbabel/generic.h>
00035 
00036 #include <math.h>
00037 
00038 #include <QtPlugin>
00039 
00040 using namespace std;
00041 using namespace OpenBabel;
00042 using namespace Eigen;
00043 
00044 namespace Avogadro {
00045 
00046   ClickMeasureTool::ClickMeasureTool(QObject *parent) : Tool(parent),  m_numSelectedAtoms(0)
00047   {
00048     QAction *action = activateAction();
00049     action->setIcon(QIcon(QString::fromUtf8(":/measure/measure.png")));
00050     action->setToolTip(tr("Click to Measure (F12)\n\n"
00051           "Left Mouse: \tSelect up to three Atoms.\n"
00052           "\tDistances are measured between 1-2 and 2-3\n"
00053           "\tAngle is measured between 1-3 using 2 as the common point\n"
00054           "Right Mouse: Reset the measurements."));
00055     action->setShortcut(Qt::Key_F12);
00056     m_lastMeasurement.resize(5);
00057     for (int i = 0; i < m_lastMeasurement.size(); ++i)
00058       m_lastMeasurement[i] = 0.0;
00059   }
00060 
00061   ClickMeasureTool::~ClickMeasureTool()
00062   {
00063   }
00064 
00065   QUndoCommand* ClickMeasureTool::mousePress(GLWidget *widget, const QMouseEvent *event)
00066   {
00067     Molecule *molecule = widget->molecule();
00068     if(!molecule) {
00069       return 0;
00070     }
00071 
00073     m_hits = widget->hits(event->pos().x()-2, event->pos().y()-2, 5, 5);
00074 
00075     // If there's a left button (and no modifier keys) continue adding to the list
00076     if(m_hits.size() && (event->buttons() & Qt::LeftButton && event->modifiers() == Qt::NoModifier))
00077     {
00078       if(m_hits[0].type() != Primitive::AtomType)
00079         return 0;
00080 
00081       Atom *atom = (Atom *)molecule->GetAtom(m_hits[0].name());
00082 
00083       if(m_numSelectedAtoms < 4) {
00084         // Select another atom
00085         m_selectedAtoms[m_numSelectedAtoms++] = atom;
00086         widget->update();
00087       }
00088     }
00089     // Right button or Left Button + modifier (e.g., Mac)
00090     else
00091     {
00092       m_angle = 0;
00093       m_vector[0].loadZero();
00094       m_vector[1].loadZero();
00095       m_numSelectedAtoms = 0;
00096       widget->update();
00097     }
00098     return 0;
00099   }
00100 
00101   QUndoCommand* ClickMeasureTool::mouseMove(GLWidget*, const QMouseEvent *)
00102   {
00103     return 0;
00104   }
00105 
00106   QUndoCommand* ClickMeasureTool::mouseRelease(GLWidget*, const QMouseEvent*)
00107   {
00108     return 0;
00109   }
00110 
00111   QUndoCommand* ClickMeasureTool::wheel(GLWidget*widget, const QWheelEvent*event)
00112   {
00113     // let's set the reference to be the center of the visible
00114     // part of the molecule.
00115     Eigen::Vector3d atomsBarycenter(0., 0., 0.);
00116     double sumOfWeights = 0.;
00117     std::vector<OpenBabel::OBNodeBase*>::iterator i;
00118     for ( Atom *atom = static_cast<Atom*>(widget->molecule()->BeginAtom(i));
00119           atom; atom = static_cast<Atom*>(widget->molecule()->NextAtom(i))) {
00120       Eigen::Vector3d transformedAtomPos = widget->camera()->modelview() * atom->pos();
00121       double atomDistance = transformedAtomPos.norm();
00122       double dot = transformedAtomPos.z() / atomDistance;
00123       double weight = exp(-30. * (1. + dot));
00124       sumOfWeights += weight;
00125       atomsBarycenter += weight * atom->pos();
00126     }
00127     atomsBarycenter /= sumOfWeights;
00128 
00129     Navigate::zoom(widget, atomsBarycenter, - MOUSE_WHEEL_SPEED * event->delta());
00130     widget->update();
00131 
00132     return NULL;
00133   }
00134 
00135   void ClickMeasureTool::calculateParameters()
00136   {
00137     // Calculate all parameters and store them in member variables.
00138     if(m_numSelectedAtoms >= 2)
00139     {
00140       // Check the selected atoms still exist
00141       if (m_selectedAtoms[0].isNull() || m_selectedAtoms[1].isNull())
00142         return;
00143 
00144       // Two atoms selected - distance measurement only
00145       m_vector[0] = m_selectedAtoms[1]->pos() - m_selectedAtoms[0]->pos();
00146       QString distanceString = tr("Distance (1->2): %1 %2").arg(
00147                                 QString::number(m_vector[0].norm()),
00148                                 QString::fromUtf8("Å"));
00149 
00150       // Check whether we have already sent this out...
00151       if (m_lastMeasurement.at(0) != m_vector[0].norm()) {
00152         emit message(distanceString);
00153         m_lastMeasurement[0] = m_vector[0].norm();
00154       }
00155     }
00156     if(m_numSelectedAtoms >= 3)
00157     {
00158       // Check the selected atoms still exist
00159       if (m_selectedAtoms[2].isNull())
00160         return;
00161 
00162       // Two distances and the angle between the three selected atoms
00163       m_vector[1] = m_selectedAtoms[1]->pos() - m_selectedAtoms[2]->pos();
00164       QString distanceString = tr("Distance (2->3): %1 %2").arg(
00165                                QString::number(m_vector[1].norm()),
00166                                QString::fromUtf8("Å"));
00167 
00168       // Calculate the angle between the atoms
00169       m_angle = vectorAngle(vector3(m_vector[0].x(), m_vector[0].y(), m_vector[0].z()),
00170                             vector3(m_vector[1].x(), m_vector[1].y(), m_vector[1].z()));
00171       QString angleString = tr("Angle: %1 %2").arg(
00172                             QString::number(m_angle),
00173                             QString("°"));
00174 
00175       // Check whether we have already sent this out
00176       if (m_lastMeasurement.at(1) != m_vector[1].norm()) {
00177         emit message(distanceString);
00178         m_lastMeasurement[1] = m_vector[1].norm();
00179       }
00180       if (m_lastMeasurement.at(3) != m_angle) {
00181         emit message(angleString);
00182         m_lastMeasurement[3] = m_angle;
00183       }
00184     }
00185     if(m_numSelectedAtoms >= 4)
00186     {
00187       // Check the selected atoms still exist
00188       if (m_selectedAtoms[3].isNull())
00189         return;
00190 
00191       // Three distances, bond angle and dihedral angle
00192       m_vector[2] = m_selectedAtoms[2]->pos() - m_selectedAtoms[3]->pos();
00193       QString distanceString = tr("Distance (3->4): %1 %2").arg(
00194                                 QString::number(m_vector[2].norm()),
00195                                 QString::fromUtf8("Å"));
00196       m_dihedral = CalcTorsionAngle(vector3(m_selectedAtoms[0]->pos().x(),
00197                                 m_selectedAtoms[0]->pos().y(),
00198                                 m_selectedAtoms[0]->pos().z()),
00199                                 vector3(m_selectedAtoms[1]->pos().x(),
00200                                 m_selectedAtoms[1]->pos().y(),
00201                                 m_selectedAtoms[1]->pos().z()),
00202                                 vector3(m_selectedAtoms[2]->pos().x(),
00203                                 m_selectedAtoms[2]->pos().y(),
00204                                 m_selectedAtoms[2]->pos().z()),
00205                                 vector3(m_selectedAtoms[3]->pos().x(),
00206                                 m_selectedAtoms[3]->pos().y(),
00207                                 m_selectedAtoms[3]->pos().z()));
00208       QString dihedralString = tr("Dihedral Angle: %1 %2").arg(
00209                                 QString::number(m_dihedral),
00210                                 QString("°"));
00211 
00212       // Check whether these measurements have been sent already
00213       if (m_lastMeasurement.at(2) != m_vector[2].norm()) {
00214         emit message(distanceString);
00215         m_lastMeasurement[2] = m_vector[2].norm();
00216       }
00217       if (m_lastMeasurement.at(4) != m_dihedral) {
00218         emit message(dihedralString);
00219         m_lastMeasurement[4] = m_angle;
00220       }
00221     }
00222   }
00223 
00224   bool ClickMeasureTool::paint(GLWidget *widget)
00225   {
00226     if(0 < m_numSelectedAtoms && m_selectedAtoms[0])
00227     {
00228       calculateParameters();
00229 
00230       // Try to put the labels in a reasonable place on the display
00231       QPoint labelPos(95, widget->height()-25);
00232       QPoint distancePos[3];
00233       distancePos[0] = QPoint(180, widget->height()-25);
00234       distancePos[1] = QPoint(240, widget->height()-25);
00235       distancePos[2] = QPoint(300, widget->height()-25);
00236 
00237       QPoint angleLabelPos(95, widget->height()-45);
00238       QPoint anglePos(180, widget->height()-45);
00239 
00240       QPoint dihedralLabelPos(95, widget->height()-65);
00241       QPoint dihedralPos(180, widget->height()-65);
00242 
00243       glColor3f(1.0,0.0,0.0);
00244       Vector3d pos = m_selectedAtoms[0]->pos();
00245       double radius = 0.18 + etab.GetVdwRad(m_selectedAtoms[0]->GetAtomicNum()) * 0.3 ;
00246 
00247       Vector3d xAxis = widget->camera()->backTransformedXAxis();
00248       Vector3d zAxis = widget->camera()->backTransformedZAxis();
00249 
00250       // relative position of the text on the atom
00251       Vector3d textRelPos = radius * (zAxis + xAxis);
00252 
00253       Vector3d textPos = pos+textRelPos;
00254       widget->painter()->drawText(textPos, tr("*1", "*1 is a number. You most likely do not need to translate this" ));
00255 
00256       if(m_numSelectedAtoms >= 2 && m_selectedAtoms[1])
00257       {
00258         glColor3f(0.0,1.0,0.0);
00259         pos = m_selectedAtoms[1]->pos();
00260         Vector3d textPos = pos+textRelPos;
00261         radius = 0.18 + etab.GetVdwRad(m_selectedAtoms[1]->GetAtomicNum()) * 0.3;
00262         widget->painter()->drawText(textPos, tr("*2", "*2 is a number. You most likely do not need to translate this"));
00263 
00264         if(m_numSelectedAtoms >= 3 && m_selectedAtoms[2])
00265         {
00266           // Display a label on the third atom
00267           pos = m_selectedAtoms[2]->pos();
00268           radius = 0.18 + etab.GetVdwRad(m_selectedAtoms[2]->GetAtomicNum()) * 0.3;
00269           textPos = pos+textRelPos;
00270           glColor3f(0.0,0.0,1.0);
00271           widget->painter()->drawText(textPos, tr("*3", "*3 is a number. You most likely do not need to translate this"));
00272         }
00273         if(m_numSelectedAtoms >= 4 && m_selectedAtoms[3])
00274         {
00275           // Display a label on the fourth atom
00276           pos = m_selectedAtoms[3]->pos();
00277           radius = 0.18 + etab.GetVdwRad(m_selectedAtoms[3]->GetAtomicNum()) * 0.3;
00278           textPos = pos + textRelPos;
00279           glColor3f(0.0,1.0,1.0);
00280           widget->painter()->drawText(textPos, tr("*4", "*4 is a number. You most likely do not need to translate this"));
00281         }
00282         //       glLoadIdentity();
00283         glColor3f(1.0,1.0,1.0);
00284         widget->painter()->drawText(labelPos, tr("Distance(s):"));
00285 
00286         glColor3f(1.0,1.0,0.0);
00287         widget->painter()->drawText(distancePos[0], QString::number(m_vector[0].norm(), 10, 2) + QString::fromUtf8(" Å"));
00288 
00289         if(m_numSelectedAtoms >= 3)
00290         {
00291           glColor3f(1.0,1.0,1.0);
00292           widget->painter()->drawText(angleLabelPos, QString("Angle:"));
00293 
00294           glColor3f(0.8, 0.8, 0.8);
00295           widget->painter()->drawText(anglePos, QString::number(m_angle, 10, 1) + QString::fromUtf8("°"));
00296 
00297           glColor3f(0.0,1.0,1.0);
00298           widget->painter()->drawText(distancePos[1], QString::number(m_vector[1].norm(), 10, 2) + QString::fromUtf8(" Å"));
00299         }
00300 
00301         if(m_numSelectedAtoms >= 4)
00302         {
00303           glColor3f(1.0, 1.0, 1.0);
00304           widget->painter()->drawText(dihedralLabelPos, QString("Dihedral:"));
00305 
00306           glColor3f(0.6, 0.6, 0.6);
00307           widget->painter()->drawText(dihedralPos, QString::number(m_dihedral, 10, 1) + QString::fromUtf8("°"));
00308 
00309           glColor3f(1.0, 1.0, 1.0);
00310           widget->painter()->drawText(distancePos[2], QString::number(m_vector[2].norm(), 10, 2) + QString::fromUtf8(" Å"));
00311         }
00312 
00313 
00314         // If there are three atoms selected, draw the angle in question
00315         if(m_numSelectedAtoms >= 3 && m_selectedAtoms[0] && m_selectedAtoms[1]
00316           && m_selectedAtoms[2])
00317         {
00318           Vector3d origin = m_selectedAtoms[1]->pos();
00319           Vector3d d1 = m_selectedAtoms[0]->pos() - origin;
00320           Vector3d d2 = m_selectedAtoms[2]->pos() - origin;
00321           // The vector length is half the average vector length
00322           double radius = (d1.norm()+d2.norm()) * 0.25;
00323           // Adjust the length of u and v to the length calculated above.
00324           d1 = (d1 / d1.norm()) * radius;
00325           d2 = (d2 / d2.norm()) * radius;
00326           if (m_angle < 1) return true;
00327           // Vector perpindicular to both d1 and d2
00328           Vector3d n = d1.cross(d2);
00329 
00330           Vector3d xAxis = Vector3d(1, 0, 0);
00331           Vector3d yAxis = Vector3d(0, 1, 0);
00332 
00333           if (n.norm() < 1e-16)
00334           {
00335             Eigen::Vector3d A = d1.cross(xAxis);
00336             Eigen::Vector3d B = d1.cross(yAxis);
00337 
00338             n = A.norm() >= B.norm() ? A : B;
00339           }
00340 
00341           n = n / n.norm();
00342           glEnable(GL_BLEND);
00343           glDepthMask(GL_FALSE);
00344           widget->painter()->setColor(0, 1.0, 0, 0.3);
00345           widget->painter()->drawShadedSector(origin, m_selectedAtoms[0]->pos(),
00346                                              m_selectedAtoms[2]->pos(), radius);
00347           glDepthMask(GL_TRUE);
00348           glDisable(GL_BLEND);
00349           widget->painter()->setColor(1.0, 1.0, 1.0, 1.0);
00350           widget->painter()->drawArc(origin, m_selectedAtoms[0]->pos(),
00351                                      m_selectedAtoms[2]->pos(), radius, 1.0);
00352         }
00353       }
00354     }
00355 
00356     return true;
00357   }
00358 }
00359 
00360 #include "clickmeasuretool.moc"
00361 
00362 Q_EXPORT_PLUGIN2(clickmeasuretool, Avogadro::ClickMeasureToolFactory)

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