re-implement selection
authorDiane Trout <diane@caltech.edu>
Thu, 16 Mar 2006 01:51:21 +0000 (01:51 +0000)
committerDiane Trout <diane@caltech.edu>
Thu, 16 Mar 2006 01:51:21 +0000 (01:51 +0000)
The new segment based paths are more challenging to try and track
which segment maps to which path, which makes highlighting stuff
a bit challenging.

But this mostly works. (other than ticket:21 hard coded selection buffer
size)

alg/gltracks.cpp
alg/gltracks.hpp
alg/test/test_gltracks.cpp [new file with mode: 0644]
qui/PathScene.cpp
qui/PathScene.hpp

index fb7df0b8363860eb3f7ce1e57dc686976ccc4a61..1d5e1e18ee3cdef5937bb12b8e0c387c3d5a7f59 100644 (file)
@@ -1,4 +1,5 @@
 #include "alg/gltracks.hpp"
+#include "mussa_exceptions.hpp"
 
 #include <iostream>
 #include <stdexcept>
@@ -62,6 +63,116 @@ void GlTracks::paintGL() const
   glFlush();
 }
 
+void GlTracks::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize)
+{
+  GLuint *ptr;
+  GLuint names;
+  GLuint consumed_names = 0;
+  float z1;
+  float z2;
+  GLuint objtype;
+  GLuint objid;
+  GLuint path_index = 0;
+  GLuint pair_key_0 = 0;
+  GLuint pair_key_1 = 0;
+
+  selectedPaths.clear();
+  selectedTracks.clear();
+
+  std::cout << "hits = " << hits << std::endl;
+  ptr = (GLuint *) buffer;
+  if (hits > 0)
+    selectedMode = true;
+  for (GLuint i=0; i < hits; ++i)
+  {
+    if ((i + 5) > bufsize) {
+      std::clog << "*** selection overflow***" << std::endl;
+    } else {
+      consumed_names = 0;
+      names = *ptr++;
+      z1 = ((float)*ptr++)/0x7fffffff;
+      z2 = ((float)*ptr++)/0x7fffffff;
+      objtype = *ptr++; ++consumed_names;
+      switch (objtype) {
+        case MussaSegment:
+          path_index = *ptr++; ++consumed_names;
+          pair_key_0 = *ptr++; ++consumed_names;
+          pair_key_1 = *ptr++; ++consumed_names;
+          if (path_index < path_segments.size()) {
+            segment_key k(pair_key_0, pair_key_1);
+            pair_segment_map::iterator psm_i;
+            psm_i = path_segments[path_index].find(k);
+            if (psm_i != path_segments[path_index].end()) {
+              Segment &seg = psm_i->second;
+              selectedPaths.insert(seg.path_ids.begin(), seg.path_ids.end());
+            }
+            // else something else is wrong
+          } else {
+            // something wasn't right
+            clog << "invalid path_index " << path_index 
+                 << " should have been [0,"<<path_segments.size()
+                 << ") " << endl;
+          }
+          break;
+        case MussaTrack:
+          objid = *ptr++; ++consumed_names;
+          selectedTracks.insert(objid);
+        break;
+        default:
+          cout << "unknown type " << objtype << " ";
+          for(; consumed_names < names; ++consumed_names) {
+            cout << consumed_names << "," << *ptr++ << " ";
+          }
+          cout << endl;
+          break;
+      }
+    }
+  }
+}
+
+void GlTracks::selectRegion(int top, int left, int bottom, int right)
+{
+  GLfloat x_scale = cur_ortho.width()/((float)viewport_size.x);
+  GLfloat y_scale = cur_ortho.height()/((float)viewport_size.y);
+  GLfloat x_left = cur_ortho.left + (left*x_scale);
+  GLfloat x_right = right * x_scale;
+
+  if (top > bottom) {
+    // woah, someone gave us a rectangle with the origin in the lower left
+    int temp = top;
+    bottom = top;
+    top = temp;
+  }
+  // swap the orientation of canvas coordinates
+  GLfloat y_top = cur_ortho.top-(bottom*y_scale);
+  GLfloat y_bottom = cur_ortho.top - top * y_scale;
+  selectedRegion = rect<float>(y_top, x_left, y_bottom, x_right);
+
+  // hopefully this will make a buffer big enough to receive 
+  // everything being selected
+  //const size_t pathz_count = mussaAnalysis->paths().refined_pathz.size();
+  //const GLuint select_buf_size = 1 + 5 * (pathz_count + tracks.size());
+  const GLuint select_buf_size = 500000;
+  GLuint selectBuf[select_buf_size];
+  glSelectBuffer(select_buf_size, selectBuf);
+  GLint hits;
+
+  (void)glRenderMode(GL_SELECT);
+  glPushMatrix();
+  glMatrixMode(GL_PROJECTION);
+  glLoadIdentity();
+  glOrtho(x_left, x_right, y_top, y_bottom, -50.0, 50.0);
+  glMatrixMode(GL_MODELVIEW);
+  glLoadIdentity();
+
+  draw();
+
+  glFlush();
+  glPopMatrix();
+  hits = glRenderMode(GL_RENDER);
+  processSelection(hits, selectBuf, select_buf_size);
+}
+
 float GlTracks::left() const
 { 
   return max_ortho.left; 
@@ -126,8 +237,8 @@ AnnotationColors& GlTracks::colorMapper()
 
 void GlTracks::clear()
 {
-  //clear_links();
-  //path_segments.clear();
+  clear_links();
+  path_segments.clear();
   track_container.clear();
 }
 
@@ -139,7 +250,7 @@ void GlTracks::push_sequence(const Sequence &s)
 
 void GlTracks::push_sequence(GlSequence &gs)
 {
-  //clear_links();
+  clear_links();
   pathid = 0;
   track_container.push_back(gs);
   update_layout();
@@ -190,11 +301,11 @@ GlTracks::link(vector<int> path, vector<bool> rc, int length)
             y2 += track_container[track_i+1].height()/2;
             
       Segment s(prev_x, y1, *path_i, y2, prev_rc);
-      s.path_ids.push_back(pathid);
+      s.path_ids.insert(pathid);
       path_segments[track_i][p] = s;
     } else {
       //found
-      found_segment->second.path_ids.push_back(pathid);
+      found_segment->second.path_ids.insert(pathid);
     }
     prev_x = *path_i;
     prev_rc = *rc_i;
@@ -259,44 +370,43 @@ void GlTracks::update_layout()
 void GlTracks::draw() const
 {
   glMatrixMode(GL_MODELVIEW);
-  //glInitNames();
-  //glPushName(MussaPaths);
+  glInitNames();
+  glPushName(MussaSegment);
   draw_segments();
+  glLoadName(MussaTrack);
   draw_tracks();
+  glPopName();
+  // a selection shouldn't have a glName associated with it
   draw_selection();
-  //glPopName();
 }
 
 void GlTracks::draw_selection() const
 {
-  /* draw selection box
+  // draw selection box
   glEnable(GL_BLEND);
   glDepthMask(GL_FALSE);
-  if (selectedMode && !drawingBand) {
+  if (selectedMode) {
     glColor4f(0.6, 0.6, 0.6, 0.9);
-    glRectf(previousBand.x(), previousBand.y(),
-            previousBand.right(), previousBand.bottom());
+    glRectf(selectedRegion.left, selectedRegion.top, 
+            selectedRegion.right, selectedRegion.bottom);
   }
   glDepthMask(GL_TRUE);
   glDisable(GL_BLEND);
-  */
 }
 
 void GlTracks::draw_tracks() const
 {
-  typedef std::vector<GlSequence>::const_iterator glseq_citor_type;
-  for(glseq_citor_type seq_i = track_container.begin();
-      seq_i != track_container.end();
-      ++seq_i)
+  for(size_t track_i = 0; track_i != track_container.size(); ++track_i)
   {
-    seq_i->draw(cur_ortho.left, cur_ortho.right);
+    glPushName(track_i);
+    track_container[track_i].draw(cur_ortho.left, cur_ortho.right);
+    glPopName();
   }
 }
 
 void GlTracks::draw_segments() const
 {
   glLineWidth(0.1);
-  glBegin(GL_LINES);
   // each vector contains path_segment_maps of all the connections
   // between this track and the next
   path_segment_map_vector::const_iterator psmv_i;
@@ -304,6 +414,8 @@ void GlTracks::draw_segments() const
       psmv_i != path_segments.end();
       ++psmv_i)
   {
+    path_segment_map_vector::difference_type path_index;
+    path_index = psmv_i - path_segments.begin();
     // these maps contain the pair index (used so we dont keep drawing the
     // same segment) and the actual segment structure.
     pair_segment_map::const_iterator psm_i;
@@ -311,17 +423,37 @@ void GlTracks::draw_segments() const
         psm_i != psmv_i->end();
         ++psm_i)
     {
+      // grab the index into our segment map
+      const segment_key& key = psm_i->first;
       // the second element of our map pair is a segment
       const Segment &s = psm_i->second;
       // need to do something so we can detect our selection
+      vector<int> selected;
+      set_intersection(selectedPaths.begin(), selectedPaths.end(),
+                       s.path_ids.begin(), s.path_ids.end(),
+                       back_inserter(selected));
+
       if (not s.reversed) {
-        glColor3f(1.0, 0.0, 0.0);
+        if (selectedPaths.size() == 0 or selected.size() > 0) {
+          glColor3f(1.0, 0.0, 0.0);
+        } else {
+          glColor3f(1.0, 0.8, 0.8);
+        }
       } else { 
-        glColor3f(0.0, 0.0, 1.0);
+        if (selectedPaths.size() == 0 or selected.size() > 0) {
+          glColor3f(0.0, 0.0, 1.0);
+        } else {
+          glColor3f(0.8, 0.8, 1.0);
+        }
       }
+      // save the multipart name for our segment
+      glPushName(path_index); glPushName(key.first); glPushName(key.second);
+      glBegin(GL_LINES);
       glVertex3f(s.start.x, s.start.y, -1);
       glVertex3f(s.end.x, s.end.y, -1);
+      glEnd();
+      // clear the names
+      glPopName(); glPopName(); glPopName();
     }
   }
