00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
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
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
00085 m_selectedAtoms[m_numSelectedAtoms++] = atom;
00086 widget->update();
00087 }
00088 }
00089
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
00114
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
00138 if(m_numSelectedAtoms >= 2)
00139 {
00140
00141 if (m_selectedAtoms[0].isNull() || m_selectedAtoms[1].isNull())
00142 return;
00143
00144
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
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
00159 if (m_selectedAtoms[2].isNull())
00160 return;
00161
00162
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
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
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
00188 if (m_selectedAtoms[3].isNull())
00189 return;
00190
00191
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
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
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
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
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
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
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
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
00322 double radius = (d1.norm()+d2.norm()) * 0.25;
00323
00324 d1 = (d1 / d1.norm()) * radius;
00325 d2 = (d2 / d2.norm()) * radius;
00326 if (m_angle < 1) return true;
00327
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)