/********************************************************************** RingEngine - Engine for "ring" display Copyright (C) 2007 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 "ringengine.h" #include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace Eigen; namespace Avogadro{ const float ringColors[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 } }; RingEngine::RingEngine(QObject *parent) : Engine(parent), m_settingsWidget(0), m_alpha(1.0) { } Engine *RingEngine::clone() const { RingEngine *engine = new RingEngine(parent()); engine->setAlias(alias()); engine->m_alpha = m_alpha; engine->setEnabled(isEnabled()); return engine; } RingEngine::~RingEngine() { } bool RingEngine::renderOpaque(PainterDevice *pd) { if (m_alpha < 0.999) return true; // Special case for everything up to 7 membered rings. QList rings = const_cast(pd->molecule())->rings(); // Now actually draw the ring structures foreach(Fragment *r, rings) renderRing(r->atoms(), pd); return true; } bool RingEngine::renderTransparent(PainterDevice *pd) { if (m_alpha > 0.999) return true; Color *map = colorMap(); map->setAlpha(m_alpha); // Special case for everything up to 7 membered rings. QList rings = const_cast(pd->molecule())->rings(); // Now actually draw the ring structures foreach(Fragment *r, rings) renderRing(r->atoms(), pd); return true; } bool RingEngine::renderRing(const QList &ring, PainterDevice *pd) { // We need to get rid of the constness in order to get the atoms Molecule *mol = const_cast(pd->molecule()); // Calculate an appropriate normal and use it for all the triangles in the // ring - this will give consistent lighting. Eigen::Vector3d v1, v2, norm; v1 = *mol->atomById(ring[1])->pos() - *mol->atomById(ring[0])->pos(); v2 = *mol->atomById(ring[2])->pos() - *mol->atomById(ring[1])->pos(); norm = v1.cross(v2); if (norm.dot(pd->camera()->backTransformedZAxis()) > 0) norm *= -1; // Disable face culling for ring structures. glDisable(GL_CULL_FACE); // Optimise for smaller ring structures switch (ring.size()) { case 3: // Single triangle - easy pd->painter()->setColor(ringColors[0][0], ringColors[0][1], ringColors[0][2]); pd->painter()->drawTriangle(*mol->atomById(ring[0])->pos(), *mol->atomById(ring[1])->pos(), *mol->atomById(ring[2])->pos(), norm); break; case 4: // Two triangles pd->painter()->setColor(ringColors[1][0], ringColors[1][1], ringColors[1][2]); pd->painter()->drawTriangle(*mol->atomById(ring[0])->pos(), *mol->atomById(ring[1])->pos(), *mol->atomById(ring[2])->pos(), norm); pd->painter()->drawTriangle(*mol->atomById(ring[0])->pos(), *mol->atomById(ring[2])->pos(), *mol->atomById(ring[3])->pos(), norm); break; case 5: // Three triangles pd->painter()->setColor(ringColors[2][0], ringColors[2][1], ringColors[2][2]); pd->painter()->drawTriangle(*mol->atomById(ring[0])->pos(), *mol->atomById(ring[1])->pos(), *mol->atomById(ring[2])->pos(), norm); pd->painter()->drawTriangle(*mol->atomById(ring[0])->pos(), *mol->atomById(ring[2])->pos(), *mol->atomById(ring[3])->pos(), norm); pd->painter()->drawTriangle(*mol->atomById(ring[0])->pos(), *mol->atomById(ring[3])->pos(), *mol->atomById(ring[4])->pos(), norm); break; case 6: // Four triangles pd->painter()->setColor(ringColors[3][0], ringColors[3][1], ringColors[3][2]); pd->painter()->drawTriangle(*mol->atomById(ring[0])->pos(), *mol->atomById(ring[1])->pos(), *mol->atomById(ring[2])->pos(), norm); pd->painter()->drawTriangle(*mol->atomById(ring[2])->pos(), *mol->atomById(ring[3])->pos(), *mol->atomById(ring[4])->pos(), norm); pd->painter()->drawTriangle(*mol->atomById(ring[4])->pos(), *mol->atomById(ring[5])->pos(), *mol->atomById(ring[0])->pos(), norm); pd->painter()->drawTriangle(*mol->atomById(ring[0])->pos(), *mol->atomById(ring[2])->pos(), *mol->atomById(ring[4])->pos(), norm); break; default: // The generic case - find the centre of the ring and draw a triangle fan pd->painter()->setColor(ringColors[4][0], ringColors[4][1], ringColors[4][2]); Vector3d center; for (int i = 0; i < ring.size(); i++) center += *mol->atomById(ring[i])->pos(); center /= ring.size(); for (int i = 0; i < ring.size()-1; i++) pd->painter()->drawTriangle(center, *mol->atomById(ring[i])->pos(), *mol->atomById(ring[i+1])->pos(), norm); pd->painter()->drawTriangle(center, *mol->atomById(ring[ring.size()-1])->pos(), *mol->atomById(ring[0])->pos(), norm); } return true; } double RingEngine::radius(const PainterDevice *, const Primitive *) const { return 0.; } double RingEngine::transparencyDepth() const { return 1.0; } Engine::Layers RingEngine::layers() const { return Engine::Opaque | Engine::Transparent; } Engine::PrimitiveTypes RingEngine::primitiveTypes() const { return Engine::Fragments; } Engine::ColorTypes RingEngine::colorTypes() const { return Engine::IndexedColors; } void RingEngine::setOpacity(int value) { m_alpha = 0.05 * value; emit changed(); } QWidget* RingEngine::settingsWidget() { if(!m_settingsWidget) { m_settingsWidget = new RingSettingsWidget(); connect(m_settingsWidget->opacitySlider, SIGNAL(valueChanged(int)), this, SLOT(setOpacity(int))); connect(m_settingsWidget, SIGNAL(destroyed()), this, SLOT(settingsWidgetDestroyed())); m_settingsWidget->opacitySlider->setValue(20*m_alpha); } return m_settingsWidget; } void RingEngine::settingsWidgetDestroyed() { qDebug() << "Destroyed Settings Widget"; m_settingsWidget = 0; } void RingEngine::writeSettings(QSettings &settings) const { Engine::writeSettings(settings); settings.setValue("opacity", 20*m_alpha); } void RingEngine::readSettings(QSettings &settings) { Engine::readSettings(settings); setOpacity(settings.value("opacity", 20).toInt()); if (m_settingsWidget) { m_settingsWidget->opacitySlider->setValue(20*m_alpha); } } } #include "ringengine.moc" Q_EXPORT_PLUGIN2(ringengine, Avogadro::RingEngineFactory)