-  glEnd();
 }
index 6df83ef5d6f2681d93dc187e9305c825dd4c009f..2a88affca6a06add94749b72f36885d75feeac2f 100644 (file)
@@ -2,6 +2,7 @@
 #define _GLTRACKS_H_
 
 #include <map>
+#include <set>
 #include <vector>
 
 #include "alg/annotation_colors.hpp"
@@ -22,6 +23,9 @@ public:
   //! render our scene
   void paintGL() const;
 
+  //! select a region (using canvas coordinates)
+  void GlTracks::selectRegion(int top, int left, int bottom, int right);
+
   //! max world left coordinate
   float left() const;
   //! max world right coordinate
@@ -55,6 +59,9 @@ public:
   //! define a path
   void link(std::vector<int> path, std::vector<bool> isRC, int length);
 
+  //! Provide a logical name for a type discriminator for our glName stack
+  enum FeatureType { MussaTrack, MussaSegment };
+
   //! a useful point class
   template<class T> struct point {
     T x;
@@ -70,6 +77,7 @@ public:
     T bottom;
     T right;
 
+    rect() {}
     rect(T t, T l, T b, T r) : top(t), left(l), bottom(b), right(r) {}
     T width() { return right - left; }
     T height() { return top - bottom; }
@@ -80,7 +88,7 @@ public:
     point<float> start;
     point<float> end;
     bool reversed;
-    std::list<int> path_ids;
+    std::set<int> path_ids;
 
     Segment() : start(0.0, 0.0), end(0.0, 0.0) {}
     Segment(float x1, float y1, float x2, float y2, bool isRC) 
@@ -106,6 +114,9 @@ private:
   //! determine where all the tracks should be placed
   void update_layout();
 
+  //! convert opengl selections into the list of paths we should highlight
+  void processSelection(GLuint hits, GLuint buffer[], GLuint bufsize);
+
   //! master scene drawing function
   /*! draw is broken out for the opengl selection code
    */
@@ -133,5 +144,16 @@ private:
   std::vector<GlSequence> track_container;
   //! counter for each path added to us via connect
   int pathid;
+
+protected:
+  //! where to draw our box 
+  rect<float> selectedRegion;
+  //! true if we have a selection
+  bool selectedMode;
+  //! indicate which paths are selected
+  std::set<int> selectedPaths;
+  //! which track is selected (it only makes sense to have one track selected).
+  std::set<int> selectedTracks;
+
 };
 #endif
