/**********************************************************************
OrbitalEngine - Engine for display of molecular orbitals
Copyright (C) 2008-2009 Marcus D. Hanwell
Copyright (C) 2008 Geoffrey R. Hutchison
Copyright (C) 2008 Tim Vandermeersch
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 "orbitalengine.h"
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace OpenBabel;
using namespace Eigen;
namespace Avogadro {
OrbitalEngine::OrbitalEngine(QObject *parent) : Engine(parent),
m_settingsWidget(0), m_mesh1(0), m_mesh2(0), m_min(0., 0., 0.), m_max(0.,0.,0.),
m_alpha(0.75), m_renderMode(0), m_drawBox(false), m_update(true), m_colored(false)
{
m_negColor.set(1.0, 0.0, 0.0, m_alpha);
m_posColor.set(0.0, 0.0, 1.0, m_alpha);
}
OrbitalEngine::~OrbitalEngine()
{
}
Engine *OrbitalEngine::clone() const
{
OrbitalEngine *engine = new OrbitalEngine(parent());
engine->setAlias(alias());
engine->setEnabled(isEnabled());
return engine;
}
bool OrbitalEngine::renderOpaque(PainterDevice *pd)
{
// Render the opaque surface if m_alpha is 1
if (m_alpha >= 0.999)
{
if (m_update)
updateSurfaces(pd);
if (m_mesh1) {
if (m_mesh1->stable()) {
if (m_colored)
pd->painter()->drawColorMesh(*m_mesh1, m_renderMode);
else {
pd->painter()->setColor(&m_posColor);
pd->painter()->drawMesh(*m_mesh1, m_renderMode);
}
}
}
if (m_mesh2) {
if (m_mesh2->stable()) {
if (m_colored)
pd->painter()->drawColorMesh(*m_mesh2, m_renderMode);
else {
pd->painter()->setColor(&m_negColor);
pd->painter()->drawMesh(*m_mesh2, m_renderMode);
}
}
}
renderSurfaces(pd);
}
return true;
}
bool OrbitalEngine::renderTransparent(PainterDevice *pd)
{
// Render the transparent surface if m_alpha is between 0 and 1.
if (m_alpha > 0.001 && m_alpha < 0.999)
{
if (m_update)
updateSurfaces(pd);
if (m_mesh1) {
if (m_mesh1->stable()) {
if (m_colored) {
pd->painter()->setColor(&m_posColor); // For transparency
pd->painter()->drawColorMesh(*m_mesh1, m_renderMode);
}
else {
pd->painter()->setColor(&m_posColor);
pd->painter()->drawMesh(*m_mesh1, m_renderMode);
}
}
}
if (m_mesh2) {
if (m_mesh2->stable()) {
if (m_colored) {
pd->painter()->setColor(&m_negColor); // For transparency
pd->painter()->drawColorMesh(*m_mesh2, m_renderMode);
}
else {
pd->painter()->setColor(&m_negColor);
pd->painter()->drawMesh(*m_mesh2, m_renderMode);
}
}
}
renderSurfaces(pd);
}
return true;
}
bool OrbitalEngine::renderQuick(PainterDevice *pd)
{
if (m_update)
updateSurfaces(pd);
int renderMode = 1;
if (m_renderMode == 2)
renderMode = 2;
if (m_mesh1) {
if (m_mesh1->stable()) {
pd->painter()->setColor(&m_posColor);
pd->painter()->drawMesh(*m_mesh1, renderMode);
}
}
if (m_mesh2) {
if (m_mesh2->stable()) {
pd->painter()->setColor(&m_negColor);
pd->painter()->drawMesh(*m_mesh2, renderMode);
}
}
renderSurfaces(pd);
return true;
}
bool OrbitalEngine::renderSurfaces(PainterDevice *pd)
{
// Draw the extents of the cube if requested to
if (m_drawBox) {
pd->painter()->setColor(1.0, 1.0, 1.0);
pd->painter()->drawLine(Vector3d(m_min.x(), m_min.y(), m_min.z()),
Vector3d(m_max.x(), m_min.y(), m_min.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_min.x(), m_min.y(), m_min.z()),
Vector3d(m_max.x(), m_min.y(), m_min.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_min.x(), m_min.y(), m_min.z()),
Vector3d(m_min.x(), m_max.y(), m_min.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_min.x(), m_min.y(), m_min.z()),
Vector3d(m_min.x(), m_min.y(), m_max.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_max.x(), m_min.y(), m_min.z()),
Vector3d(m_max.x(), m_max.y(), m_min.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_max.x(), m_min.y(), m_min.z()),
Vector3d(m_max.x(), m_min.y(), m_max.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_min.x(), m_max.y(), m_min.z()),
Vector3d(m_max.x(), m_max.y(), m_min.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_min.x(), m_max.y(), m_min.z()),
Vector3d(m_min.x(), m_max.y(), m_max.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_min.x(), m_min.y(), m_max.z()),
Vector3d(m_min.x(), m_max.y(), m_max.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_min.x(), m_min.y(), m_max.z()),
Vector3d(m_max.x(), m_min.y(), m_max.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_max.x(), m_max.y(), m_max.z()),
Vector3d(m_max.x(), m_max.y(), m_min.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_max.x(), m_max.y(), m_max.z()),
Vector3d(m_max.x(), m_min.y(), m_max.z()), 1.0);
pd->painter()->drawLine(Vector3d(m_max.x(), m_max.y(), m_max.z()),
Vector3d(m_min.x(), m_max.y(), m_max.z()), 1.0);
}
return true;
}
void OrbitalEngine::updateSurfaces(PainterDevice *pd)
{
// Attempt to find the correct meshes
m_molecule = pd->molecule();
int iMesh1 = 0;
int iMesh2 = 1;
if (m_mesh1)
iMesh1 = m_mesh1->index();
if (m_mesh2)
iMesh2 = m_mesh2->index();
QList meshes;
foreach(Mesh *mesh, pd->molecule()->meshes()) {
if (!mesh->lock()->tryLockForRead())
continue;
if (mesh->isoValue() > 0.0)
meshes.push_back(mesh);
mesh->lock()->unlock();
}
if (meshes.empty())
return;
if (m_settingsWidget) {
if (!m_settingsWidget->orbital1Combo->count()) {
updateOrbitalCombo();
}
else {
// Valid settings widget with populated orbital combos
iMesh1 = m_settingsWidget->orbital1Combo->currentIndex();
if (iMesh1 >= meshes.size()) {
qDebug() << "Invalid orbital selected.";
return;
}
}
}
// attribute is the text key for the Mesh
m_mesh1 = meshes[iMesh1];
m_mesh2 = m_molecule->meshById(m_mesh1->otherMesh());
// Check whether mesh has multiple colors
bool colorMesh = m_mesh1->colors().size() == m_mesh1->vertices().size();
if (m_settingsWidget) {
m_settingsWidget->colorCombo->setEnabled(colorMesh);
m_settingsWidget->colorCombo->setCurrentIndex(m_colored ? 1 : 0);
}
if (m_colored && !colorMesh)
m_colored = false;
qDebug() << " Orbital 1 title: " << m_mesh1->name();
qDebug() << " Orbital 2 title: " << m_mesh2->name();
// Get the cube extents
Cube *cube = m_molecule->cubeById(m_mesh1->cube());
m_min = cube->min();
m_max = cube->max();
m_update = false;
}
void OrbitalEngine::updateOrbitalCombo()
{
if (!m_settingsWidget || !m_molecule)
return;
// Reset the orbital combo
qDebug() << "Update orbital combo called...";
int tmp1 = m_settingsWidget->orbital1Combo->currentIndex();
if (tmp1 < 0) tmp1 = 0;
m_settingsWidget->orbital1Combo->clear();
QList meshList = m_molecule->meshes();
if (meshList.empty())
return;
foreach(Mesh *mesh, meshList) {
if (!mesh->lock()->tryLockForRead()) {
qDebug() << "Cannot get a read lock on the mesh...";
continue;
}
if (mesh->isoValue() > 0.0) {
if (m_mesh1)
if (m_mesh1->id() == mesh->id())
tmp1 = m_settingsWidget->orbital1Combo->count();
m_settingsWidget->orbital1Combo->addItem(mesh->name() + ", isosurface = "
+ QString::number(mesh->isoValue()));
}
mesh->lock()->unlock();
}
m_settingsWidget->orbital1Combo->setCurrentIndex(tmp1);
}
double OrbitalEngine::transparencyDepth() const
{
return 1.0;
}
Engine::Layers OrbitalEngine::layers() const
{
return Engine::Transparent;
}
Engine::PrimitiveTypes OrbitalEngine::primitiveTypes() const
{
return Engine::Surfaces; // i.e., don't display the "primitives tab"
}
Engine::ColorTypes OrbitalEngine::colorTypes() const
{
return Engine::IndexedColors;
}
void OrbitalEngine::setOrbital(int)
{
m_update = true;
emit changed();
}
void OrbitalEngine::setOpacity(int value)
{
m_alpha = 0.05 * value;
m_posColor.setAlpha(m_alpha);
m_negColor.setAlpha(m_alpha);
emit changed();
}
void OrbitalEngine::setRenderMode(int value)
{
m_renderMode = value;
emit changed();
}
void OrbitalEngine::setDrawBox(int value)
{
if (value == 0) m_drawBox = false;
else m_drawBox = true;
emit changed();
}
void OrbitalEngine::setColorMode(int value)
{
m_colored = static_cast(value);
emit changed();
}
void OrbitalEngine::setPosColor(const QColor& color)
{
m_posColor.set(color.redF(), color.greenF(), color.blueF(), m_alpha);
emit changed();
}
void OrbitalEngine::setNegColor(const QColor& color)
{
m_negColor.set(color.redF(), color.greenF(), color.blueF(), m_alpha);
emit changed();
}
QWidget* OrbitalEngine::settingsWidget()
{
if(!m_settingsWidget)
{
m_settingsWidget = new OrbitalSettingsWidget(qobject_cast(parent()));
connect(m_settingsWidget->orbital1Combo, SIGNAL(currentIndexChanged(int)),
this, SLOT(setOrbital(int)));
connect(m_settingsWidget->opacitySlider, SIGNAL(valueChanged(int)),
this, SLOT(setOpacity(int)));
connect(m_settingsWidget->renderCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(setRenderMode(int)));
connect(m_settingsWidget->drawBoxCheck, SIGNAL(stateChanged(int)),
this, SLOT(setDrawBox(int)));
connect(m_settingsWidget->colorCombo, SIGNAL(currentIndexChanged(int)),
this, SLOT(setColorMode(int)));
connect(m_settingsWidget->posColor, SIGNAL(colorChanged(QColor)),
this, SLOT(setPosColor(QColor)));
connect(m_settingsWidget->negColor, SIGNAL(colorChanged(QColor)),
this, SLOT(setNegColor(QColor)));
connect(m_settingsWidget, SIGNAL(destroyed()),
this, SLOT(settingsWidgetDestroyed()));
// Initialise the widget from saved settings
m_settingsWidget->opacitySlider->setValue(static_cast(m_alpha * 20));
m_settingsWidget->renderCombo->setCurrentIndex(m_renderMode);
m_settingsWidget->drawBoxCheck->setChecked(m_drawBox);
m_settingsWidget->colorCombo->setCurrentIndex(m_colored ? 1 : 0);
// Initialise the colour buttons
QColor initial;
initial.setRgbF(m_posColor.red(), m_posColor.green(), m_posColor.blue());
m_settingsWidget->posColor->setColor(initial);
initial.setRgbF(m_negColor.red(), m_negColor.green(), m_negColor.blue());
m_settingsWidget->negColor->setColor(initial);
updateOrbitalCombo();
// Connect the molecule updated signal
if (m_molecule)
connect(m_molecule, SIGNAL(updated()), this, SLOT(updateOrbitalCombo()));
}
return m_settingsWidget;
}
void OrbitalEngine::settingsWidgetDestroyed()
{
qDebug() << "Destroyed Settings Widget";
m_settingsWidget = 0;
}
void OrbitalEngine::setPrimitives(const PrimitiveList &primitives)
{
Engine::setPrimitives(primitives);
// This is used to load new molecules and so there could be a new cube file
m_update = true;
}
void OrbitalEngine::addPrimitive(Primitive *primitive)
{
Engine::addPrimitive(primitive);
// Updating primitives does not invalidate these surfaces...
if (primitive->type() == Primitive::MeshType)
m_update = true;
}
void OrbitalEngine::updatePrimitive(Primitive *primitive)
{
// Updating primitives does not invalidate these surfaces...
if (primitive->type() == Primitive::MeshType)
m_update = true;
}
void OrbitalEngine::removePrimitive(Primitive *primitive)
{
Engine::removePrimitive(primitive);
if (primitive->type() == Primitive::MeshType)
m_update = true;
}
void OrbitalEngine::setMolecule(const Molecule *molecule)
{
disconnect(m_molecule, 0, this, 0);
Engine::setMolecule(molecule);
connect(m_molecule, SIGNAL(updated()), this, SLOT(updateOrbitalCombo()));
}
void OrbitalEngine::writeSettings(QSettings &settings) const
{
Engine::writeSettings(settings);
settings.setValue("alpha", m_alpha);
settings.setValue("renderMode", m_renderMode);
settings.setValue("drawBox", m_drawBox);
settings.setValue("colorMode", m_colored);
if (m_mesh1)
settings.setValue("mesh1Id", static_cast(m_mesh1->id()));
if (m_mesh2)
settings.setValue("mesh2Id", static_cast(m_mesh2->id()));
// settings.setValue("posColor", m_posColor);
// settings.setValue("posColor", m_negColor);
}
void OrbitalEngine::readSettings(QSettings &settings)
{
Engine::readSettings(settings);
m_alpha = settings.value("alpha", 0.5).toDouble();
m_posColor.setAlpha(m_alpha);
m_negColor.setAlpha(m_alpha);
m_renderMode = settings.value("renderMode", 0).toInt();
m_colored = settings.value("colorMode", false).toBool();
m_drawBox = settings.value("drawBox", false).toBool();
if (m_molecule) {
m_mesh1 = m_molecule->meshById(settings.value("mesh1Id", 0).toInt());
m_mesh2 = m_molecule->meshById(settings.value("mesh2Id", 0).toInt());
}
}
}
#include "orbitalengine.moc"
Q_EXPORT_PLUGIN2(orbitalengine, Avogadro::OrbitalEngineFactory)