/********************************************************************** CartoonEngine - Engine for protein structures. Copyright (C) 2009 Tim Vandermeersch Some portions Copyright (C) 2007-2008 by 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 General Public License as published by the Free Software Foundation; either version 2 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 "cartoonengine.h" #include #include #include #include #include #include #include #include #include #include #include #include using Eigen::Vector3d; namespace Avogadro { const float chainColors[6][3] = { { 1.0, 0.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 1.0, 0.0, 1.0 }, { 1.0, 1.0, 0.0 }, { 0.0, 1.0, 1.0 } }; CartoonEngine::CartoonEngine(QObject *parent) : Engine(parent), m_type(0), m_radius(1.0), m_useNitrogens(2) { // Initialise variables m_update = true; } Engine *CartoonEngine::clone() const { CartoonEngine *engine = new CartoonEngine(parent()); engine->setAlias(alias()); engine->m_type = m_type; engine->m_radius = m_radius; engine->setEnabled(isEnabled()); return engine; } CartoonEngine::~CartoonEngine() { } bool CartoonEngine::renderOpaque(PainterDevice *pd) { // Check if the chains need updating before drawing them if (m_update) updateChains(pd); // draw debug points... /* pd->painter()->setColor(0.0, 1.0, 0.0); foreach (const Eigen::Vector3d &p, m_helixPoints) pd->painter()->drawSphere(&p, 0.1); */ pd->painter()->setColor(chainColors[0][0], chainColors[0][1], chainColors[0][2]); int numTriangles = m_normals.size(); glDisable( GL_CULL_FACE ); for (int i = 0; i < numTriangles; ++i) { pd->painter()->drawTriangle(m_triangles.at(i*3), m_triangles.at(i*3+1), m_triangles.at(i*3+2), m_normals.at(i)); } glEnable( GL_CULL_FACE ); pd->painter()->setColor(chainColors[0][0], chainColors[0][1], chainColors[0][2]); for (int i = 0; i < m_helixes3.size(); ++i) pd->painter()->drawCylinder(m_helixes3[i][0], m_helixes3[i][1], 2.3); pd->painter()->setColor(chainColors[1][0], chainColors[1][1], chainColors[1][2]); /* if (m_type == 0) { for (int i = 0; i < m_chains.size(); i++) { if (m_chains[i].size() <= 1) continue; pd->painter()->setColor(chainColors[i % 6][0], chainColors[i % 6][1], chainColors[i % 6][2]); pd->painter()->drawSpline(m_chains[i], m_radius); } } else { // Render cylinders between the points and spheres at each point for (int i = 0; i < m_chains.size(); i++) { if (m_chains[i].size() <= 1) continue; pd->painter()->setColor(chainColors[i % 6][0], chainColors[i % 6][1], chainColors[i % 6][2]); pd->painter()->drawSphere(&m_chains[i][0], m_radius); for (int j = 1; j < m_chains[i].size(); j++) { pd->painter()->drawSphere(&m_chains[i][j], m_radius); pd->painter()->drawCylinder(m_chains[i][j-1], m_chains[i][j], m_radius); } } } */ return true; } bool CartoonEngine::renderQuick(PainterDevice *pd) { pd->painter()->setColor(chainColors[0][0], chainColors[0][1], chainColors[0][2]); int numTriangles = m_normals.size(); glDisable( GL_CULL_FACE ); for (int i = 0; i < numTriangles; ++i) { pd->painter()->drawTriangle(m_triangles.at(i*3), m_triangles.at(i*3+1), m_triangles.at(i*3+2), m_normals.at(i)); } glEnable( GL_CULL_FACE ); pd->painter()->setColor(chainColors[0][0], chainColors[0][1], chainColors[0][2]); for (int i = 0; i < m_helixes3.size(); ++i) pd->painter()->drawCylinder(m_helixes3[i][0], m_helixes3[i][1], 2.3); pd->painter()->setColor(chainColors[1][0], chainColors[1][1], chainColors[1][2]); // Just render cylinders between the backbone... double tRadius = m_radius / 2.0; for (int i = 0; i < m_chains.size(); i++) { if (m_chains[i].size() <= 1) continue; pd->painter()->setColor(chainColors[i % 6][0], chainColors[i % 6][1], chainColors[i % 6][2]); pd->painter()->drawSphere(&m_chains[i][0], tRadius); for (int j = 1; j < m_chains[i].size(); j++) { pd->painter()->drawSphere(&m_chains[i][j], tRadius); pd->painter()->drawCylinder(m_chains[i][j-1], m_chains[i][j], tRadius); } } return true; } double CartoonEngine::radius(const PainterDevice *, const Primitive *) const { return m_radius; } void CartoonEngine::setPrimitives(const PrimitiveList &primitives) { Engine::setPrimitives(primitives); m_update = true; } void CartoonEngine::addPrimitive(Primitive *primitive) { Engine::addPrimitive(primitive); m_update = true; } void CartoonEngine::updatePrimitive(Primitive *) { m_update = true; } void CartoonEngine::removePrimitive(Primitive *primitive) { Engine::removePrimitive(primitive); m_update = true; } void CartoonEngine::updateChains(PainterDevice *pd) { if (!isEnabled()) return; // Get a list of residues for the molecule const Molecule *molecule = pd->molecule(); Protein protein((Molecule*)molecule); m_triangles.clear(); m_normals.clear(); // 4-turn helixes for (int i = 0; i < protein.num4turnHelixes(); ++i) { QList helixPoints; QList helixAxis; QList helixNormals; // all N, CA, C, O atoms in that order QList helix = protein.helix4BackboneAtoms(i); int numResidues = helix.size() / 4; // compute centers... // compute the helix centers QList helixCenters; for (int i = 0; i < helix.size() - 15; i+= 16) { Eigen::Vector3d p1 = Eigen::Vector3d::Zero(); for (int j = 0; j < 16; ++j) p1 += *(molecule->atomById(helix.at(i+j))->pos()); p1 /= 16.0; helixCenters.append(p1); } if (numResidues % 4 != 0) { Eigen::Vector3d p2 = Eigen::Vector3d::Zero(); for (int i = helix.size() - 16; i < helix.size(); ++i) p2 += *(molecule->atomById(helix.at(i))->pos()); p2 /= 16.0; helixCenters.append(p2); } for (int i = 0; i < numResidues - 1; ++i) { // the axis points Eigen::Vector3d P1 = helixCenters.at(0); Eigen::Vector3d P2 = helixCenters.at(helixCenters.size()-1); // the central axis Eigen::Vector3d axis = P2 - P1; axis.normalize(); // first nitrogen position Eigen::Vector3d posN1 = *(molecule->atomById(helix.at(i*4))->pos()); // find point on line, closest to N Eigen::Vector3d p1 = P1 + (posN1 - P1).dot(axis) * axis; // second nitrogen position Eigen::Vector3d posN2 = *(molecule->atomById(helix.at(i*4+4))->pos()); // find point on line, closest to N Eigen::Vector3d p2 = P1 + (posN2 - P1).dot(axis) * axis; double deltaHeight = (p1 - p2).norm(); // shortest lines from axis to nitrogens Eigen::Vector3d r1 = posN1 - p1; Eigen::Vector3d r2 = posN2 - p2; double deltaRadius = r2.norm() - r1.norm(); double angle = acos( r1.dot(r2) / (r1.norm() * r2.norm()) ); helixPoints.append(posN1); helixNormals.append(r1); helixAxis.append(axis); double incHeight = deltaHeight / 4.0; double incRadius = deltaRadius / 4.0; double incAngle = angle / 4.0; Eigen::Transform3d m; m = Eigen::AngleAxisd(incAngle, axis); for (int j = 1; j < 4; ++j) { Eigen::Vector3d lastPoint = helixPoints.last(); Eigen::Vector3d onLine = P1 + (lastPoint - P1).dot(axis) * axis; // correct for deltaRadius lastPoint -= (onLine - lastPoint).normalized() * incRadius; Eigen::Vector3d newPoint = m * (lastPoint - onLine) + onLine; newPoint += axis * incHeight; helixPoints.append(newPoint); helixNormals.append(newPoint - onLine); helixAxis.append(axis); } } for (int i = 0; i < helixNormals.size() - 1; ++i) { Eigen::Vector3d axis(helixAxis.at(i)); m_triangles.append(helixPoints.at(i) + axis); m_triangles.append(helixPoints.at(i+1) - axis); m_triangles.append(helixPoints.at(i) - axis); m_triangles.append(helixPoints.at(i) + axis); m_triangles.append(helixPoints.at(i+1) + axis); m_triangles.append(helixPoints.at(i+1) - axis); m_normals.append(helixNormals.at(i)); m_normals.append(helixNormals.at(i)); } } // 3-turn helixes for (int i = 0; i < protein.num3turnHelixes(); ++i) { QList helix = protein.helix3BackboneAtoms(i); Eigen::Vector3d p1 = Eigen::Vector3d::Zero(); for (int i = 0; i < 12; ++i) p1 += *(molecule->atomById(helix.at(i))->pos()); p1 /= 12.0; Eigen::Vector3d p2 = Eigen::Vector3d::Zero(); for (int i = helix.size() - 12; i < helix.size(); ++i) p2 += *(molecule->atomById(helix.at(i))->pos()); p2 /= 12.0; Eigen::Vector3d ab = p1 - p2; ab.normalize(); ab *= 3.0; p1 += ab; p2 -= ab; QVector helixPoints; helixPoints.append(p1); helixPoints.append(p2); m_helixes3.append(helixPoints); } m_chains.clear(); QList list; list = primitives().subList(Primitive::ResidueType); unsigned int currentChain = 0; QVector pts; foreach(Primitive *p, list) { Residue *r = static_cast(p); if(r->name() =="HOH") { continue; } if(r->chainNumber() != currentChain) { // this residue is on a new chain if(pts.size() > 0) m_chains.push_back(pts); currentChain = r->chainNumber(); pts.clear(); } foreach (unsigned long atom, r->atoms()) { // should be CA QString atomId = r->atomId(atom); atomId = atomId.trimmed(); if (atomId == "CA") { pts.push_back(*molecule->atomById(atom)->pos()); } else if (atomId == "N" && m_useNitrogens == 2) { pts.push_back(*molecule->atomById(atom)->pos()); } } // end atoms in residue } // end primitive list (i.e., all residues) m_chains.push_back(pts); // Add the last chain (possibly the only chain) m_update = false; } Engine::PrimitiveTypes CartoonEngine::primitiveTypes() const { return Engine::Atoms; } Engine::ColorTypes CartoonEngine::colorTypes() const { return Engine::IndexedColors; } void CartoonEngine::writeSettings(QSettings &settings) const { Engine::writeSettings(settings); } void CartoonEngine::readSettings(QSettings &settings) { Engine::readSettings(settings); } } #include "cartoonengine.moc" Q_EXPORT_PLUGIN2(cartoonengine, Avogadro::CartoonEngineFactory)