diff --git a/alg/test/test_gltracks.cpp b/alg/test/test_gltracks.cpp
new file mode 100644 (file)
index 0000000..58b91fc
--- /dev/null
@@ -0,0 +1,47 @@
+#include <boost/test/auto_unit_test.hpp>
+#include <boost/assign/std/vector.hpp>
+using namespace boost::assign;
+
+#include <string>
+#include <list>
+#include <vector>
+
+#include "alg/annotation_colors.hpp"
+#include "alg/gltracks.hpp"
+#include "alg/sequence.hpp"
+
+using namespace std;
+
+BOOST_AUTO_TEST_CASE ( gltracks_connect )
+{
+  string s0("AAGGCCTT");
+  string s1("TTGGCCAA");
+  string s2("GATTACAA");
+  Sequence seq0(s0);
+  Sequence seq1(s1);
+  Sequence seq2(s2);
+
+  GlTracks gt;
+  gt.push_sequence(seq0);
+  gt.push_sequence(seq1);
+  gt.push_sequence(seq2);
+
+  // make up some sample data
+  vector<bool> rc;
+  rc += false, false, false;
+  vector<int> path;
+                path += 1,1,1; gt.link(path, rc, 1);
+  path.clear(); path += 1,1,3; gt.link(path, rc, 1); 
+  path.clear(); path += 2,3,3; gt.link(path, rc, 1); 
+  path.clear(); path += 3,3,3; gt.link(path, rc, 1);
+
+  BOOST_CHECK_EQUAL( gt.path_segments.size(), 2 );
+  GlTracks::segment_key p(1, 1);
+  GlTracks::pair_segment_map::iterator psm_i = gt.path_segments[0].find(p);
+  BOOST_CHECK( psm_i != gt.path_segments[0].end() );
+  BOOST_CHECK_EQUAL( psm_i->second.path_ids.size(), 2 );
+
+  gt.clear();
+  BOOST_CHECK_EQUAL( gt.path_segments.size(), 0 );
+}
+
index b9c3637dcb997ddeb1f329927768571b54043af2..ccbd7e0f0e758ea6686f908aa9fef01f0324d651 100644 (file)
 using namespace std;
 
 PathScene::PathScene(QWidget *parent)
