// -*- C++ -*-
/*
* Gnome Chemistry Utils
* gcu/chem3ddoc.cc
*
* Copyright (C) 2006-2007 Jean Bréfort <jean.brefort@normalesup.org>
*
* This program 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.
*
* This program 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 St, Fifth Floor, Boston, MA 02110-1301
* USA
*/
#include "config.h"
#include "chem3ddoc.h"
#include "application.h"
#include "glview.h"
#include <gcu/chemistry.h>
#include <gcu/element.h>
#include <libgnomevfs/gnome-vfs.h>
#include <sstream>
#include <libintl.h>
#include <locale.h>
#include <openbabel/obconversion.h>
#include <GL/gl.h>
#include <GL/glu.h>
#ifdef HAVE_FSTREAM
# include <fstream>
#else
# include <fstream.h>
#endif
#ifdef HAVE_OSTREAM
# include <ostream>
#else
# include <ostream.h>
#endif
#ifdef HAVE_SSTREAM
# include <sstream>
#else
# include <sstream.h>
#endif
#include <cstring>
using namespace gcu;
Chem3dDoc::Chem3dDoc (): GLDocument (NULL)
{
m_View = new GLView (this);
m_Display3D = BALL_AND_STICK;
}
Chem3dDoc::Chem3dDoc (Application *App, GLView *View): GLDocument (App)
{
m_View = (View)? View: new GLView (this);
m_Display3D = BALL_AND_STICK;
}
Chem3dDoc::~Chem3dDoc ()
{
}
void Chem3dDoc::Draw ()
{
std::vector < OBNodeBase * >::iterator i;
OBAtom* atom = m_Mol.BeginAtom (i);
guint Z;
gdouble R, w, x, y, z, x0, y0, z0, dist;
x0 = y0 = z0 = 0.0;
const gdouble* color;
while (atom) {
Z = atom->GetAtomicNum ();
x0 += atom->GetX ();
y0 += atom->GetY ();
z0 += atom->GetZ ();
atom = m_Mol.NextAtom (i);
}
x0 /= m_Mol.NumAtoms ();
y0 /= m_Mol.NumAtoms ();
z0 /= m_Mol.NumAtoms ();
atom = m_Mol.BeginAtom (i);
GLUquadricObj *quadObj ;
dist = 0;
while (atom) {
Z = atom->GetAtomicNum ();
if (Z > 0) {
R = etab.GetVdwRad (Z);
if (m_Display3D == BALL_AND_STICK)
R *= 0.2;
x = atom->GetX () - x0;
y = atom->GetY () - y0;
z = atom->GetZ () - z0;
color = gcu_element_get_default_color (Z);
if ((w = sqrt (x * x + y * y + z * z)) > dist - R)
dist = w + R;
glPushMatrix () ;
glTranslated (x, y, z) ;
glColor3d (color[0], color[1], color[2]) ;
quadObj = gluNewQuadric () ;
gluQuadricDrawStyle (quadObj, GL_FILL);
gluQuadricNormals (quadObj, GL_SMOOTH) ;
gluSphere (quadObj, R, 20, 10) ;
gluDeleteQuadric (quadObj) ;
glPopMatrix () ;
}
atom = m_Mol.NextAtom (i);
}
m_MaxDist = dist * 1.05;
std::vector < OBEdgeBase * >::iterator j;
OBBond* bond = m_Mol.BeginBond (j);
double x1, y1, z1, arot, xrot, yrot;
if (m_Display3D == BALL_AND_STICK)
while (bond) {
atom = bond->GetBeginAtom ();
if (atom->GetAtomicNum () == 0) {
bond = m_Mol.NextBond (j);
continue;
}
x = atom->GetX () - x0;
y = atom->GetY () - y0;
z = atom->GetZ () - z0;
atom = bond->GetEndAtom ();
if (atom->GetAtomicNum () == 0) {
bond = m_Mol.NextBond (j);
continue;
}
x1 = atom->GetX () - x0 - x;
y1 = atom->GetY () - y0 - y;
z1 = atom->GetZ () - z0 - z;
dist = sqrt (x1 * x1 + y1 * y1 + z1 * z1);
w = sqrt (x1 * x1 + y1 * y1);
if (w > 0) {
xrot = - y1 / w ;
yrot = x1 / w ;
arot = atan2 (w, z1) * 180. / M_PI ;
} else {
xrot = 0;
if (z1 > 0)
yrot = arot = 0.0;
else
{
yrot = 1.0;
arot = 180.0;
}
}
glPushMatrix ();
glTranslated (x, y, z);
glRotated (arot, xrot, yrot, 0.0f);
glColor3f (0.75, 0.75, 0.75);
quadObj = gluNewQuadric ();
gluQuadricDrawStyle (quadObj, GL_FILL);
gluQuadricNormals (quadObj, GL_SMOOTH);
gluCylinder (quadObj, 0.12, 0.12, dist, 20, 10);
gluDeleteQuadric (quadObj);
glPopMatrix ();
bond = m_Mol.NextBond (j);
}
}
void Chem3dDoc::Load (char const *uri, char const *mime_type)
{
GnomeVFSHandle *handle;
GnomeVFSFileInfo *info = gnome_vfs_file_info_new ();
GnomeVFSResult result = gnome_vfs_open (&handle, uri, GNOME_VFS_OPEN_READ);
if (result != GNOME_VFS_OK) {
gnome_vfs_file_info_unref (info);
return;
}
if (mime_type)
gnome_vfs_get_file_info_from_handle (handle, info, (GnomeVFSFileInfoOptions) 0);
else {
gnome_vfs_get_file_info_from_handle (handle, info,
(GnomeVFSFileInfoOptions)(GNOME_VFS_FILE_INFO_GET_MIME_TYPE |
GNOME_VFS_FILE_INFO_FORCE_SLOW_MIME_TYPE));
if (!strncmp (info->mime_type, "text", 4))
gnome_vfs_get_file_info_from_handle (handle, info,
(GnomeVFSFileInfoOptions)GNOME_VFS_FILE_INFO_GET_MIME_TYPE);
}
gchar *buf = new gchar[info->size + 1];
GnomeVFSFileSize n;
gnome_vfs_read (handle, buf, info->size, &n);
buf[info->size] = 0;
if (n == info->size) {
if (!mime_type)
mime_type = info->mime_type;
LoadData (buf, mime_type);
if (m_App) {
char *dirname = g_path_get_dirname (uri);
m_App->SetCurDir (dirname);
g_free (dirname);
}
}
if (!strlen (m_Mol.GetTitle()))
m_Mol.SetTitle (g_path_get_basename (uri));
gnome_vfs_file_info_unref (info);
delete [] buf;
g_free (handle);
}
void Chem3dDoc::LoadData (char const *data, char const *mime_type)
{
istringstream is (data);
m_Mol.Clear ();
char *old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
setlocale (LC_NUMERIC, "C");
OBConversion Conv;
OBFormat* pInFormat = Conv.FormatFromMIME (mime_type);
if (pInFormat) {
Conv.SetInAndOutFormats (pInFormat, pInFormat);
Conv.Read (&m_Mol,&is);
m_Empty = m_Mol.NumAtoms () == 0;
}
setlocale (LC_NUMERIC, old_num_locale);
m_View->Update ();
g_free (old_num_locale);
}
struct VrmlBond {
double x, y, z;
double xrot, zrot, arot;
};
typedef struct {int n; list<OBAtom*> l;} sAtom;
typedef struct {int n; list<struct VrmlBond> l;} sBond;
void Chem3dDoc::OnExportVRML (string const &filename)
{
char *old_num_locale;
double R, w, x, y, z, x0, y0, z0, dist;
int n = 0, Z;
const gdouble* color;
char const *symbol;
try {
ostringstream file;
GnomeVFSHandle *handle = NULL;
GnomeVFSFileSize fs;
GnomeVFSResult res;
map<string, sAtom> AtomsMap;
map<string, sBond> BondsMap;
if ((res = gnome_vfs_create (&handle, filename.c_str (), GNOME_VFS_OPEN_WRITE, true, 0644)) != GNOME_VFS_OK)
throw (int) res;
old_num_locale = g_strdup (setlocale (LC_NUMERIC, NULL));
setlocale (LC_NUMERIC, "C");
file << "#VRML V2.0 utf8" << endl;
x0 = y0 = z0 = 0.0;
std::vector < OBNodeBase * >::iterator i;
OBAtom* atom = m_Mol.BeginAtom (i);
while (atom) {
Z = atom->GetAtomicNum ();
x0 += atom->GetX ();
y0 += atom->GetY ();
z0 += atom->GetZ ();
atom = m_Mol.NextAtom (i);
}
x0 /= m_Mol.NumAtoms ();
y0 /= m_Mol.NumAtoms ();
z0 /= m_Mol.NumAtoms ();
//Create prototypes for atoms
for (atom = m_Mol.BeginAtom (i); atom; atom = m_Mol.NextAtom (i)) {
Z = atom->GetAtomicNum ();
if (!Z)
continue;
symbol = Element::Symbol (Z);
if (AtomsMap[symbol].l.empty()) {
AtomsMap[symbol].n = n;
R = etab.GetVdwRad (Z);
if (m_Display3D == BALL_AND_STICK)
R *= 0.2;
color = gcu_element_get_default_color (Z);
file << "PROTO Atom" << n++ << " [] {Shape {" << endl << "\tgeometry Sphere {radius " << R << "}" << endl;
file << "\tappearance Appearance {" << endl << "\t\tmaterial Material {" << endl << "\t\t\tdiffuseColor " << color[0] << " " << color[1] << " " << color[2] << endl;
file << "\t\t\tspecularColor 1 1 1" << endl << "\t\t\tshininess 0.9" << endl << "\t\t}" << endl << "\t}\r\n}}" << endl;
}
AtomsMap[symbol].l.push_back(atom);
}
//Create prototypes for bonds
double conv = M_PI / 180;
Matrix m (m_View->GetPsi () * conv, m_View->GetTheta () * conv, m_View->GetPhi () * conv, euler);
if (m_Display3D == BALL_AND_STICK) {
std::vector < OBEdgeBase * >::iterator b;
OBBond* bond = m_Mol.BeginBond (b);
double x1, y1, z1;
struct VrmlBond vb;
n = 0;
while (bond) {
atom = bond->GetBeginAtom ();
if (atom->GetAtomicNum () == 0) {
bond = m_Mol.NextBond (b);
continue;
}
vb.x = atom->GetX () - x0;
vb.y = atom->GetY () - y0;
vb.z = atom->GetZ () - z0;
atom = bond->GetEndAtom ();
if (atom->GetAtomicNum () == 0) {
bond = m_Mol.NextBond (b);
continue;
}
x1 = atom->GetX () - x0 - vb.x;
y1 = atom->GetY () - y0 - vb.y;
z1 = atom->GetZ () - z0 - vb.z;
vb.x += x1 / 2;
vb.y += y1 / 2;
vb.z += z1 / 2;
m.Transform(vb.x, vb.y, vb.z);
m.Transform(x1, y1, z1);
dist = sqrt (x1 * x1 + y1 * y1 + z1 * z1);
w = sqrt (x1 * x1 + z1 * z1);
if (w > 0) {
vb.xrot = z1 / w;
vb.zrot = -x1 / w;
vb.arot = atan2 (w, y1);
} else {
vb.zrot = 0.;
vb.xrot = 0.;
vb.arot = 0.0;
}
char *buf = g_strdup_printf ("%g", dist);
if (BondsMap[buf].l.empty()) {
BondsMap[buf].n = n;
file << "PROTO Bond" << n++ << " [] {Shape {" << endl << "\tgeometry Cylinder {radius " << 0.12 << "\theight " << dist << "}" << endl;
file << "\tappearance Appearance {" << endl << "\t\tmaterial Material {" << endl << "\t\t\tdiffuseColor " << .75 << " " << .75 << " " << .75 << endl;
file << "\t\t\tspecularColor 1 1 1" << endl << "\t\t\tshininess 0.9" << endl << "\t\t}" << endl << "\t}\r\n}}" << endl;
}
BondsMap[buf].l.push_back (vb);
bond = m_Mol.NextBond (b);
}
}
//world begin
file << "Background{skyColor " << m_View->GetRed () << " " << m_View->GetBlue () << " " << m_View->GetGreen () << "}" << endl;
file << "Viewpoint {fieldOfView " << m_View->GetAngle () / 90*1.570796326794897 << "\tposition 0 0 " << m_View->GetRadius () << "}" << endl;
file << "Transform {" << endl << "\tchildren [" << endl;
map<std::string, sAtom>::iterator k, kend = AtomsMap.end ();
list<OBAtom*>::iterator j, jend;
for (k = AtomsMap.begin (); k != kend; k++) {
jend = (*k).second.l.end ();
for (j = (*k).second.l.begin (); j != jend; j++) {
x = (*j)->GetX ();
y = (*j)->GetY ();
z = (*j)->GetZ ();
m.Transform(x, y, z);
file << "\t\tTransform {translation " << x << " " << y << " " << z << " children [Atom" << (*k).second.n << " {}]}" << endl;
}
(*k).second.l.clear();
}
AtomsMap.clear();
map<std::string, sBond>::iterator l, lend = BondsMap.end ();
list<struct VrmlBond>::iterator mc, mend;
for (l = BondsMap.begin (); l != lend; l++) {
mend = (*l).second.l.end ();
for (mc = (*l).second.l.begin (); mc != mend; mc++) {
file << "\t\tTransform {" << endl << "\t\t\trotation " << (*mc).xrot << " " << 0. << " " << (*mc).zrot << " " << (*mc).arot << endl;
file << "\t\t\ttranslation " << (*mc).x << " " << (*mc).y << " " << (*mc).z << endl\
<< "\t\t\tchildren [Bond" << (*l).second.n << " {}]}" << endl;
}
}
//end of the world
file << "\t]" << endl << "}" << endl;
setlocale(LC_NUMERIC, old_num_locale);
g_free(old_num_locale);
if ((res = gnome_vfs_write (handle, file.str ().c_str (), (GnomeVFSFileSize) file.str ().size (), &fs)) != GNOME_VFS_OK)
throw (int) res;
gnome_vfs_close (handle);
}
catch (int n) {
cerr <<"gnome-vfs error" << n << endl;
}
}
syntax highlighted by Code2HTML, v. 0.9.1