KalziumGLWidget::KalziumGLWidget( QWidget * parent )
: QGLWidget( parent )
{
- m_sphereDisplayList = 0;
- m_bondDisplayList = 0;
m_isDragging = false;
m_molecule = 0;
m_detail = 0;
KalziumGLWidget::~KalziumGLWidget()
{
- if( m_sphereDisplayList )
- glDeleteLists( m_sphereDisplayList, 1 );
}
void KalziumGLWidget::initializeGL()
// render the atoms
if( m_atomStyle == ATOM_SPHERE )
{
- m_sphere.select();
-
glEnable( GL_LIGHTING );
FOR_ATOMS_OF_MOL( a, m_molecule )
drawSphere(
x, y, z,
+ atomRadius(),
c);
}
}
case BOND_CYLINDER_GRAY:
case BOND_CYLINDER_BICOLOR:
glEnable( GL_LIGHTING );
- m_cylinder.select();
break;
case BOND_DISABLED: break;
}
GLColor c( 0.4, 0.4, 1.0, 0.7 );
- GLFLOAT radius = m_molMinBondLength * 0.5;
- GLFLOAT min_radius = (GLFLOAT) atomRadius () * 1.2;
+ GLFLOAT radius = m_molMinBondLength * 0.35;
+ GLFLOAT min_radius = (GLFLOAT) atomRadius () * 1.25;
if( radius < min_radius ) radius = min_radius;
glEnable( GL_BLEND );
glEnable( GL_LIGHTING );
- m_sphere.select();
-
drawSphere(
x, y, z,
+ radius,
c);
glDisable( GL_BLEND );
m_cylinder.setup( 8 * ( m_detail + 1 ), bondRadius() );
}
-void KalziumGLWidget::drawSphere( GLdouble x, GLdouble y, GLdouble z, GLColor &color )
+void KalziumGLWidget::drawSphere( GLdouble x, GLdouble y, GLdouble z,
+ GLfloat radius, GLColor &color )
{
color.applyAsMaterials();
glPushMatrix();
glTranslated( x, y, z );
- m_sphere.draw();
+ m_sphere.drawScaled( radius );
glPopMatrix();
}
color.applyAsMaterials();
// the "axis vector" of the cylinder
- FLOAT axis[3] = { x2 - x1, y2 - y1, z2 - z1 };
+ GLVector3<FLOAT> axis( x2 - x1, y2 - y1, z2 - z1 );
// find two vectors v, w such that (axis,v,w) is an orthogonal basis.
- FLOAT v[3], w[3];
- construct_ortho_3D_basis_given_first_vector3( axis, v, w );
+ GLVector3<FLOAT> v, w;
+ axis.construct_ortho_basis_given_first_vector( v, w );
// normalize v and w. We DON'T want to normalize axis
- normalize3( v );
- normalize3( w );
+ v.normalize();
+ w.normalize();
// construct the 4D transformation matrix
FLOAT matrix[16];
// column 1
- matrix[0] = v[0];
- matrix[1] = v[1];
- matrix[2] = v[2];
+ matrix[0] = v.x;
+ matrix[1] = v.y;
+ matrix[2] = v.z;
matrix[3] = 0.0;
// column 2
- matrix[4] = w[0];
- matrix[5] = w[1];
- matrix[6] = w[2];
+ matrix[4] = w.x;
+ matrix[5] = w.y;
+ matrix[6] = w.z;
matrix[7] = 0.0;
// column 3
- matrix[8] = axis[0];
- matrix[9] = axis[1];
- matrix[10] = axis[2];
+ matrix[8] = axis.x;
+ matrix[9] = axis.y;
+ matrix[10] = axis.z;
matrix[11] = 0.0;
// column 4
updateGL();
}
-bool KalziumGLWidget::approx_equal( FLOAT a, FLOAT b, FLOAT precision )
+template<class T> bool GLVector3<T>::approx_equal(
+ FLOAT a, FLOAT b, FLOAT precision )
{
FLOAT abs_a = GLFABS( a );
FLOAT abs_b = GLFABS( b );
return( GLFABS( a - b ) <= precision * max_abs );
}
-FLOAT KalziumGLWidget::norm3( FLOAT *u )
-{
- return GLSQRT( u[0] * u[0] + u[1] * u[1] + u[2] * u[2] );
-}
-
-void KalziumGLWidget::normalize3( FLOAT *u )
+template<class T> void GLVector3<T>::construct_ortho_basis_given_first_vector(
+ GLVector3<T> &v, GLVector3<T> &w )
{
- FLOAT n = norm3( u );
- if( 0 == n ) return;
- u[0] /= n;
- u[1] /= n;
- u[2] /= n;
-}
+ if( norm() == 0 ) return;
+
+ // let us first make a normalized copy of *this
+ GLVector3<T> u = *this;
+ u.normalize();
-void KalziumGLWidget::construct_ortho_3D_basis_given_first_vector3(
- const FLOAT *U, FLOAT *v, FLOAT *w)
-{
- // let us first make a normalized copy of U
- FLOAT u[3];
- u[0] = U[0]; u[1] = U[1]; u[2] = U[2];
- if( 0 == norm3( u ) ) return;
- normalize3( u );
+ // first we want to set v to be non-colinear to u
- // initially we set v = u
- v[0] = u[0]; v[1] = u[1]; v[2] = u[2];
+ v = u;
- // next we want to change v so that it becomes non-colinear to u
- if( ! approx_equal( v[0], v[1], 0.01 ) )
+ if( ! approx_equal( v.x, v.y, 0.1 ) )
{
- FLOAT tmp = v[0];
- v[0] = v[1];
- v[1] = tmp;
+ FLOAT tmp = v.x;
+ v.x = v.y;
+ v.y = tmp;
}
- else if( ! approx_equal( v[1], v[2], 0.01 ) )
+ else if( ! approx_equal( v.y, v.z, 0.1 ) )
{
- FLOAT tmp = v[2];
- v[2] = v[1];
- v[1] = tmp;
+ FLOAT tmp = v.z;
+ v.z = v.y;
+ v.y = tmp;
}
else // the 3 coords of v are approximately equal
{ // which implies that v is not colinear to (0,0,1)
- v[0] = 0.0; v[1] = 0.0; v[2] = 1.0;
+ v = GLVector3<T>( 0, 0, 1 );
}
// now, v is not colinear to u. We compute its dot product with u
- FLOAT u_dot_v = u[0] * v[0] + u[1] * v[1] + u[2] * v[2];
+ FLOAT u_dot_v = u.x * v.x + u.y * v.y + u.z * v.z;
// now we change v so that it becomes orthogonal to u
- v[0] -= u[0] * u_dot_v;
- v[1] -= u[1] * u_dot_v;
- v[2] -= u[2] * u_dot_v;
+ v.x -= u.x * u_dot_v;
+ v.y -= u.y * u_dot_v;
+ v.z -= u.z * u_dot_v;
// now that u and v are orthogonal, w can be constructed as
// their crossed product
- w[0] = u[1] * v[2] - u[2] * v[1];
- w[1] = u[2] * v[0] - u[0] * v[2];
- w[2] = u[0] * v[1] - u[1] * v[0];
+ w.x = u.y * v.z - u.z * v.y;
+ w.y = u.z * v.x - u.x * v.z;
+ w.z = u.x * v.y - u.y * v.x;
}
GLColor::GLColor()
c.m_green = 1.0;
c.m_blue = 0.0;
}
- else if ( atom->GetAtomicNum() == 11 )
- {//Natrium
- c.m_red = 0.2;
- c.m_green = 1.0;
- c.m_blue = 0.0;
- }
- else if ( atom->GetAtomicNum() == 17 )
- {//Chlorine
- c.m_red = 0.1;
- c.m_green = 0.1;
- c.m_blue = 0.9;
- }
else
{
c.m_red = 0.5;
GLVertexArray::GLVertexArray()
{
+ allocateId();
+ m_mode = GL_TRIANGLE_STRIP;
m_vertexBuffer = 0;
m_normalBuffer = 0;
m_indexBuffer = 0;
if( m_normalBuffer ) delete [] m_normalBuffer;
}
+void GLVertexArray::allocateId()
+{
+ static int counter = 0;
+ m_id = counter++;
+}
+
+
void GLVertexArray::select()
{
- if( ! m_isInitialized ) return;
+ static int selected_id = -1;
+
+ if( selected_id == m_id ) return;
+
glEnableClientState( GL_VERTEX_ARRAY );
glEnableClientState( GL_NORMAL_ARRAY );
glVertexPointer( 3, GL_FLOAT, 0, m_vertexBuffer );
glNormalPointer( GL_FLOAT, 0, m_normalBuffer );
+ selected_id = m_id;
}
void GLVertexArray::draw()
{
- if( m_isInitialized )
- glDrawElements( m_mode, m_indexCount,
- GL_UNSIGNED_SHORT, m_indexBuffer );
+ if( ! m_isInitialized ) return;
+ select();
+ glDrawElements( m_mode, m_indexCount,
+ GL_UNSIGNED_SHORT, m_indexBuffer );
}
-bool GLVertexArray::reallocateBuffers()
+bool GLVertexArray::allocateBuffers()
{
if( m_vertexCount > 65536 ) return false;
GLSphere::GLSphere()
: GLVertexArray()
{
- m_mode = GL_TRIANGLE_STRIP;
m_detail = 0;
m_radius = -1.0;
}
m_vertexCount = ( 3 * m_detail + 1 ) * ( 5 * m_detail + 1 );
m_indexCount = (2 * ( 2 * m_detail + 1 ) + 2 ) * 5 * m_detail;
- if( ! reallocateBuffers() ) return;
+ if( ! allocateBuffers() ) return;
for( int strip = 0; strip < 5; strip++ )
for( int column = 1; column < m_detail; column++ )
if( detail == m_detail && radius == m_radius ) return;
m_detail = detail;
m_radius = radius;
+ allocateId();
initialize();
}
+void GLSphere::drawScaled( GLfloat radius )
+{
+ const GLfloat precision = 0.001;
+
+ if( GLVector3<GLfloat>::approx_equal( radius, m_radius, precision ) )
+ {
+ draw();
+ return;
+ }
+
+ GLfloat factor = radius / m_radius;
+ glEnable( GL_NORMALIZE );
+ glPushMatrix();
+ glScalef( factor, factor, factor );
+ draw();
+ glPopMatrix();
+ glDisable( GL_NORMALIZE );
+}
+
GLCylinder::GLCylinder()
: GLVertexArray()
{
if( faces == m_faces && radius == m_radius ) return;
m_faces = faces;
m_radius = radius;
+ allocateId();
initialize();
}
if( m_faces < 3 ) return;
m_vertexCount = 2 * m_faces + 2; // we will use a redundant vertex array
- m_indexCount = 1; // we won't use it.
+ m_indexCount = 0; // we won't use it.
- if( ! reallocateBuffers() ) return;
+ if( ! allocateBuffers() ) return;
for( int i = 0; i <= m_faces; i++)
{
void GLCylinder::draw()
{
- if ( m_isInitialized ) glDrawArrays( m_mode, 0, m_vertexCount );
+ if ( ! m_isInitialized ) return;
+ select();
+ glDrawArrays( m_mode, 0, m_vertexCount );
}
#include "kalziumglwidget.moc"
void applyAsMaterials();
};
+/**
+ * This template class represents a vector in 3-space. It is meant to be
+ * used with T = a floating-point type.
+ *
+ * @author Benoit Jacob
+ */
-
-template<class T> struct GLVector3
+template<class T> class GLVector3
{
- T x, y, z;
- GLVector3() {}
- GLVector3( T _x, T _y, T _z)
- { x = _x; y = _y; z = _z; }
- inline T norm() { return GLSQRT( x * x + y * y + z * z ); }
- void normalize()
- {
- T n = norm();
- if( n == 0.0 ) return;
- x /= n;
- y /= n;
- z /= n;
- }
- GLVector3<T>& operator= ( const GLVector3<T>& other )
- {
- x = other.x;
- y = other.y;
- z = other.z;
- return *this;
- }
+ public:
+ T x, y, z;
+ GLVector3() {}
+ GLVector3( T _x, T _y, T _z)
+ { x = _x; y = _y; z = _z; }
+
+ /**
+ * Tests whether two FLOATs are approximately equal.
+ * Recall that operator == between floating-point types
+ * is broken.
+ * returns true if abs( a - b ) <= c * precision
+ * where c = max( abs( a ), abs( b ) )
+ */
+ static bool approx_equal( FLOAT a, FLOAT b, FLOAT precision );
+
+ GLVector3<T>& operator= ( const GLVector3<T>& other )
+ {
+ x = other.x;
+ y = other.y;
+ z = other.z;
+ return *this;
+ }
+
+ /**
+ * returns the norm of the vector, that is, its length
+ */
+ inline T norm() { return GLSQRT( x * x + y * y + z * z ); }
+
+ /**
+ * normalizes the vector, that is, scales it so that its norm
+ * becomes 1.
+ */
+ void normalize()
+ {
+ T n = norm();
+ if( n == 0.0 ) return;
+ x /= n;
+ y /= n;
+ z /= n;
+ }
+
+ /**
+ * Constructs two vectors v and w
+ * such that (*this, v, w) is a direct orthogonal basis.
+ * v and w are not getting normalized.
+ */
+ void construct_ortho_basis_given_first_vector(
+ GLVector3<T> & v, GLVector3<T> & w );
};
/**
*/
class GLVertexArray
{
+ private:
+ int m_id;
+
protected:
GLenum m_mode;
GLVector3<GLfloat> *m_vertexBuffer;
bool m_isInitialized;
virtual void initialize() = 0;
- virtual bool reallocateBuffers();
+ virtual bool allocateBuffers();
+ virtual void allocateId();
+ virtual void select();
public:
GLVertexArray();
virtual ~GLVertexArray();
- virtual void select();
virtual inline void draw();
};
GLSphere();
virtual ~GLSphere() {}
virtual void setup( int detail, GLfloat radius );
+ virtual void drawScaled( GLfloat radius );
};
/**
Q_OBJECT
protected:
- GLuint m_sphereDisplayList;
- GLuint m_bondDisplayList;
+ /**
+ * The geometric model of the sphere (used for atoms).
+ */
GLSphere m_sphere;
+
+ /**
+ * The geometric model of the cylinder (used for bonds).
+ */
GLCylinder m_cylinder;
/**
QPoint m_lastDraggingPosition;
+
+ /**
+ * Stores the rotation that is applied to the model.
+ */
GLFLOAT m_RotationMatrix[16];
/**
* The molecule which is displayed
*/
OpenBabel::OBMol* m_molecule;
+
+ /**
+ * approximate radius of the molecule
+ */
GLFLOAT m_molRadius;
+
+ /**
+ * length of the shortest bond in the molecule
+ */
GLFLOAT m_molMinBondLength;
+
+ /**
+ * length of the longest bond in the molecule
+ */
GLFLOAT m_molMaxBondLength;
/**
*/
bool m_useFog;
-
/**
* The selected atom
*/
BOND_CYLINDER_BICOLOR
} m_bondStyle;
- private: // some standard 3D math stuff here
-
- /**
- * Tests whether two FLOATs are approximately equal.
- * Recall that operator == between floating-point types
- * is broken.
- * returns true if abs( a - b ) <= c * precision
- * where c = max( abs( a ), abs( b ) )
- */
- bool approx_equal( FLOAT a, FLOAT b, FLOAT precision );
-
- /**
- * Returns the norm of a 3D vector
- */
- FLOAT norm3( FLOAT *u );
-
/**
- * Normalizes a 3D vector
- */
- void normalize3( FLOAT *u );
-
- /**
- * Given a 3D vector U, constructs two vectors v and w
- * such that (U, v, w) is a direct orthogonal basis.
- * U is not supposed to be normalized, and v and w
- * are not getting normalized.
+ * Some style presets
*/
- void construct_ortho_3D_basis_given_first_vector3(
- const FLOAT *U, FLOAT *v, FLOAT *w);
+ enum StylePreset
+ {
+ PRESET_LINES,
+ PRESET_STICKS,
+ PRESET_SPHERES_AND_GRAY_BONDS,
+ PRESET_SPHERES_AND_BICOLOR_BONDS,
+ PRESET_BIG_SPHERES
+ };
public:
/**
*/
virtual ~KalziumGLWidget();
- inline OpenBabel::OBMol* molecule () const
- { return m_molecule; }
-
/**
- * Some style presets
+ * returns a pointer to the molecule being worked on
*/
- enum StylePreset
- {
- PRESET_LINES,
- PRESET_STICKS,
- PRESET_SPHERES_AND_GRAY_BONDS,
- PRESET_SPHERES_AND_BICOLOR_BONDS,
- PRESET_BIG_SPHERES
- };
-
- /**
- * Chooses the style of rendering among some presets
- * @param stylePreset the wanted style preset
- */
- virtual void ChooseStylePreset( StylePreset stylePreset );
+ inline OpenBabel::OBMol* molecule () const
+ { return m_molecule; }
public slots:
/**
*/
void slotSetDetail( int detail );
+ /**
+ * Chooses the style of rendering among some presets
+ * @param stylePreset the wanted style preset
+ */
/**
* Chooses the style of rendering among some presets
* @param stylePreset the wanted style preset
GLFLOAT x,
GLFLOAT y,
GLFLOAT z,
+ GLfloat radius,
GLColor &color );
/**
GLFLOAT x2, GLFLOAT y2, GLFLOAT z2,
GLColor &color );
+ /**
+ * returns the radius ( = half-thickness ) with which the
+ * bonds are drawn
+ */
inline GLFLOAT bondRadius();
+
+ /**
+ * returns the radius with which the atoms are drawn
+ * (currently all atoms are drawn with the same radius.
+ * when we'll have a better model taking van der Waals radii
+ * into account, we'll remove this member).
+ */
inline GLFLOAT atomRadius();
/**
- * returns the color which an atom should be painted
+ * Chooses the style of rendering among some presets
+ * @param stylePreset the wanted style preset
*/
+ virtual void ChooseStylePreset( StylePreset stylePreset );
+ /**
+ * returns the color which a given atom should be painted
+ */
GLColor& getAtomColor( OpenBabel::OBAtom* atom );
+ /**
+ * recomputes the geometry of the geometric objects ( sphere,
+ * cylinder ).
+ */
virtual void setupObjects();
};