-  : QGLWidget(parent)
-    //selectedMode(false),
-    //rubberBand(0),
-    //drawingBand(false)
+  : QGLWidget(parent),
+    rubberBand(0),
+    drawingBand(false)
 { 
 }
 
 QSize PathScene::sizeHint() const
 {
-  return QSize(GlTracks::viewportHeight(), GlTracks::viewportWidth());
+  return QSize((int)GlTracks::viewportHeight(), (int)GlTracks::viewportWidth());
 }
 
 void PathScene::setViewportCenter(float x)
@@ -86,64 +85,9 @@ void PathScene::paintGL()
   GlTracks::paintGL();
 }
 
-/*
-void PathScene::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize)
-{
-  const size_t pathz_count = mussaAnalysis->paths().refined_pathz.size();
-  GLuint *ptr;
-  GLuint names;
-  float z1;
-  float z2;
-  GLuint objtype;
-  GLuint objid;
-
-  selectedPaths.clear();
-  selectedPaths.reserve(pathz_count);
-  selectedPaths.insert(selectedPaths.begin(), pathz_count, false);
-  selectedTrack = 0x7fffffff;
-  
-  std::cout << "hits = " << hits << " " << pathz_count << std::endl;
-  ptr = (GLuint *) buffer;
-  if (hits > 0)
-    selectedMode = true;
-  for (GLuint i=0; i < hits; ++i)
-  {
-    if ((i + 5) > bufsize) {
-      std::clog << "*** selection overflow***" << std::endl;
-    } else {
-      names = *ptr++;
-      z1 = ((float)*ptr++)/0x7fffffff;
-      z2 = ((float)*ptr++)/0x7fffffff;
-      objtype = *ptr++;
-      objid = *ptr++;
-      if (names != 2) {
-        std::clog << "wrong number of glNames, selection is having trouble ";
-        std::clog << " got " << names << std::endl;
-        std::clog << " names: " ;
-        for (GLuint j = 0 ; j < names-2; ++j) {
-          std::clog << *ptr++ << " ";
-        }
-        std::clog << endl;
-        return;
-      } 
-      switch (objtype) {
-        case MussaPaths:
-          selectedPaths[objid] = true;
-        break;
-        case MussaTracks:
-          if (objid < selectedTrack) {
-            selectedTrack = objid; 
-          }
-        break;
-      }
-    }
-  }
-}
-
 void PathScene::mousePressEvent( QMouseEvent *e)
 {
   drawingBand = true;
-  std::cout << "x=" << e->x() << " y=" << e->y() << std::endl;
 
   selectedMode = false;
   bandOrigin = e->pos();
@@ -160,58 +104,12 @@ void PathScene::mouseMoveEvent( QMouseEvent *e)
     rubberBand->setGeometry(QRect(bandOrigin, e->pos()).normalized());
 }
 
-void dumpRect(const QRect &r)
-{
-  std::cout << "x=" << r.x() << " y=" << r.y() 
-            << " w=" << r.width() << " h=" << r.height() << std::endl;
-}
-
 void PathScene::mouseReleaseEvent( QMouseEvent *e)
 {
   drawingBand = false;
   rubberBand->hide();
   QRect r = QRect(bandOrigin, e->pos()).normalized();
   bandOrigin = r.topLeft();
-  std::cout << "band ";
-  dumpRect(r);
-
-  std::cout << "window ";
-  dumpRect(geometry());
-
-  GLfloat x_scale = curOrtho2d.width()/((float)geometry().width());
-  GLfloat y_scale = curOrtho2d.height()/((float)geometry().height());
-  GLfloat x_left = curOrtho2d.left() + (r.x()*x_scale);
-  GLfloat x_right = x_left + r.width() * x_scale;
-  // using QRectF with Y axis swapped
-  GLfloat y_top = curOrtho2d.bottom()-(r.y()*y_scale);
-  GLfloat y_bottom = y_top - r.height() * y_scale;
-  previousBand.setCoords(x_left, y_top, x_right, y_bottom);
-  std::cout << x_left << " " << x_right << " " << y_top << " " << y_bottom 
-            << std::endl;
-
-  // hopefully this will make a buffer big enough to receive 
-  // everything being selected
-  const size_t pathz_count = mussaAnalysis->paths().refined_pathz.size();
-  const GLuint select_buf_size = 1 + 5 * (pathz_count + tracks.size()) ;
-  GLuint selectBuf[select_buf_size];
-  glSelectBuffer(select_buf_size, selectBuf);
-  GLint hits;
-
-  (void)glRenderMode(GL_SELECT);
-  glPushMatrix();
-  glMatrixMode(GL_PROJECTION);
-  glLoadIdentity();
-  glOrtho(x_left, x_right, y_top, y_bottom, -50.0, 50.0);
-  glMatrixMode(GL_MODELVIEW);
-  glLoadIdentity();
-  mussaesque();
-  glFlush();
-
-  glPopMatrix();
-  hits = glRenderMode(GL_RENDER);
-  processSelection(hits, selectBuf, select_buf_size);
-
-  resizeGL(geometry().width(), geometry().height());
+
+  selectRegion(r.top(), r.left(), r.bottom(), r.right());
 }
-*/
index 46c905ae7ffe20468d6001bcd82749de75fe0ab8..7433907df35a909db215c331ca9cee6a25b907f5 100644 (file)
@@ -38,32 +38,16 @@ signals:
   void viewportChanged();
 
 private:
