From 8c2c611a8202d99c4967af144358d40c3cdf120c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Beno=C3=AEt=20Jacob?= Date: Tue, 25 Jul 2006 13:18:32 +0000 Subject: [PATCH] Added OpenGL selection code. Single left click on an atom now selects/deselects it, if the mouse didn't move between button press and button release. Some tolerance (2 pixels) added to make that usable with imprecise pointing devices. TODO: notify the treeview (currently it doesn't get updated). M kalzium/src/kalziumglwidget.h M kalzium/src/kalziumglhelperclasses.h M kalzium/src/kalziumglwidget.cpp M kalzium/src/kalziumglhelperclasses.cpp svn path=/trunk/KDE/kdeedu/kalzium/src/kalziumglwidget.h; revision=566121 --- kalzium/kalziumglhelperclasses.cpp | 32 +++++++ kalzium/kalziumglhelperclasses.h | 4 + kalzium/kalziumglwidget.cpp | 142 ++++++++++++++++++++++++++--- kalzium/kalziumglwidget.h | 14 ++- 4 files changed, 180 insertions(+), 12 deletions(-) diff --git a/kalzium/kalziumglhelperclasses.cpp b/kalzium/kalziumglhelperclasses.cpp index 6b250f0..1bb854b 100644 --- a/kalzium/kalziumglhelperclasses.cpp +++ b/kalzium/kalziumglhelperclasses.cpp @@ -657,4 +657,36 @@ bool KalziumGLHelpers::createOrthoBasisGivenFirstVector w.normalize(); return true; } +/* +void LinearRegression( const std::list & points, + vector3 & ret_plane_base_point, vector3 & 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; + } + +}*/ diff --git a/kalzium/kalziumglhelperclasses.h b/kalzium/kalziumglhelperclasses.h index c5a546b..e027b21 100644 --- a/kalzium/kalziumglhelperclasses.h +++ b/kalzium/kalziumglhelperclasses.h @@ -159,6 +159,10 @@ struct Color */ bool createOrthoBasisGivenFirstVector( const OpenBabel::vector3 &U, OpenBabel::vector3 & v, OpenBabel::vector3 & 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 51a09e7..2c7d3d1 100644 --- a/kalzium/kalziumglwidget.cpp +++ b/kalzium/kalziumglwidget.cpp @@ -33,7 +33,8 @@ using namespace OpenBabel; KalziumGLWidget::KalziumGLWidget( QWidget * parent ) : QGLWidget( parent ) { - m_isDragging = false; + m_isLeftButtonPressed = false; + m_movedSinceLeftButtonPressed = false; m_molecule = 0; m_detail = 0; m_displayList = 0; @@ -110,13 +111,57 @@ void KalziumGLWidget::paintGL() return; } + renderScene(); + +#ifdef USE_FPS_COUNTER + FPSCounter(); + update(); +#endif +} + +void KalziumGLWidget::renderScene( GLenum renderMode, + const QPoint *mousePosition, + GLsizei selectionBufferSize, + GLuint *selectionBuffer, + GLint *numberOfHits ) +{ + // if renderMode is not GL_RENDER, check that it is GL_SELECT and that + // the required arguments have been passed + if( renderMode != GL_RENDER ) + { + if( renderMode != GL_SELECT + || ! mousePosition + || ! selectionBufferSize + || ! selectionBuffer + || ! numberOfHits ) return; + } + + if( renderMode == GL_SELECT ) + { + glSelectBuffer( selectionBufferSize, selectionBuffer ); + glRenderMode( GL_SELECT ); + } + + // set up the projection matrix glMatrixMode( GL_PROJECTION ); glLoadIdentity(); + if( renderMode == GL_SELECT ) + { + // in GL_SELECT mode, we only want to render a tiny area around + // the mouse pointer + GLint viewport[4]; + glGetIntegerv( GL_VIEWPORT, viewport ); + gluPickMatrix( mousePosition->x(), + viewport[3] - mousePosition->y(), + 3, 3, viewport); + } gluPerspective( 40.0, float( width() ) / height(), getMolRadius(), 5.0 * getMolRadius() ); glMatrixMode( GL_MODELVIEW ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + // clear the buffers when in GL_RENDER mode + if( renderMode == GL_RENDER ) + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // set up the camera glLoadIdentity(); @@ -124,7 +169,7 @@ void KalziumGLWidget::paintGL() glMultMatrixd ( m_RotationMatrix ); // set up fog - if( m_useFog ) + if( m_useFog && renderMode == GL_RENDER ) { glEnable( GL_FOG ); GLfloat fogColor[] = { 0.0, 0.0, 0.0, 1.0 }; @@ -136,6 +181,13 @@ void KalziumGLWidget::paintGL() } else glDisable( GL_FOG ); + // initialize the name stack when in GL_SELECT mode + if( renderMode == GL_SELECT ) + { + glInitNames(); + glPushName( 0 ); + } + #ifdef USE_DISPLAY_LISTS if( m_haveToRecompileDisplayList ) { @@ -153,12 +205,14 @@ void KalziumGLWidget::paintGL() } glCallList( m_displayList ); #endif + renderSelection(); -#ifdef USE_FPS_COUNTER - FPSCounter(); - update(); -#endif + if( renderMode == GL_SELECT ) + { + glFlush(); + *numberOfHits = glRenderMode( GL_RENDER ); + } } void KalziumGLWidget::renderAtoms() @@ -197,6 +251,7 @@ void KalziumGLWidget::renderSelection() glEnable( GL_BLEND ); foreach(OpenBabel::OBAtom* atom, m_selectedAtoms) { + glLoadName( atom->GetIdx() ); m_sphere.draw( atom->GetVector(), 0.18 + m_molStyle.getAtomRadius( atom ) ); } @@ -249,8 +304,11 @@ void KalziumGLWidget::mousePressEvent( QMouseEvent * event ) { if( event->buttons() & Qt::LeftButton ) { - m_isDragging = true; + m_isLeftButtonPressed = true; + m_movedSinceLeftButtonPressed = false; m_lastDraggingPosition = event->pos (); + m_initialDraggingPosition = event->pos (); + } } @@ -258,17 +316,35 @@ void KalziumGLWidget::mouseReleaseEvent( QMouseEvent * event ) { if( !( event->buttons() & Qt::LeftButton ) ) { - m_isDragging = false; - updateGL(); + m_isLeftButtonPressed = false; + + if( ! m_movedSinceLeftButtonPressed ) + { + OBAtom *atomUnderMouse = + getAtomUnderMouse( event->pos() ); + if( atomUnderMouse ) + { + if( m_selectedAtoms.contains( atomUnderMouse ) ) + { + m_selectedAtoms.removeAll( + atomUnderMouse ); + } + else m_selectedAtoms.append( atomUnderMouse ); + } + updateGL(); + } } } void KalziumGLWidget::mouseMoveEvent( QMouseEvent * event ) { - if( m_isDragging ) + if( m_isLeftButtonPressed ) { QPoint deltaDragging = event->pos() - m_lastDraggingPosition; m_lastDraggingPosition = event->pos(); + if( ( event->pos() + - m_initialDraggingPosition ).manhattanLength() > 2 ) + m_movedSinceLeftButtonPressed = true; glPushMatrix(); glLoadIdentity(); @@ -339,6 +415,7 @@ void KalziumGLWidget::setupObjects() void KalziumGLWidget::drawAtom( OBAtom *atom ) { + glLoadName( atom->GetIdx() ); Color( atom ).applyAsMaterials(); m_sphere.draw( atom->GetVector(), m_molStyle.getAtomRadius( atom ) ); } @@ -366,15 +443,18 @@ void KalziumGLWidget::drawBond( OBBond *bond ) switch( m_molStyle.m_bondStyle ) { case MolStyle::BONDS_GRAY: + glLoadName( 0 ); Color( 0.55, 0.55, 0.55 ).applyAsMaterials(); m_cylinder.draw( v1, v2, radius, order, m_molStyle.m_multipleBondShift ); break; case MolStyle::BONDS_USE_ATOMS_COLORS: + glLoadName( atom1->GetIdx() ); Color( atom1 ).applyAsMaterials(); m_cylinder.draw( v1, v3, radius, order, m_molStyle.m_multipleBondShift ); + glLoadName( atom2->GetIdx() ); Color( atom2 ).applyAsMaterials(); m_cylinder.draw( v2, v3, radius, order, m_molStyle.m_multipleBondShift ); @@ -489,4 +569,44 @@ void KalziumGLWidget::slotAtomsSelected( QList atoms ) updateGL(); } +OpenBabel::OBAtom * KalziumGLWidget::getAtomUnderMouse( + const QPoint & mousePosition ) +{ + if( ! m_molecule ) return 0; + + const GLsizei selectionBufferSize = 1024; + GLuint selectionBuffer[selectionBufferSize]; + GLint numberOfHits; + + renderScene( GL_SELECT, + &mousePosition, + selectionBufferSize, + selectionBuffer, + &numberOfHits ); + + unsigned int i, j; + GLuint names, *ptr = selectionBuffer, + minZ = 0xffffffff, + *ptrNames, + numberOfNames = 0; + printf ("hits = %d\n", numberOfHits); + for( i = 0; i < numberOfHits; i++ ) + { + names = *ptr; + ptr++; + if( *ptr < minZ ) + { + numberOfNames = names; + minZ = *ptr; + ptrNames = ptr+2; + } + ptr += names+2; + } + + for( j = 0, ptr = ptrNames; j < numberOfNames; j++, ptr++ ) + if( *ptr ) return m_molecule->GetAtom( *ptr ); + + return 0; +} + #include "kalziumglwidget.moc" diff --git a/kalzium/kalziumglwidget.h b/kalzium/kalziumglwidget.h index 10b7e1f..f5f04b9 100644 --- a/kalzium/kalziumglwidget.h +++ b/kalzium/kalziumglwidget.h @@ -50,9 +50,12 @@ class KalziumGLWidget : public QGLWidget * equals true if the user is currently dragging (rotating) * the view */ - bool m_isDragging; + bool m_isLeftButtonPressed; + + bool m_movedSinceLeftButtonPressed; QPoint m_lastDraggingPosition; + QPoint m_initialDraggingPosition; /** * Stores the rotation that is applied to the model. @@ -217,6 +220,12 @@ class KalziumGLWidget : public QGLWidget void drawBond( OpenBabel::OBBond *bond ); + void renderScene( GLenum renderMode = GL_RENDER, + const QPoint * mousePosition = 0, + GLsizei selectionBufferSize = 0, + GLuint * selectionBuffer = 0, + GLint * numberOfHits = 0 ); + /** * recomputes the geometry of the geometric objects ( sphere, * cylinder ). @@ -231,6 +240,9 @@ class KalziumGLWidget : public QGLWidget * @param style the wanted molecule style */ void setMolStyle( int style ); + + OpenBabel::OBAtom * getAtomUnderMouse( + const QPoint & mousePosition ); }; #endif // KALZIUMGLWIDGET_H -- 2.47.3