refactored PathScene into SequenceBrowserWidget
[mussa.git] / alg / glseqbrowser.cpp
1 #include "alg/glseqbrowser.hpp"
2 #include "mussa_exceptions.hpp"
3
4 #include <iostream>
5 #include <stdexcept>
6
7 using namespace std;
8
9 GlSeqBrowser::GlSeqBrowser()
10   : border(25),
11     max_ortho(400.0, 0.0, 600.0, 0.0),
12     cur_ortho(max_ortho),
13     viewport_size(600, 400),
14     viewport_center(0),
15     zoom_level(2),
16     color_mapper(),
17     track_container()
18 {
19 }
20
21 GlSeqBrowser::GlSeqBrowser(const GlSeqBrowser& gt)
22   : border(gt.border),
23     max_ortho(gt.max_ortho),
24     cur_ortho(gt.cur_ortho),
25     viewport_size(gt.viewport_size),
26     viewport_center(gt.viewport_center),
27     zoom_level(gt.zoom_level),
28     color_mapper(gt.color_mapper),
29     track_container(gt.track_container)
30 {
31 }
32
33 void GlSeqBrowser::initializeGL()
34 {
35   glEnable(GL_DEPTH_TEST);
36   glClearColor(1.0, 1.0, 1.0, 0.0);
37   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
38   glShadeModel(GL_FLAT);
39 }
40
41 void GlSeqBrowser::resizeGL(int width, int height)
42 {
43   viewport_size.x = width;
44   viewport_size.y = height;
45   glViewport(0, 0, (GLsizei)width, (GLsizei)height);
46   //update_layout();
47 }
48
49 void GlSeqBrowser::paintGL() const
50 {
51   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
52
53   glPushMatrix();
54   glMatrixMode(GL_PROJECTION);
55   glLoadIdentity();
56   glOrtho(cur_ortho.left, cur_ortho.right,
57           cur_ortho.bottom, cur_ortho.top,
58           -50.0, 50);
59
60   draw();
61
62   glPopMatrix();
63   glFlush();
64 }
65
66 void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize)
67 {
68   GLuint *ptr;
69   GLuint names;
70   GLuint consumed_names = 0;
71   float z1;
72   float z2;
73   GLuint objtype;
74   GLuint objid;
75   GLuint path_index = 0;
76   GLuint pair_key_0 = 0;
77   GLuint pair_key_1 = 0;
78
79   selectedPaths.clear();
80   selectedTracks.clear();
81
82   std::cout << "hits = " << hits << std::endl;
83   ptr = (GLuint *) buffer;
84   if (hits > 0)
85     selectedMode = true;
86   for (GLuint i=0; i < hits; ++i)
87   {
88     if ((i + 5) > bufsize) {
89       std::clog << "*** selection overflow***" << std::endl;
90     } else {
91       consumed_names = 0;
92       names = *ptr++;
93       z1 = ((float)*ptr++)/0x7fffffff;
94       z2 = ((float)*ptr++)/0x7fffffff;
95       objtype = *ptr++; ++consumed_names;
96       switch (objtype) {
97         case MussaSegment:
98           path_index = *ptr++; ++consumed_names;
99           pair_key_0 = *ptr++; ++consumed_names;
100           pair_key_1 = *ptr++; ++consumed_names;
101           if (path_index < path_segments.size()) {
102             segment_key k(pair_key_0, pair_key_1);
103             pair_segment_map::iterator psm_i;
104             psm_i = path_segments[path_index].find(k);
105             if (psm_i != path_segments[path_index].end()) {
106               Segment &seg = psm_i->second;
107               selectedPaths.insert(seg.path_ids.begin(), seg.path_ids.end());
108             }
109             // else something else is wrong
110           } else {
111             // something wasn't right
112             clog << "invalid path_index " << path_index 
113                  << " should have been [0,"<<path_segments.size()
114                  << ") " << endl;
115           }
116           break;
117         case MussaTrack:
118           objid = *ptr++; ++consumed_names;
119           selectedTracks.insert(objid);
120         break;
121         default:
122           cout << "unknown type " << objtype << " ";
123           for(; consumed_names < names; ++consumed_names) {
124             cout << consumed_names << "," << *ptr++ << " ";
125           }
126           cout << endl;
127           break;
128       }
129     }
130   }
131 }
132
133 void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right)
134 {
135   GLfloat x_scale = cur_ortho.width()/((float)viewport_size.x);
136   GLfloat y_scale = cur_ortho.height()/((float)viewport_size.y);
137   GLfloat x_left = cur_ortho.left + (left*x_scale);
138   GLfloat x_right = right * x_scale;
139
140   if (top > bottom) {
141     // woah, someone gave us a rectangle with the origin in the lower left
142     int temp = top;
143     bottom = top;
144     top = temp;
145   }
146   // swap the orientation of canvas coordinates
147   GLfloat y_top = cur_ortho.top-(bottom*y_scale);
148   GLfloat y_bottom = cur_ortho.top - top * y_scale;
149   selectedRegion = rect<float>(y_top, x_left, y_bottom, x_right);
150
151   // hopefully this will make a buffer big enough to receive 
152   // everything being selected
153   //const size_t pathz_count = mussaAnalysis->paths().refined_pathz.size();
154   //const GLuint select_buf_size = 1 + 5 * (pathz_count + sequences.size());
155   const GLuint select_buf_size = 500000;
156   GLuint selectBuf[select_buf_size];
157   glSelectBuffer(select_buf_size, selectBuf);
158   GLint hits;
159
160   (void)glRenderMode(GL_SELECT);
161   glPushMatrix();
162   glMatrixMode(GL_PROJECTION);
163   glLoadIdentity();
164   glOrtho(x_left, x_right, y_top, y_bottom, -50.0, 50.0);
165   glMatrixMode(GL_MODELVIEW);
166   glLoadIdentity();
167
168   draw();
169
170   glFlush();
171   glPopMatrix();
172   hits = glRenderMode(GL_RENDER);
173   processSelection(hits, selectBuf, select_buf_size);
174 }
175
176 float GlSeqBrowser::left() const
177
178   return max_ortho.left; 
179 }
180
181 float GlSeqBrowser::right() const
182
183   return max_ortho.right; 
184 }
185
186 void GlSeqBrowser::setViewportCenter(float x)
187 {
188   update_viewport(x, zoom_level);
189   viewport_center = x;
190 }
191
192 float GlSeqBrowser::viewportLeft() const
193
194   return cur_ortho.left; 
195 }
196
197 float GlSeqBrowser::viewportCenter() const
198 {
199   return viewport_center;
200 }
201
202 float GlSeqBrowser::viewportRight() const
203
204   return cur_ortho.right; 
205 }
206
207 float GlSeqBrowser::viewportHeight() const
208 {
209   return cur_ortho.top - cur_ortho.bottom;
210 }
211
212 float GlSeqBrowser::viewportWidth() const
213 {
214   return cur_ortho.right - cur_ortho.left;
215 }
216
217 void GlSeqBrowser::setZoom(int new_zoom)
218 {
219   update_viewport(viewport_center, new_zoom);
220   zoom_level = new_zoom;
221 }
222
223 int GlSeqBrowser::zoom() const
224 {
225   return zoom_level;
226 }
227
228 void GlSeqBrowser::setColorMapper(AnnotationColors& cm)
229 {
230   color_mapper = cm;
231 }
232
233 AnnotationColors& GlSeqBrowser::colorMapper()
234 {
235   return color_mapper;
236 }
237
238 void GlSeqBrowser::clear()
239 {
240   clear_links();
241   path_segments.clear();
242   track_container.clear();
243 }
244
245 void GlSeqBrowser::push_sequence(const Sequence &s)
246 {
247   GlSequence gs(s, color_mapper);
248   push_sequence(gs);
249 }
250
251 void GlSeqBrowser::push_sequence(GlSequence &gs)
252 {
253   clear_links();
254   pathid = 0;
255   track_container.push_back(gs);
256   update_layout();
257   if (track_container.size() > 1)
258     path_segments.push_back(pair_segment_map());
259 }
260
261 const std::vector<GlSequence>& GlSeqBrowser::sequences() const
262 {
263   return track_container;
264 }
265
266 void GlSeqBrowser::clear_links()
267 {
268   path_segment_map_vector::iterator psmv_i;
269   for(psmv_i = path_segments.begin();
270       psmv_i != path_segments.end();
271       ++psmv_i)
272   {
273     psmv_i->clear();
274   }
275 }
276
277 void 
278 GlSeqBrowser::link(const vector<int>& path, const vector<bool>& rc, int length)
279 {
280   if (path.size() < 2) {
281     // should i throw an error instead?
282     return;
283   }
284   if (path.size() != rc.size()) {
285     throw runtime_error("path and reverse compliment must be the same length");
286   }
287   vector<int>::const_iterator path_i = path.begin();
288   vector<bool>::const_iterator rc_i = rc.begin();
289   int track_i = 0;
290   int prev_x = *path_i; ++path_i;
291   bool prev_rc = *rc_i; ++rc_i;
292   while (path_i != path.end() and rc_i != rc.end())
293   {
294     segment_key p(prev_x, *path_i);
295     pair_segment_map::iterator found_segment = path_segments[track_i].find(p);
296     if (found_segment == path_segments[track_i].end()) {
297       // not already found
298       float y1 = track_container[track_i].y();
299             y1 -= track_container[track_i].height()/2;
300       float y2 = track_container[track_i+1].y();
301             y2 += track_container[track_i+1].height()/2;
302             
303       Segment s(prev_x, y1, *path_i, y2, prev_rc);
304       s.path_ids.insert(pathid);
305       path_segments[track_i][p] = s;
306     } else {
307       //found
308       found_segment->second.path_ids.insert(pathid);
309     }
310     prev_x = *path_i;
311     prev_rc = *rc_i;
312     ++track_i;
313     ++path_i;
314     ++rc_i;
315   }
316   // pathid is reset by push_sequence
317   ++pathid;
318 }
319
320 void GlSeqBrowser::update_viewport(float center, int new_zoom)
321 {
322   float max_width = max_ortho.width();
323   // division by zero is a major bummer
324   if (new_zoom < 1) {
325     new_zoom = 1;
326   }
327   float new_max_width = max_width / new_zoom;
328   cur_ortho.left = center-new_max_width;
329   cur_ortho.right = center+new_max_width;
330 }
331
332 void GlSeqBrowser::update_layout()
333 {
334   typedef std::vector<GlSequence>::iterator glseq_itor_type;
335   float available_height = (float)cur_ortho.top - 2 * (float)border;
336   float max_base_pairs = 0;
337   size_t track_count = track_container.size();
338
339   if (track_count > 1) {
340     // we have several sequences
341     float track_spacing = available_height / (track_count-1);
342     float y = available_height + (float)border;
343     for(glseq_itor_type seq_i = track_container.begin();
344         seq_i != track_container.end();
345         ++seq_i, y-=track_spacing)
346     {
347       seq_i->setX(0);
348       seq_i->setY(y);
349       if (seq_i->length() > max_base_pairs)
350         max_base_pairs = seq_i->length();
351     }
352   } else if (track_count == 1) {
353     // center the single track
354     glseq_itor_type seq_i = track_container.begin();
355     seq_i->setX(0);
356     seq_i->setY(viewport_size.x /2);
357     max_base_pairs = seq_i->length();
358   } else {
359     // nothing to do as we're empty
360     return;
361   }
362   max_ortho.right = max_base_pairs + border;
363   max_ortho.left = -border;
364   max_ortho.top = viewport_size.x;
365   max_ortho.bottom = 0;
366   cur_ortho = max_ortho;
367   viewport_center = (cur_ortho.width()/2) + cur_ortho.left;
368 }
369
370 void GlSeqBrowser::draw() const
371 {
372   glMatrixMode(GL_MODELVIEW);
373   glInitNames();
374   glPushName(MussaSegment);
375   draw_segments();
376   glLoadName(MussaTrack);
377   draw_tracks();
378   glPopName();
379   // a selection shouldn't have a glName associated with it
380   draw_selection();
381 }
382
383 void GlSeqBrowser::draw_selection() const
384 {
385   // draw selection box
386   glEnable(GL_BLEND);
387   glDepthMask(GL_FALSE);
388   if (selectedMode) {
389     glColor4f(0.6, 0.6, 0.6, 0.9);
390     glRectf(selectedRegion.left, selectedRegion.top, 
391             selectedRegion.right, selectedRegion.bottom);
392   }
393   glDepthMask(GL_TRUE);
394   glDisable(GL_BLEND);
395 }
396
397 void GlSeqBrowser::draw_tracks() const
398 {
399   for(size_t track_i = 0; track_i != track_container.size(); ++track_i)
400   {
401     glPushName(track_i);
402     track_container[track_i].draw(cur_ortho.left, cur_ortho.right);
403     glPopName();
404   }
405 }
406
407 void GlSeqBrowser::draw_segments() const
408 {
409   glLineWidth(0.1);
410   // each vector contains path_segment_maps of all the connections
411   // between this track and the next
412   path_segment_map_vector::const_iterator psmv_i;
413   for(psmv_i = path_segments.begin();
414       psmv_i != path_segments.end();
415       ++psmv_i)
416   {
417     path_segment_map_vector::difference_type path_index;
418     path_index = psmv_i - path_segments.begin();
419     // these maps contain the pair index (used so we dont keep drawing the
420     // same segment) and the actual segment structure.
421     pair_segment_map::const_iterator psm_i;
422     for(psm_i = psmv_i->begin();
423         psm_i != psmv_i->end();
424         ++psm_i)
425     {
426       // grab the index into our segment map
427       const segment_key& key = psm_i->first;
428       // the second element of our map pair is a segment
429       const Segment &s = psm_i->second;
430       // need to do something so we can detect our selection
431       vector<int> selected;
432       set_intersection(selectedPaths.begin(), selectedPaths.end(),
433                        s.path_ids.begin(), s.path_ids.end(),
434                        back_inserter(selected));
435
436       if (not s.reversed) {
437         if (selectedPaths.size() == 0 or selected.size() > 0) {
438           glColor3f(1.0, 0.0, 0.0);
439         } else {
440           glColor3f(1.0, 0.8, 0.8);
441         }
442       } else { 
443         if (selectedPaths.size() == 0 or selected.size() > 0) {
444           glColor3f(0.0, 0.0, 1.0);
445         } else {
446           glColor3f(0.8, 0.8, 1.0);
447         }
448       }
449       // save the multipart name for our segment
450       glPushName(path_index); glPushName(key.first); glPushName(key.second);
451       glBegin(GL_LINES);
452       glVertex3f(s.start.x, s.start.y, -1);
453       glVertex3f(s.end.x, s.end.y, -1);
454       glEnd();
455       // clear the names
456       glPopName(); glPopName(); glPopName();
457     }
458   }
459 }