From 0376a61184c55036eb66a5891804be8c91325a86 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Beno=C3=AEt=20Jacob?= Date: Wed, 28 Jun 2006 15:10:35 +0000 Subject: [PATCH] fix selection of multiple atoms (Carsten: I apologize for the lack of documentation of my rendering code. Currently you have to call m_sphere.select() before you can draw a sphere. I could change that now to make it more easy to use, but it's not the right time to do it because a lot of things are going to change when we'll implement rendering different atoms with different sizes). also begin implementation of text renderer for the GL widget. The problem with QGLWidget::renderText is that it only works for ASCII text (possibly 8-bit-extended, though I don't know how to set the character encoding, but in any case no unicode) . Thus no safe i18n is possible. Even if it's not necessary to display the message "please load molecule", I thought it'd be nice to have the possibility to display whatever text we want in the QGLWidget. But if you think it's futile, I'll drop it. Benoit M kalzium/src/kalziumglwidget.h M kalzium/src/kalziumglhelperclasses.h M kalzium/src/kalziumglwidget.cpp svn path=/trunk/KDE/kdeedu/kalzium/src/kalziumglwidget.h; revision=555816 --- kalzium/kalziumglhelperclasses.h | 556 ++++++++++++++++++------------- kalzium/kalziumglwidget.cpp | 10 +- kalzium/kalziumglwidget.h | 2 + 3 files changed, 341 insertions(+), 227 deletions(-) diff --git a/kalzium/kalziumglhelperclasses.h b/kalzium/kalziumglhelperclasses.h index 5698616..b285f41 100644 --- a/kalzium/kalziumglhelperclasses.h +++ b/kalzium/kalziumglhelperclasses.h @@ -14,13 +14,14 @@ ***************************************************************************/ #include #include +#include +#include /** USE_DOUBLE_PRECISION: if defined, use doubles instead of floats for * handling the model's geometric data. This does not seem to impact * significantly the performance. The vertex arrays are unaffected: they * always use floats. */ - #define USE_DOUBLE_PRECISION /** USE_FPS_COUNTER: if defined, the GL Widgets will show a frames-per-second @@ -31,252 +32,357 @@ namespace KalziumGLHelpers { - #ifdef USE_DOUBLE_PRECISION - typedef double FLOAT; - typedef GLdouble GLFLOAT; - #else - typedef float FLOAT; - typedef GLfloat GLFLOAT; - #endif - - inline float SQRT( float x ) { return sqrtf( x ); } - inline double SQRT( double x ) { return sqrt( x ); } - inline float SIN( float x ) { return sinf( x ); } - inline double SIN( double x ) { return sin( x ); } - inline float COS( float x ) { return cosf( x ); } - inline double COS( double x ) { return cos( x ); } - inline float FABS( float x ) { return fabsf( x ); } - inline double FABS( double x ) { return fabs( x ); } - inline void GLMULTMATRIX( const GLfloat *m ) { glMultMatrixf(m); } - inline void GLMULTMATRIX( const GLdouble *m ) { glMultMatrixd(m); } - inline void GLTRANSLATE( GLfloat x, GLfloat y, GLfloat z ) \ - { glTranslatef( x, y, z ); } - inline void GLTRANSLATE( GLdouble x, GLdouble y, GLdouble z ) \ - { glTranslated( x, y, z ); } - + +#ifdef USE_DOUBLE_PRECISION +typedef double FLOAT; +typedef GLdouble GLFLOAT; +#else +typedef float FLOAT; +typedef GLfloat GLFLOAT; +#endif + +inline float SQRT( float x ) { return sqrtf( x ); } +inline double SQRT( double x ) { return sqrt( x ); } +inline float SIN( float x ) { return sinf( x ); } +inline double SIN( double x ) { return sin( x ); } +inline float COS( float x ) { return cosf( x ); } +inline double COS( double x ) { return cos( x ); } +inline float FABS( float x ) { return fabsf( x ); } +inline double FABS( double x ) { return fabs( x ); } +inline void GLMULTMATRIX( const GLfloat *m ) { glMultMatrixf(m); } +inline void GLMULTMATRIX( const GLdouble *m ) { glMultMatrixd(m); } +inline void GLTRANSLATE( GLfloat x, GLfloat y, GLfloat z ) \ + { glTranslatef( x, y, z ); } +inline void GLTRANSLATE( GLdouble x, GLdouble y, GLdouble z ) \ + { glTranslated( x, y, z ); } + +/** +* This class represents a color in OpenGL float red-green-blue format. +* +* @author Benoit Jacob +*/ +struct Color +{ + GLfloat m_red, m_green, m_blue, m_alpha; + + Color(); + Color( GLfloat red, GLfloat green, GLfloat blue, + GLfloat alpha = 1.0 ); + + Color& operator=( const Color& other ); + /** - * This class represents a color in OpenGL float red-green-blue format. - * - * @author Benoit Jacob + * Sets this color to be the one used by OpenGL for rendering + * when lighting is disabled. */ - struct Color + inline void apply() { - GLfloat m_red, m_green, m_blue, m_alpha; - - Color(); - Color( GLfloat red, GLfloat green, GLfloat blue, - GLfloat alpha = 1.0 ); - - Color& operator=( const Color& other ); - - /** - * Sets this color to be the one used by OpenGL for rendering - * when lighting is disabled. - */ - inline void apply() + glColor4fv( reinterpret_cast( this ) ); + } + + /** + * Applies nice OpenGL materials using this color as the + * diffuse color while using different shades for the ambient and + * specular colors. This is only useful if lighting is enabled. + */ + void applyAsMaterials(); +}; + +/** +* Tests whether two Ts are approximately equal. Here T is assumed to be +* a floating-point type. Recall that operator== between floating-point +* types is broken. +* returns true if abs( a - b ) <= c * precision +* where c = max( abs( a ), abs( b ) ) +*/ +template static bool approx_equal( T a, T b, T precision ) +{ + T abs_a = FABS( a ); + T abs_b = FABS( b ); + + T max_abs; + if( abs_a <= abs_b ) + max_abs = abs_b; + else + max_abs = abs_a; + return( FABS( a - b ) <= precision * max_abs ); +} + +/** +* 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 Vector3 +{ + public: + T x, y, z; + Vector3() {} + Vector3( T _x, T _y, T _z) + { x = _x; y = _y; z = _z; } + + Vector3& operator= ( const Vector3& other ) { - glColor4fv( reinterpret_cast( this ) ); + x = other.x; + y = other.y; + z = other.z; + return *this; } - + /** - * Applies nice OpenGL materials using this color as the - * diffuse color while using different shades for the ambient and - * specular colors. This is only useful if lighting is enabled. + * returns the norm of the vector, that is, its length */ - void applyAsMaterials(); - }; - - /** - * Tests whether two Ts are approximately equal. Here T is assumed to be - * a floating-point type. Recall that operator== between floating-point - * types is broken. - * returns true if abs( a - b ) <= c * precision - * where c = max( abs( a ), abs( b ) ) - */ - template static bool approx_equal( T a, T b, T precision ) + inline T norm() const { return SQRT( 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; + } +}; + +/** +* Given a 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. +* v and w are not getting normalized. +*/ +template void construct_ortho_basis_given_first_vector( + const Vector3 &U, Vector3 & v, Vector3 & w ) +{ + if( U.norm() == 0 ) return; + + // let us first make a normalized copy of U + Vector3 u = U; + u.normalize(); + + // first we want to set v to be non-colinear to u + v = u; + + if( ! approx_equal( v.x, v.y, 0.1 ) ) { - T abs_a = FABS( a ); - T abs_b = FABS( b ); - - T max_abs; - if( abs_a <= abs_b ) - max_abs = abs_b; - else - max_abs = abs_a; - return( FABS( a - b ) <= precision * max_abs ); + T tmp = v.x; + v.x = v.y; + v.y = tmp; } - - /** - * 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 Vector3 + else if( ! approx_equal( v.y, v.z, 0.1 ) ) { - public: - T x, y, z; - Vector3() {} - Vector3( T _x, T _y, T _z) - { x = _x; y = _y; z = _z; } - - Vector3& operator= ( const Vector3& 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() const { return SQRT( 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; - } - }; + T 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 = Vector3( 0, 0, 1 ); + } - /** - * Given a 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. - * v and w are not getting normalized. - */ - template void construct_ortho_basis_given_first_vector( - const Vector3 &U, Vector3 & v, Vector3 & w ) - { - if( U.norm() == 0 ) return; - - // let us first make a normalized copy of U - Vector3 u = U; - u.normalize(); - - // first we want to set v to be non-colinear to u - v = u; - - if( ! approx_equal( v.x, v.y, 0.1 ) ) + // now, v is not colinear to u. We compute its dot product with u + T 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.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.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; +} + +/** +* This is an abstract base class for an OpenGL vertex array. +* +* @author Benoit Jacob +*/ +class VertexArray +{ + + protected: + GLenum m_mode; + Vector3 *m_vertexBuffer; + Vector3 *m_normalBuffer; + unsigned int m_vertexCount; + unsigned short *m_indexBuffer; + unsigned int m_indexCount; + + bool m_isInitialized; + + virtual void initialize() = 0; + virtual bool allocateBuffers(); + + public: + VertexArray(); + virtual ~VertexArray(); + virtual void select(); + virtual inline void draw() { - T tmp = v.x; - v.x = v.y; - v.y = tmp; + glDrawElements( m_mode, m_indexCount, + GL_UNSIGNED_SHORT, m_indexBuffer ); } - else if( ! approx_equal( v.y, v.z, 0.1 ) ) +}; + +/** +* This class represents and draws a sphere +* +* @author Benoit Jacob +*/ +class Sphere : public VertexArray +{ + private: + inline unsigned short indexOfVertex( + int strip, int column, int row); + void computeVertex( int strip, int column, int row ); + + protected: + int m_detail; + GLfloat m_radius; + + virtual void initialize(); + + public: + Sphere(); + virtual ~Sphere() {} + virtual void setup( int detail, GLfloat radius ); + virtual void drawScaled( GLfloat radius ); +}; + +/** +* This class represents and draws a cylinder +* +* @author Benoit Jacob +*/ +class Cylinder : public VertexArray +{ + protected: + int m_faces; + GLfloat m_radius; + + virtual void initialize(); + + public: + Cylinder(); + virtual ~Cylinder() {} + virtual void setup( int detail, GLfloat radius ); + virtual inline void draw() { - T 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 = Vector3( 0, 0, 1 ); + glDrawArrays( m_mode, 0, m_vertexCount ); } - - // now, v is not colinear to u. We compute its dot product with u - T 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.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.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; - } +}; - /** - * This is an abstract base class for an OpenGL vertex array. - * - * @author Benoit Jacob - */ - class VertexArray - { - - protected: - GLenum m_mode; - Vector3 *m_vertexBuffer; - Vector3 *m_normalBuffer; - unsigned int m_vertexCount; - unsigned short *m_indexBuffer; - unsigned int m_indexCount; +class TextPainter +{ + protected: + int m_width, m_height; + QImage *m_image; + QPainter *m_painter; + QFontMetrics *m_fontMetrics; + + public: + TextPainter() + { + m_width = 0; + m_height = 0; + m_image = 0; + m_painter = 0; + m_fontMetrics = 0; + } + ~TextPainter() + { + if( m_image ) delete m_image; + if( m_painter ) delete m_painter; + if( m_fontMetrics ) delete m_fontMetrics; + } + bool print( QGLWidget *glwidget, int x, int y, const QString &string) + { + glDisable( GL_LIGHTING ); + glEnable(GL_TEXTURE_2D); - bool m_isInitialized; + /*if( ! m_font ) + { + m_font = new QFont(); + if( ! m_font ) return false; + }*/ + + const QFont &m_font = (glwidget->font()); + + if( ! m_painter ) + { + m_painter = new QPainter(); + if( ! m_painter ) return false; + } + + m_painter->setFont(m_font); + + if( ! m_fontMetrics ) + { + + m_fontMetrics = new QFontMetrics(m_font); + if( ! m_fontMetrics ) return false; + } + + int new_width = m_fontMetrics->width( string ); + int new_height = m_fontMetrics->height(); - virtual void initialize() = 0; - virtual bool allocateBuffers(); - - public: - VertexArray(); - virtual ~VertexArray(); - virtual void select(); - virtual inline void draw() + if(new_width == 0 || new_height == 0) { - glDrawElements( m_mode, m_indexCount, - GL_UNSIGNED_SHORT, m_indexBuffer ); + return false; } - }; - - /** - * This class represents and draws a sphere - * - * @author Benoit Jacob - */ - class Sphere : public VertexArray - { - private: - inline unsigned short indexOfVertex( - int strip, int column, int row); - void computeVertex( int strip, int column, int row ); - - protected: - int m_detail; - GLfloat m_radius; - - virtual void initialize(); - - public: - Sphere(); - virtual ~Sphere() {} - virtual void setup( int detail, GLfloat radius ); - virtual void drawScaled( GLfloat radius ); - }; - - /** - * This class represents and draws a cylinder - * - * @author Benoit Jacob - */ - class Cylinder : public VertexArray - { - protected: - int m_faces; - GLfloat m_radius; - - virtual void initialize(); - - public: - Cylinder(); - virtual ~Cylinder() {} - virtual void setup( int detail, GLfloat radius ); - virtual inline void draw() + + if( new_width > m_width || new_height > m_height ) { - glDrawArrays( m_mode, 0, m_vertexCount ); + if( m_image ) delete m_image; + m_width = ( new_width > m_width ) ? new_width : m_width; + m_height = ( new_height > m_height ) ? new_height : m_height; + m_image = new QImage( m_width, m_height, QImage::Format_ARGB32 ); } - }; + + m_painter->begin( m_image ); + m_painter->setRenderHint(QPainter::TextAntialiasing); + //painter.setBackground(Qt::black); + m_painter->setBrush(Qt::white); + m_painter->eraseRect( 0, 0, m_width, m_height ); + + //painter.drawText ( 0, 0, s ); + m_painter->drawText ( 0, m_height, string ); + m_painter->end(); + + glwidget->bindTexture( *m_image ); + glMatrixMode( GL_PROJECTION ); + glPushMatrix(); + glLoadIdentity(); + glOrtho( 0, glwidget->width(), 0, glwidget->height(), -1, 1 ); + glMatrixMode( GL_MODELVIEW ); + glPushMatrix(); + glLoadIdentity(); + glBegin(GL_QUADS); + glTexCoord2f( 0, 0); + glVertex2f( x , y ); + glTexCoord2f( 1, 0); + glVertex2f( x+m_width , y ); + glTexCoord2f( 1, 1); + glVertex2f( x+m_width , y+m_height ); + glTexCoord2f( 0, 1); + glVertex2f( x , y+m_height ); + glEnd(); + glDisable( GL_TEXTURE_2D); + glPopMatrix(); + glMatrixMode( GL_PROJECTION ); + glPopMatrix(); + glMatrixMode( GL_MODELVIEW ); + return true; + } +}; } // namespace KalziumGL diff --git a/kalzium/kalziumglwidget.cpp b/kalzium/kalziumglwidget.cpp index e48baea..5e20605 100644 --- a/kalzium/kalziumglwidget.cpp +++ b/kalzium/kalziumglwidget.cpp @@ -22,6 +22,7 @@ #include #include + #ifdef USE_FPS_COUNTER #include #endif @@ -38,6 +39,7 @@ KalziumGLWidget::KalziumGLWidget( QWidget * parent ) m_molecule = 0; m_detail = 0; m_useFog = false; + m_textPainter = 0; ChooseStylePreset( PRESET_SPHERES_AND_BICOLOR_BONDS ); @@ -46,6 +48,7 @@ KalziumGLWidget::KalziumGLWidget( QWidget * parent ) KalziumGLWidget::~KalziumGLWidget() { + if ( m_textPainter) delete m_textPainter; } void KalziumGLWidget::initializeGL() @@ -92,6 +95,8 @@ void KalziumGLWidget::initializeGL() GL_SEPARATE_SPECULAR_COLOR_EXT ); setupObjects(); + + m_textPainter = new TextPainter; } void KalziumGLWidget::paintGL() @@ -227,6 +232,8 @@ void KalziumGLWidget::paintGL() // now, paint a semitransparent sphere around the selected atoms if( m_selectedAtoms.count() > 0 )//there are items selected { + m_sphere.select(); + Color c( 0.4, 0.4, 1.0, 0.7 ); GLFLOAT radius = m_molMinBondLength * 0.35; @@ -281,9 +288,8 @@ void KalziumGLWidget::paintGL() old_time = new_time; } - glDisable( GL_LIGHTING ); glColor3f( 1.0, 1.0, 0.0 ); - renderText ( 20, height() - 20, s ); + //m_textPainter->print( this, 20, 20, s ); update(); #endif diff --git a/kalzium/kalziumglwidget.h b/kalzium/kalziumglwidget.h index 5b60ec4..f18f1ed 100644 --- a/kalzium/kalziumglwidget.h +++ b/kalzium/kalziumglwidget.h @@ -32,6 +32,8 @@ class KalziumGLWidget : public QGLWidget Q_OBJECT protected: + TextPainter *m_textPainter; + /** * The geometric model of the sphere (used for atoms). */ -- 2.47.3