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 #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
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
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
00095 m_selectedAtoms[m_numSelectedAtoms++] = atom;
00096 widget->update();
00097 }
00098 }
00099
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
00121
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
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
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
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
00187 if (m_molecule.isNull())
00188 return;
00189
00190 QList<Primitive*> neighborList;
00191 if (m_numSelectedAtoms)
00192 {
00193
00194 if (m_selectedAtoms[0].isNull())
00195 return;
00196
00197
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());
00205 }
00206 else {
00207 FOR_ATOMS_OF_MOL(atom, m_molecule)
00208 neighborList.append(static_cast<const Atom *>(&(*atom)));
00209 }
00210 }
00211
00212 if (m_numSelectedAtoms >= 1)
00213 {
00214
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
00228 if (m_selectedAtoms[1].isNull())
00229 return;
00230
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)
00239 axis = Vector3d(1., 0., 0.);
00240 else if (m_axis == 1)
00241 axis = Vector3d(0., 1., 0.);
00242 else if (m_axis == 2)
00243 axis = Vector3d(0., 0., 1.);
00244
00245
00246 double angle = acos(axis.dot(pos));
00247
00248
00249 if (angle > 0)
00250 {
00251
00252 axis = axis.cross(pos);
00253 axis.normalize();
00254
00255
00256 MatrixP3d atomRotation;
00257 atomRotation.loadRotation3(-angle, axis);
00258
00259
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
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
00291 QComboBox *comboAlign = new QComboBox(m_settingsWidget);
00292 comboAlign->addItem(tr("Everything"));
00293 comboAlign->addItem(tr("Molecule"));
00294
00295
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
00336 m_axis = axis;
00337 }
00338
00339 void AlignTool::alignChanged(int align)
00340 {
00341
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)