/**********************************************************************
ZMatrix - Class to store a z matrix
Copyright (C) 2009 Marcus D. Hanwell
This file is part of the Avogadro molecular editor project.
For more information, see
Avogadro is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
Avogadro 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
**********************************************************************/
#include "zmatrix.h"
#include
#include
#include
#include
#include
#include
#include
namespace Avogadro{
using Eigen::Vector3d;
ZMatrix::ZMatrix(QObject *parent) : QObject(parent)
{
m_molecule = qobject_cast(parent);
}
ZMatrix::~ZMatrix()
{
}
void ZMatrix::addRow(int row)
{
qDebug() << "Adding new row" << row << m_items.size();
if (row == -1) {
m_items.push_back(zItem());
m_items.last().atomIndex = m_molecule->addAtom()->id();
emit rowAdded(m_items.size()-1);
}
else {
m_items.insert(row, zItem());
m_items[row].atomIndex = m_molecule->addAtom()->id();
emit rowAdded(row);
}
// Set some reasonable defaults for new rows
int size = m_items.size();
if (size > 1) {
Bond *b = m_molecule->addBond();
switch (size) {
case 2: // Always connected to the first atom - no choice
m_items[1].indices[0] = 0;
m_items[1].lengths[0] = 1.0;
b->setAtoms(m_items[1].atomIndex, m_items[0].atomIndex);
break;
case 3:
m_items[2].indices[0] = 0;
m_items[2].lengths[0] = 1.0;
m_items[2].indices[1] = 1;
m_items[2].lengths[1] = 100.0;
b->setAtoms(m_items[2].atomIndex, m_items[0].atomIndex);
break;
default: // Provide a default - user can change
m_items[size-1].indices[0] = size - 4;
m_items[size-1].lengths[0] = 1.0;
m_items[size-1].indices[1] = size - 3;
m_items[size-1].lengths[1] = 100;
m_items[size-1].indices[2] = size - 2;
m_items[size-1].lengths[2] = 120;
b->setAtoms(m_items[size-1].atomIndex, m_items[size-4].atomIndex);
}
}
}
void ZMatrix::setBond(int atom1, int atom2)
{
Bond *b = m_molecule->bond(m_items[atom1].atomIndex,
m_items[m_items[atom1].indices[0]].atomIndex);
b->setAtoms(m_items[atom1].atomIndex, m_items[atom2].atomIndex);
m_items[atom1].indices[0] = atom2;
}
void ZMatrix::update()
{
// Start at the origin and calculate the coords relative to that
for(int i = 0; i < m_items.size(); ++i) {
Atom *a = m_molecule->atomById(m_items[i].atomIndex);
a->setAtomicNumber(m_items[i].atomicNumber);
if (i == 0) // First atom - origin
a->setPos(Vector3d::Zero());
else if (i == 1) // Second atom - just length
a->setPos(Vector3d(m_items[i].lengths[0], 0.0, 0.0));
else if (i == 2) { // Third atom - length and angle
double length = m_items[i].lengths[0];
double angle = m_items[i].lengths[1] * cDegToRad;
double x = length * cos(angle);
double y = length * sin(angle);
a->setPos(Vector3d(x, y, 0.0));
}
else { // The general case where all three values are set
double length = m_items[i].lengths[0];
double angle = m_items[i].lengths[1] * cDegToRad;
double dihedral = m_items[i].lengths[2] * cDegToRad;
Atom *a1 = m_molecule->atomById(m_items[m_items[i].indices[0]].atomIndex);
Atom *a2 = m_molecule->atomById(m_items[m_items[i].indices[1]].atomIndex);
Atom *a3 = m_molecule->atomById(m_items[m_items[i].indices[2]].atomIndex);
// Dihedral angle - perform rotation
Vector3d v1(*a1->pos() - *a2->pos());
Vector3d v2(*a1->pos() - *a3->pos());
if (v1.norm() < 0.01 || v2.norm() < 0.01) {// Undefined
a->setPos(a1->pos());
continue;
}
// Find the two orthogonal axes for the dihedral angle rotation
Vector3d axis1 = v1.cross(v2).normalized();
Vector3d axis2 = v1.cross(axis1).normalized();
axis1 *= -sin(dihedral);
axis2 *= cos(dihedral);
// Now rotate about the bond angle
Vector3d v3 = (axis1 + axis2).normalized();
v3 *= length * sin(angle);
v1.normalize();
v1 *= length * cos(angle);
v2 = *a1->pos() + v3 - v1;
// Now we have the position of the new atom
a->setPos(v2);
}
}
}
} // End namespace Avogadro
#include "zmatrix.moc"