]> Git trees. - libqmvoc.git/commitdiff
Twofold improvement in Kalzium's 3D viewer:
authorBenoît Jacob <jacob.benoit.1@gmail.com>
Fri, 17 Nov 2006 08:42:01 +0000 (08:42 +0000)
committerBenoît Jacob <jacob.benoit.1@gmail.com>
Fri, 17 Nov 2006 08:42:01 +0000 (08:42 +0000)
 - automatically set up nice viewpoint when loading a molecule.
 - draw multiple bonds with "intelligent" orientation, so that they
   avoid looking like single bonds.
Both improvements rely on Eigen's linear regression feature.

svn path=/trunk/KDE/kdeedu/kalzium/src/kalziumglhelperclasses.cpp; revision=605562

kalzium/kalziumglhelperclasses.cpp
kalzium/kalziumglhelperclasses.h
kalzium/kalziumglwidget.cpp

index 58347841e161d164d11a9a87f7829fd7a3a4a0c6..f9e3c321662afc3abf29142c6e862019cf5a1d1d 100644 (file)
  *                                                                         *
  ***************************************************************************/
 #include "kalziumglhelperclasses.h"
-#include <math.h>
 
-using namespace KalziumGLHelpers;
 using namespace OpenBabel;
 using namespace Eigen;
 
