factor out viewport to display scaling functions
[mussa.git] / alg / glseqbrowser.cpp
index 254b845915233d48e2734e59ce6a88aef4d49128..df9462618b119f56c704a234a059d184ef2c8a4a 100644 (file)
@@ -1,6 +1,7 @@
 #include "alg/glseqbrowser.hpp"
 #include "mussa_exceptions.hpp"
 
+#include <math.h>
 #include <iostream>
 #include <sstream>
 #include <stdexcept>
@@ -25,7 +26,8 @@ GlSeqBrowser::GlSeqBrowser(const GlSeqBrowser& gt)
     viewport_center(gt.viewport_center),
     zoom_level(gt.zoom_level),
     color_mapper(gt.color_mapper),
-    track_container(gt.track_container)
+    track_container(gt.track_container),
+    path_segments(gt.path_segments)
 {
 }
 
@@ -124,6 +126,8 @@ void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize
           //TrackRegion track(objid, left, right); 
           track.set(objid, left, right);
           selected_tracks.push_back(track);
+          //clog << "selected track " << objid
+          //     << "(" << left << ", " << right << ")" << endl;
         }
         break;
         default:
@@ -140,10 +144,8 @@ void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize
 
 void GlSeqBrowser::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 = cur_ortho.left + (right * x_scale);
+  GLfloat x_left = viewportXtoWorldX(left);
+  GLfloat x_right = viewportXtoWorldX(right);cur_ortho.left;
 
   if (top > bottom) {
     // woah, someone gave us a rectangle with the origin in the lower left
@@ -152,8 +154,8 @@ void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right)
     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;
+  GLfloat y_top = viewportYtoWorldY(bottom);
+  GLfloat y_bottom = viewportYtoWorldY(top);
   selectedRegion = rect<float>(y_top, x_left, y_bottom, x_right);
 
   // hopefully this will make a buffer big enough to receive 
@@ -181,6 +183,13 @@ void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right)
   processSelection(hits, selectBuf, select_buf_size, selectedRegion);
 }
 
+void GlSeqBrowser::clearSelection()
+{
+  selected_paths.clear();
+  selected_tracks.clear();
+  selectedMode = false;  
+}
+
 float GlSeqBrowser::border() const
 {
   return border_width;
@@ -221,6 +230,15 @@ float GlSeqBrowser::right() const
   }
 }
 
+float GlSeqBrowser::get_pixel_width() const
+{
+  GLint viewport[4];
+  glGetIntegerv(GL_VIEWPORT, viewport);
+  GLint vp_width = viewport[3]; // grab the viewport width
+  
+  return round((cur_ortho.right-cur_ortho.left)/vp_width);
+}  
+
 void GlSeqBrowser::setViewportCenter(float x)
 {
   update_viewport(x, zoom_level);
@@ -252,6 +270,16 @@ float GlSeqBrowser::viewportWidth() const
   return cur_ortho.right - cur_ortho.left;
 }
 
+int GlSeqBrowser::viewportPixelHeight() const
+{
+  return viewport_size.y;
+}
+
+int GlSeqBrowser::viewportPixelWidth() const
+{
+  return viewport_size.x;
+}
+
 double GlSeqBrowser::zoomOut()
 {
 
@@ -285,14 +313,14 @@ double GlSeqBrowser::zoom() const
   return zoom_level;
 }
 
-void GlSeqBrowser::setColorMapper(boost::shared_ptr<AnnotationColors> cm)
+void GlSeqBrowser::setColorMapper(AnnotationColorsRef cm)
 {
   color_mapper = cm;
 }
 
-const AnnotationColors& GlSeqBrowser::colorMapper()
+const AnnotationColorsRef GlSeqBrowser::colorMapper()
 {
-  return *color_mapper;
+  return color_mapper;
 }
 
 void GlSeqBrowser::clear()
@@ -313,34 +341,38 @@ void GlSeqBrowser::clear_selection()
 
 void GlSeqBrowser::push_sequence(const Sequence& s)
 {
-  boost::shared_ptr<Sequence> seq_copy(new Sequence(s));
-  GlSequence gs(seq_copy, color_mapper);
+  GlSequenceRef gs(new GlSequence(s, color_mapper));
   push_sequence(gs);
 }
 
-
-void GlSeqBrowser::push_sequence(boost::shared_ptr<Sequence> s)
+void GlSeqBrowser::push_sequence(SequenceRef s)
 {
-  boost::shared_ptr<GlSequence> gs(new GlSequence(s, color_mapper));
+  GlSequenceRef gs(new GlSequence(*s, color_mapper));
   push_sequence(gs);
 }
 
 void GlSeqBrowser::push_sequence(GlSequence gs)
 {
-  boost::shared_ptr<GlSequence> new_gs(new GlSequence(gs));
+  GlSequenceRef new_gs(new GlSequence(gs));
   push_sequence(new_gs);
 }
 
