/**********************************************************************
WireEngine - Engine for wireframe display
Copyright (C) 2006-2007 Geoffrey R. Hutchison
Copyright (C) 2006-2007 Benoit Jacob
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 "wireengine.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace Eigen;
namespace Avogadro {
WireEngine::WireEngine(QObject *parent) : Engine(parent), m_settingsWidget(NULL),
m_showMulti(0), m_showDots(2)
{
}
Engine* WireEngine::clone() const
{
WireEngine* engine = new WireEngine(parent());
engine->setAlias(alias());
engine->setShowDots(m_showDots);
engine->setShowMultipleBonds(m_showMulti);
engine->setEnabled(isEnabled());
return engine;
}
bool WireEngine::renderOpaque(PainterDevice *pd)
{
glDisable(GL_LIGHTING);
// Skip this entire step if the user turns it off
if (m_showDots) {
foreach(Atom *a, atoms())
renderOpaque(pd, a);
}
foreach(Bond *b, bonds())
renderOpaque(pd, b);
glEnable(GL_LIGHTING);
return true;
}
bool WireEngine::renderOpaque(PainterDevice *pd, const Atom *a)
{
const Vector3d & v = *a->pos();
const Camera *camera = pd->camera();
// perform a rough form of frustum culling
Eigen::Vector3d transformedPos = pd->camera()->modelview() * v;
double dot = transformedPos.z() / transformedPos.norm();
if(dot > -0.8) return true;
Color *map = colorMap(); // possible custom color map
if (!map) map = pd->colorMap(); // fall back to global color map
glPushName(Primitive::AtomType);
glPushName(a->index());
// Compute a rough "dynamic" size for the atom dots
// We could probably have a better gradient here, but it looks decent
double size = 3.0; // default size;
if (camera->distance(v) < 5.0)
size = 7.0;
else if (camera->distance(v) > 5.0 && camera->distance(v) < 10.0)
size = 4.0;
else if (camera->distance(v) > 30.0 && camera->distance(v) < 60.0)
size = 2.0;
else if (camera->distance(v) > 60.0 && camera->distance(v) < 85.0)
size = 1.5;
else if (camera->distance(v) > 85.0)
size = 1.0;
// All dots are scaled by the VDW radius -- subtle, but effective
if (pd->isSelected(a)) {
map->setToSelectionColor();
map->apply();
glPointSize(OpenBabel::etab.GetVdwRad(a->atomicNumber()) * (size + 1.0));
}
else {
map->set(a);
map->apply();
glPointSize(OpenBabel::etab.GetVdwRad(a->atomicNumber()) * size);
}
glBegin(GL_POINTS);
glVertex3d(v.x(), v.y(), v.z());
glEnd();
glPopName(); // atom index
glPopName(); // Primitive::AtomType
return true;
}
inline double WireEngine::radius (const Atom *atom) const
{
return OpenBabel::etab.GetVdwRad(atom->atomicNumber());
}
bool WireEngine::renderOpaque(PainterDevice *pd, const Bond *b)
{
const Atom* atom1 = pd->molecule()->atomById(b->beginAtomId());
const Vector3d & v1 = *atom1->pos();
const Camera *camera = pd->camera();
Color *map = colorMap(); // possible custom color map
if (!map) map = pd->colorMap(); // fall back to global color map
// perform a rough form of frustum culling
Eigen::Vector3d transformedEnd1 = pd->camera()->modelview() * v1;
double dot = transformedEnd1.z() / transformedEnd1.norm();
if(dot > -0.8) return true; // i.e., don't bother rendering
const Atom* atom2 = pd->molecule()->atomById(b->endAtomId());
const Vector3d & v2 = *atom2->pos();
Vector3d d = v2 - v1;
d.normalize();
Vector3d v3;
if (atom1->atomicNumber() != atom2->atomicNumber()) {
// compute the "transition point" between the two atoms
v3 = ( v1 + v2 + d*( radius( atom1 )-radius( atom2 ) ) ) / 2.0;
}
// Compute the width to draw the wireframe bonds
double width = 1.0;
double averageDistance = (camera->distance(v1) + camera->distance(v2)) / 2.0;
if (averageDistance < 20.0 && averageDistance > 10.0)
width = 1.5;
else if (averageDistance < 10.0 && averageDistance > 5.0)
width = 2.0;
else if (averageDistance < 5.0)
width = 2.5;
// Default to single bond, no stipple
short stipple = static_cast(0xFFFF);
int order = 1;
if (m_showMulti) {
order = b->order();
if (order > 1)
width *= order * 0.75; // make multiple bonds a litte thicker too
// For aromatic (dashed bonds)
if (b->isAromatic()) {
order = -1;
stipple = static_cast(0xCCCC);
}
}
map->set(atom1);
pd->painter()->setColor(map);
// if have two of the same atoms, just draw one line
if (atom1->atomicNumber() == atom2->atomicNumber()) {
if (order != 1) pd->painter()->drawMultiLine(v1, v2, width, order, stipple);
else pd->painter()->drawLine(v1, v2, width);
return true;
}
// otherwise, we draw a line to the "transition point", change color, etc.
if (order != 1) pd->painter()->drawMultiLine(v1, v3, width, order, stipple);
else pd->painter()->drawLine(v1, v3, width);
map->set(atom2);
pd->painter()->setColor(map);
if (order != 1) pd->painter()->drawMultiLine(v3, v2, width, order, stipple);
else pd->painter()->drawLine(v3, v2, width);
return true;
}
void WireEngine::setShowMultipleBonds(int setting)
{
m_showMulti = setting;
emit changed();
}
void WireEngine::setShowDots(int setting)
{
m_showDots = setting;
emit changed();
}
QWidget* WireEngine::settingsWidget()
{
if(!m_settingsWidget)
{
m_settingsWidget = new WireSettingsWidget();
connect(m_settingsWidget->showMultipleCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setShowMultipleBonds(int)));
connect(m_settingsWidget->showDotsCheckBox, SIGNAL(stateChanged(int)), this, SLOT(setShowDots(int)));
connect(m_settingsWidget, SIGNAL(destroyed()), this, SLOT(settingsWidgetDestroyed()));
m_settingsWidget->showDotsCheckBox->setCheckState((Qt::CheckState)m_showDots);
m_settingsWidget->showMultipleCheckBox->setCheckState((Qt::CheckState)m_showMulti);
}
return m_settingsWidget;
}
void WireEngine::settingsWidgetDestroyed()
{
qDebug() << "Destroyed Settings Widget";
m_settingsWidget = 0;
}
void WireEngine::writeSettings(QSettings &settings) const
{
Engine::writeSettings(settings);
settings.setValue("showDots", m_showDots);
settings.setValue("showMulti", m_showMulti);
}
void WireEngine::readSettings(QSettings &settings)
{
Engine::readSettings(settings);
setShowDots(settings.value("showDots", 2).toInt());
setShowMultipleBonds(settings.value("showMulti", 1).toInt());
if (m_settingsWidget) {
m_settingsWidget->showDotsCheckBox->setCheckState((Qt::CheckState)m_showDots);
m_settingsWidget->showMultipleCheckBox->setCheckState((Qt::CheckState)m_showMulti);
}
}
}
#include "wireengine.moc"
Q_EXPORT_PLUGIN2(wireengine, Avogadro::WireEngineFactory)