+#include<iostream>
+using namespace std;
+
+namespace KalziumGLHelpers
+{
+
 MolStyle::MolStyle( BondStyle bondStyle, AtomStyle atomStyle,
        double singleBondRadius,
        bool renderMultipleBonds,
@@ -387,14 +391,12 @@ void Cylinder::buildBuffers()
                m_normalBuffer[ 2 * i ].y() = y;
                m_normalBuffer[ 2 * i ].z() = 0.0;
 
+               m_normalBuffer[ 2 * i + 1 ] = m_normalBuffer[ 2 * i ];
+
                m_vertexBuffer[ 2 * i ].x() = x;
                m_vertexBuffer[ 2 * i ].y() = y;
                m_vertexBuffer[ 2 * i ].z() = 1.0;
 
-               m_normalBuffer[ 2 * i + 1 ].x() = x;
-               m_normalBuffer[ 2 * i + 1 ].y() = y;
-               m_normalBuffer[ 2 * i + 1 ].z() = 0.0;
-
                m_vertexBuffer[ 2 * i + 1 ].x() = x;
                m_vertexBuffer[ 2 * i + 1 ].y() = y;
                m_vertexBuffer[ 2 * i + 1 ].z() = 0.0;
@@ -406,25 +408,34 @@ void Cylinder::draw( const Vector3d &end1, const Vector3d &end2,
 {
        // the "axis vector" of the cylinder
        Vector3d axis = end2 - end1;
+       double axisNorm = axis.norm();
+       if( axisNorm == 0.0 ) return;
+       Vector3d axisNormalized = axis / axisNorm;
        
-       // find two unit vectors v, w such that
-       // (axis,v,w) is an orthogonal basis
-       Vector3d v, w;
-       createOrthoBasisGivenFirstVector( axis, &v, &w );
-       v *= radius;
-       w *= radius;
+       Vector3d ortho1( axisNormalized.y(), -axisNormalized.x(), 0.0 );
+       double ortho1Norm = ortho1.norm();
+       if( ortho1Norm > 0.001 ) ortho1 /= ortho1Norm;
+       else {
+               ortho1 = Vector3d( 0.0,
+                                  axisNormalized.z(),
+                                  -axisNormalized.y() );
+               ortho1.normalize();
+       }
+       ortho1 *= radius;
+
+       Vector3d ortho2 = cross( axisNormalized, ortho1 );
 
        // construct the 4D transformation matrix
        Matrix4d matrix;
 
-       matrix(0, 0) = v(0);
-       matrix(1, 0) = v(1);
-       matrix(2, 0) = v(2);
+       matrix(0, 0) = ortho1(0);
+       matrix(1, 0) = ortho1(1);
+       matrix(2, 0) = ortho1(2);
        matrix(3, 0) = 0.0;
 
-       matrix(0, 1) = w(0);
-       matrix(1, 1) = w(1);
-       matrix(2, 1) = w(2);
+       matrix(0, 1) = ortho2(0);
+       matrix(1, 1) = ortho2(1);
+       matrix(2, 1) = ortho2(2);
        matrix(3, 1) = 0.0;
 
        matrix(0, 2) = axis(0);
@@ -441,13 +452,25 @@ void Cylinder::draw( const Vector3d &end1, const Vector3d &end2,
        glPushMatrix();
        glMultMatrixd( matrix.array() );
        if( order == 1 ) VertexArray::draw();
-       else for( int i = 0; i < order; i++)
+       else
        {
-               glPushMatrix();
-               glRotated( 360.0 * i / order, 0.0, 0.0, 1.0 );
-               glTranslated( shift / radius, 0.0, 0.0 );
-               VertexArray::draw();
-               glPopMatrix();
+               double angleOffset = 0.0;
+               if( order >= 3 )
+               {
+                       if( order == 3 ) angleOffset = 90.0;
+                       else angleOffset = 22.5;
+               }
+               
+               double displacementFactor = shift / radius;
+               for( int i = 0; i < order; i++)
+               {
+                       glPushMatrix();
+                       glRotated( angleOffset + 360.0 * i / order,
+                                  0.0, 0.0, 1.0 );
+                       glTranslated( displacementFactor, 0.0, 0.0 );
+                       VertexArray::draw();
+                       glPopMatrix();
+               }
        }
        glPopMatrix();
 }
@@ -634,7 +657,7 @@ void TextRenderer::print( int x, int y, const QString &string )
        if( ! m_isBetweenBeginAndEnd ) do_end();
 }
 
-void KalziumGLHelpers::createOrthoBasisGivenFirstVector
+void createOrthoBasisGivenFirstVector
        ( const Vector3d &U, Vector3d * v, Vector3d * w )
 {
        U.makeOrthoVector(v);
@@ -642,36 +665,4 @@ void KalziumGLHelpers::createOrthoBasisGivenFirstVector
        w->normalize();
 }
 
-/*
-void LinearRegression( const std::list<Vector3d *> & points,
-       Vector3d & ret_plane_base_point, Vector3d & ret_plane_normal_vector )
-{
-       double sum_x = 0.0;
-       double sum_y = 0.0;
-       double sum_z = 0.0;
-       double sum_xx = 0.0;
-       double sum_xy = 0.0;
-       double sum_xz = 0.0;
-       double sum_yy = 0.0;
-       double sum_yz = 0.0;
-       double sum_zz = 0.0;
-
-       for( std::list<Vector3d *>::const_iterator iter = points.begin();
-               iter != points.end(); iter++ )
-       {
-               double x = iter->x();
-               double y = iter->y();
-               double z = iter->z();
-               sum_x += x;
-               sum_y += y;
-               sum_z += z;
-               sum_xx += x * x;
-               sum_xy += x * y;
-               sum_xz += x * z;
-               sum_yy += y * y;
-               sum_yz += y * z;
-               sum_zz += z * z;
-       }
-
-       
-}*/
+} // namespace KalziumGLHelpers
index 2783639693c368da2ff3be0ed1a2c89d4cfa1bca..d025e0dba0bab2bdb3ef4d663f82eaf275292d6a 100644 (file)
@@ -77,7 +77,7 @@ struct MolStyle
        double m_singleBondRadius;
        /** If true, multiple bonds will be rendered as such. If false,
         * they will be rendered as single bonds */
-       double m_renderMultipleBonds;
+       bool m_renderMultipleBonds;
        /** The radius ( = half-thickness ) of each bond inside a
         * multiple bond */
        double m_multipleBondRadius;
@@ -161,10 +161,6 @@ void createOrthoBasisGivenFirstVector( const Eigen::Vector3d & U,
                                              Eigen::Vector3d * v,
                                              Eigen::Vector3d * w );
 
-/*void LinearRegression( const std::list<vector3 *> & points,
-       vector3 & ret_plane_base_point, vector3 & ret_plane_normal_vector );
-*/
-
 /**
 * This is an abstract base class for an OpenGL vertex array, with an option
 * (controlled by USE_DISPLAY_LISTS) to compile a display list from it, in which
index f3b1da25ae3009eda5793106ee05e4cd80da73a8..aa0827762598aac08dd136429114592711efc66c 100644 (file)
@@ -13,8 +13,6 @@
  ***************************************************************************/
 #include "kalziumglwidget.h"
 
-#include <math.h>
-
 #include <kdebug.h>
 #include <klocale.h>
 
@@ -26,6 +24,7 @@
 #endif
 
 #include <openbabel/mol.h>
+#include <eigen/regression.h>
 
 using namespace KalziumGLHelpers;
 using namespace OpenBabel;
@@ -468,7 +467,7 @@ void KalziumGLWidget::drawBond( OBBond *bond )
                                m_molStyle.m_multipleBondShift );
                        glLoadName( atom2->GetIdx() );
                        Color( atom2 ).applyAsMaterials();
-                       m_cylinder.draw( v2, v3, radius, order,
+                       m_cylinder.draw( v3, v2, radius, order,
                                m_molStyle.m_multipleBondShift );
                        break;
 
@@ -499,6 +498,7 @@ void KalziumGLWidget::slotSetMolecule( OpenBabel::OBMol* molecule )
        if ( !molecule ) return;
        m_molecule = molecule;
        m_haveToRecompileDisplayList = true;
+       m_rotationMatrix.loadIdentity();
        m_selectedAtoms.clear();
        m_clickedAtom = 0;
        prepareMoleculeData();
@@ -548,7 +548,7 @@ void KalziumGLWidget::prepareMoleculeData()
        //Center the molecule
        m_molecule->Center();
 
-       // calculate the radius of the molecule without the electrons
+       // compute the radius of the molecule without the electrons
        // that is, the maximal distance between the center of an atom
        // of the molecule and the center of the molecule
        m_molRadiusWithoutElectrons = 0.0;
@@ -559,6 +559,39 @@ void KalziumGLWidget::prepareMoleculeData()
                if( rad > m_molRadiusWithoutElectrons )
                        m_molRadiusWithoutElectrons = rad;
        }
+
+       // compute the molecule's fitting plane
+       unsigned int numAtoms = 0, i = 0;
+       FOR_ATOMS_OF_MOL( a, m_molecule ) numAtoms++;
+       Vector3d * atomCenters = new Vector3d[numAtoms];
+       FOR_ATOMS_OF_MOL( a, m_molecule )
+       {
+               atomCenters[i] = Vector3d( a->GetVector().AsArray() );
+               i++;
+       }
+       Vector4d planeCoeffs;
+       computeFittingHyperplane( numAtoms, atomCenters, &planeCoeffs );
+       delete[] atomCenters;
+
+       // compute rotation matrix to orient the molecule in the (x,y)-plane
+       Vector3d planeNormalVector( & planeCoeffs(0) ), v, w;
+       planeNormalVector.normalize();
+       createOrthoBasisGivenFirstVector( planeNormalVector, &v, &w );
+       Matrix3d rotation;
+       rotation.setRow( 0, v );
+       rotation.setRow( 1, w );
+       rotation.setRow( 2, planeNormalVector );
+
+       // apply rotation to each atom in the molecule
+       FOR_ATOMS_OF_MOL( a, m_molecule )
+       {
+               Vector3d atomCenter;
+               atomCenter = Vector3d( a->GetVector().AsArray() );
+               atomCenter = rotation * atomCenter;
+               a->SetVector( atomCenter.x(),
+                             atomCenter.y(),
+                             atomCenter.z() );
+       }
 }
 
 double KalziumGLWidget::getMolRadius()