From 1ef6cf4d32e92ead9e5fbde05768b076d4794c5a Mon Sep 17 00:00:00 2001 From: =?utf8?q?Beno=C3=AEt=20Jacob?= Date: Fri, 17 Nov 2006 08:42:01 +0000 Subject: [PATCH] Twofold improvement in Kalzium's 3D viewer: - 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 | 107 +++++++++++++---------------- kalzium/kalziumglhelperclasses.h | 6 +- kalzium/kalziumglwidget.cpp | 41 +++++++++-- 3 files changed, 87 insertions(+), 67 deletions(-) diff --git a/kalzium/kalziumglhelperclasses.cpp b/kalzium/kalziumglhelperclasses.cpp index 5834784..f9e3c32 100644 --- a/kalzium/kalziumglhelperclasses.cpp +++ b/kalzium/kalziumglhelperclasses.cpp @@ -11,12 +11,16 @@ * * ***************************************************************************/ #include "kalziumglhelperclasses.h" -#include -using namespace KalziumGLHelpers; using namespace OpenBabel; using namespace Eigen; +#include +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 & 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::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 diff --git a/kalzium/kalziumglhelperclasses.h b/kalzium/kalziumglhelperclasses.h index 2783639..d025e0d 100644 --- a/kalzium/kalziumglhelperclasses.h +++ b/kalzium/kalziumglhelperclasses.h @@ -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 & 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 diff --git a/kalzium/kalziumglwidget.cpp b/kalzium/kalziumglwidget.cpp index f3b1da2..aa08277 100644 --- a/kalzium/kalziumglwidget.cpp +++ b/kalzium/kalziumglwidget.cpp @@ -13,8 +13,6 @@ ***************************************************************************/ #include "kalziumglwidget.h" -#include - #include #include @@ -26,6 +24,7 @@ #endif #include +#include 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() -- 2.47.3