-void GlSeqBrowser::push_sequence(boost::shared_ptr<GlSequence> gs)
+void GlSeqBrowser::push_sequence(GlSequenceRef gs)
 {
+  ColorRef default_color(GlSequence::default_gene_color());
+  GlSequenceRef new_gs(new GlSequence(gs));
+  new_gs->update_annotation_draw_function("gene", draw_narrow_track, default_color);
+  // mark where the sequence is
+  new_gs->add_annotations_for_defined_sequence(draw_summarized_track);
+  
   clear_links();
-  track_container.push_back(gs);
+  track_container.push_back(new_gs);
   update_layout();
   if (track_container.size() > 1)
     path_segments.push_back(pair_segment_map());
 }
 
-const std::vector<boost::shared_ptr<GlSequence> >& GlSeqBrowser::sequences() const
+const std::vector<GlSequenceRef >& GlSeqBrowser::sequences() const
 {
   return track_container;
 }
@@ -356,7 +388,7 @@ void GlSeqBrowser::clear_links()
 }
 
 void 
-GlSeqBrowser::link(const vector<int>& path, const vector<bool>& rc, int )
+GlSeqBrowser::link(const vector<int>& path, const vector<bool>& rc, int length)
 {
   if (path.size() < 2) {
     // should i throw an error instead?
@@ -388,12 +420,17 @@ GlSeqBrowser::link(const vector<int>& path, const vector<bool>& rc, int )
             y2 += track_container[track_i+1]->height()/2;
       
       bool rcFlag = (prev_rc or *rc_i) and !(prev_rc and *rc_i);
-      Segment s(prev_x, y1, *path_i, y2, rcFlag);
+      Segment s(prev_x, y1, *path_i, y2, rcFlag, length);
       s.path_ids.insert(pathid);
       path_segments[track_i][p] = s;
     } else {
       //found
       found_segment->second.path_ids.insert(pathid);
+      // make each segment the size of the largest of any link between these 
+      // two bases
+      if (found_segment->second.length < length) {
+        found_segment->second.length = length;
+      }
     }
     prev_x = *path_i;
     prev_rc = *rc_i;
@@ -433,11 +470,10 @@ list<TrackRegion> GlSeqBrowser::selectedTracks() const
 
 //! copy sequence from selected track using formating function
 template<class Item>
-void GlSeqBrowser::copySelectedTracks(std::list<Item>& result, 
-             Item (*formatter)(boost::shared_ptr<Sequence> s, 
-                               int left, 
-                               int right))
+size_t GlSeqBrowser::copySelectedTracks(std::list<Item>& result, 
+             Item (*formatter)(const Sequence& s, int left, int right))
 {
+  size_t base_pairs_copied = 0;
   result.clear();
 
   for(selected_track_iterator track_i = selected_tracks.begin();
@@ -451,30 +487,30 @@ void GlSeqBrowser::copySelectedTracks(std::list<Item>& result,
            << endl;
     } else {
       // we should be safe
-      boost::shared_ptr<Sequence> seq = track_container[track_index]->sequence();
+      Sequence seq(*track_container[track_index]);
       result.push_back(formatter(seq, track_i->left, track_i->right));
+      base_pairs_copied += max(track_i->right-track_i->left, 0);
     }
   }
+  return base_pairs_copied;
 }
 
 //! copy sequence from selected tracks as FASTA sequences
-void GlSeqBrowser::copySelectedTracksAsFasta(std::string& copy_buffer)
+size_t GlSeqBrowser::copySelectedTracksAsFasta(std::string& copy_buffer)
 {
   std::list<std::string> result;
   struct AsFasta {
-    static string formatter(boost::shared_ptr<Sequence> seq, 
-                            int left, 
-                            int right)
+    static string formatter(const Sequence& seq, int left, int right)
     {
       stringstream s;
-      s << ">" << seq->get_fasta_header() 
+      s << ">" << seq.get_fasta_header() 
         << "|" << "subregion=" << left << "-" << right+1
         << std::endl
-        << seq->subseq(left, right-left+1) << std::endl;
+        << seq.subseq(left, right-left+1) << std::endl;
       return s.str();
     }
   };
-  copySelectedTracks(result, AsFasta::formatter);
+  size_t base_pairs_copied = copySelectedTracks(result, AsFasta::formatter);
   // I wish there was some way to use for_each and bind here
   for (list<string>::iterator result_i = result.begin();
        result_i != result.end();
@@ -482,52 +518,53 @@ void GlSeqBrowser::copySelectedTracksAsFasta(std::string& copy_buffer)
   {
     copy_buffer.append(*result_i);
   }
+  return base_pairs_copied;
 }
 
 //! copy sequence from selected tracks as new sequences
-void GlSeqBrowser::copySelectedTracksAsSequences(std::list<Sequence>& result)
+size_t GlSeqBrowser::copySelectedTracksAsSequences(std::list<Sequence>& result)
 {
   struct AsSequence {
-    static Sequence formatter(boost::shared_ptr<Sequence> seq, 
+    static Sequence formatter(const Sequence& seq, 
                               int left, 
                               int right)
     {
-      return seq->subseq(left, right-left+1);
+      return seq.subseq(left, right-left+1);
     }
   };
-  copySelectedTracks(result, AsSequence::formatter);
+  return copySelectedTracks(result, AsSequence::formatter);
 }
 
-void GlSeqBrowser::copySelectedTracksAsSeqLocation(
+size_t GlSeqBrowser::copySelectedTracksAsSeqLocation(
     std::list<SequenceLocation>& result)
 {
   struct AsSeqLocation {
-    static SequenceLocation formatter(boost::shared_ptr<Sequence> seq, 
+    static SequenceLocation formatter(const Sequence& seq, 
                                       int left, 
                                       int right)
     {
       return SequenceLocation(seq, left, right);
     }
   };
-  copySelectedTracks(result, AsSeqLocation::formatter);
+  return copySelectedTracks(result, AsSeqLocation::formatter);
 }
 
 //! copy sequence from selected tracks as plain sequences
-void GlSeqBrowser::copySelectedTracksAsString(std::string& copy_buffer)
+size_t GlSeqBrowser::copySelectedTracksAsString(std::string& copy_buffer)
 {
   std::list<string> result;
   struct AsString {
-    static string formatter(boost::shared_ptr<Sequence> seq, 
+    static string formatter(const Sequence& seq, 
                             int left, 
                             int right)
     {
       stringstream s;
-      s << seq->subseq(left, right-left+1) << std::endl;
+      s << seq.subseq(left, right-left+1);
       return s.str();
     }
   };
 
-  copySelectedTracks(result, AsString::formatter);
+  size_t base_pairs_copied = copySelectedTracks(result, AsString::formatter);
   // I wish there was some way to use for_each and bind here
   for (list<string>::iterator result_i = result.begin();
        result_i != result.end();
@@ -535,7 +572,7 @@ void GlSeqBrowser::copySelectedTracksAsString(std::string& copy_buffer)
   {
     copy_buffer.append(*result_i);
   }
-
+  return base_pairs_copied;
 }
 
 void GlSeqBrowser::centerOnPath(const vector<int>& paths)
@@ -642,6 +679,9 @@ void GlSeqBrowser::draw_segments() const
   glLineWidth(1);
   glEnable(GL_BLEND);
   glDepthMask(GL_FALSE);
+  const float zdepth = -1.0;
+  const float min_segment_width = max((float)(1.0), get_pixel_width());
+  
   // each vector contains path_segment_maps of all the connections
   // between this track and the next
   path_segment_map_vector::const_iterator psmv_i;
@@ -669,33 +709,50 @@ void GlSeqBrowser::draw_segments() const
                        back_inserter(selected));
 
       if (not s.reversed) {
+        // forward
         if (selected_paths.size() == 0 or selected.size() > 0) {
           glColor4f(1.0, 0.0, 0.0, 1.0);
         } else {
           glColor4f(1.0, 0.7, 0.7, 0.4);
         }
       } else { 
+        // reverse
         if (selected_paths.size() == 0 or selected.size() > 0) {
           glColor4f(0.0, 0.0, 1.0, 1.0);
         } else {
           glColor4f(0.7, 0.7, 1.0, 0.4);
         }
-        /*
-        if (selected_paths.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);
-      float seq_start_x = track_container[path_index]->x();
-      float seq_end_x = track_container[path_index+1]->x();
-      glVertex3f(s.start.x + seq_start_x, s.start.y, -1);
-      glVertex3f(s.end.x   + seq_end_x  , s.end.y, -1);
-      glEnd();
+      float seq_start_x = s.start.x 
+                        + track_container[path_index]->x();
+      float seq_end_x = s.end.x
+                      + track_container[path_index+1]->x();
+      if (s.length <= min_segment_width) {
+        // use lines for elements of length <=1 or < 1 pixel.
+        // and try to center the line
+        const float offset = s.length * 0.5;
+        glBegin(GL_LINES);
+          glVertex3f(seq_start_x+offset, s.start.y, -1);
+          glVertex3f(seq_end_x  +offset, s.end.y, -1);
+        glEnd();
+      } else {
+        // otherwise use quads
+        // compute length
+        float seq_start_x_length = s.start.x 
+                                 + s.length
+                                 + track_container[path_index]->x();
+        float seq_end_x_length = s.end.x
+                               + s.length
+                               + track_container[path_index+1]->x();
+        glBegin(GL_QUADS);
+          glVertex3f(seq_start_x, s.start.y, zdepth);
+          glVertex3f(seq_end_x, s.end.y, zdepth);
+          glVertex3f(seq_end_x_length, s.end.y, zdepth);
+          glVertex3f(seq_start_x_length, s.start.y, zdepth);
+        glEnd();
+      }      
       // clear the names
       glPopName(); glPopName(); glPopName();
     }