-  //GlTracks tracks_view;
-
   void initializeGL();
   void resizeGL(int height, int width);
   void paintGL();
 
-  //! true if we have a selection
-  bool selectedMode;
-  //! indicate which paths are selected
-  std::vector<bool> selectedPaths;
-  //! which track is selected (it only makes sense to have one track selected).
-  unsigned int selectedTrack;
-
-  //! convert opengl selections into the list of paths we should highlight
-  void processSelection(GLuint hits, GLuint buffer[], GLuint bufsize);
-  //! Provide a logical name for a type discriminator for our glName stack
-  enum FeatureType { MussaTracks, MussaPaths };
-
   //! \defgroup Selection
-  //QRubberBand *rubberBand;
-  //QPoint bandOrigin;
-  //QRectF previousBand;
-  //bool drawingBand;
-  //void mousePressEvent(QMouseEvent *);
-  //void mouseMoveEvent(QMouseEvent *);
-  //void mouseReleaseEvent(QMouseEvent *);
-
+  QRubberBand *rubberBand;
+  QPoint bandOrigin;
+  bool drawingBand;
+  void mousePressEvent(QMouseEvent *);
+  void mouseMoveEvent(QMouseEvent *);
+  void mouseReleaseEvent(QMouseEvent *);
 };
 #endif