1 #include "alg/glseqbrowser.hpp"
2 #include "mussa_exceptions.hpp"
11 GlSeqBrowser::GlSeqBrowser()
13 cur_ortho(400.0, 0.0, 600.0, 0.0),
14 viewport_size(600, 400),
15 viewport_center((cur_ortho.right-cur_ortho.left)/2+cur_ortho.left),
22 GlSeqBrowser::GlSeqBrowser(const GlSeqBrowser& gt)
23 : border_width(gt.border_width),
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 path_segments(gt.path_segments)
34 void GlSeqBrowser::initializeGL()
36 glEnable(GL_DEPTH_TEST);
37 glClearColor(1.0, 1.0, 1.0, 0.0);
38 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
39 glShadeModel(GL_FLAT);
42 void GlSeqBrowser::resizeGL(int width, int height)
44 viewport_size.x = width;
45 viewport_size.y = height;
46 glViewport(0, 0, (GLsizei)width, (GLsizei)height);
47 update_viewport(viewport_center, zoom_level);
50 void GlSeqBrowser::paintGL() const
52 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
55 glMatrixMode(GL_PROJECTION);
57 glOrtho(cur_ortho.left, cur_ortho.right,
58 cur_ortho.bottom, cur_ortho.top,
67 void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize, const rect<float>& r)
71 GLuint consumed_names = 0;
76 GLuint path_index = 0;
77 GLuint pair_key_0 = 0;
78 GLuint pair_key_1 = 0;
81 selected_paths.clear();
82 selected_tracks.clear();
84 ptr = (GLuint *) buffer;
87 for (GLuint i=0; i < hits; ++i)
89 if ((i + 5) > bufsize) {
90 std::clog << "*** selection overflow***" << std::endl;
94 z1 = ((float)*ptr++)/0x7fffffff;
95 z2 = ((float)*ptr++)/0x7fffffff;
96 objtype = *ptr++; ++consumed_names;
99 path_index = *ptr++; ++consumed_names;
100 pair_key_0 = *ptr++; ++consumed_names;
101 pair_key_1 = *ptr++; ++consumed_names;
102 if (path_index < path_segments.size()) {
103 segment_key k(pair_key_0, pair_key_1);
104 pair_segment_map::iterator psm_i;
105 psm_i = path_segments[path_index].find(k);
106 if (psm_i != path_segments[path_index].end()) {
107 Segment &seg = psm_i->second;
108 selected_paths.insert(seg.path_ids.begin(), seg.path_ids.end());
110 // else something else is wrong
112 // something wasn't right
113 clog << "invalid path_index " << path_index
114 << " should have been [0,"<<path_segments.size()
120 objid = *ptr++; ++consumed_names;
122 int left = track_container[objid]->leftbase(r.left);
123 int right = track_container[objid]->rightbase(r.right);
124 // the static_cast should be ok, since basepairs line up on
126 //TrackRegion track(objid, left, right);
127 track.set(objid, left, right);
128 selected_tracks.push_back(track);
129 //clog << "selected track " << objid
130 // << "(" << left << ", " << right << ")" << endl;
134 cout << "unknown type " << objtype << " ";
135 for(; consumed_names < names; ++consumed_names) {
136 cout << consumed_names << "," << *ptr++ << " ";
145 void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right)
147 GLfloat x_scale = cur_ortho.width()/((float)viewport_size.x);
148 GLfloat y_scale = cur_ortho.height()/((float)viewport_size.y);
149 GLfloat x_left = cur_ortho.left + (left*x_scale);
150 GLfloat x_right = cur_ortho.left + (right * x_scale);
153 // woah, someone gave us a rectangle with the origin in the lower left
158 // swap the orientation of canvas coordinates
159 GLfloat y_top = cur_ortho.top-(bottom*y_scale);
160 GLfloat y_bottom = cur_ortho.top - top * y_scale;
161 selectedRegion = rect<float>(y_top, x_left, y_bottom, x_right);
163 // hopefully this will make a buffer big enough to receive
164 // everything being selected
165 //const size_t pathz_count = mussaAnalysis->paths().refined_pathz.size();
166 //const GLuint select_buf_size = 1 + 5 * (pathz_count + sequences.size());
167 const GLuint select_buf_size = 500000;
168 GLuint selectBuf[select_buf_size];
169 glSelectBuffer(select_buf_size, selectBuf);
172 (void)glRenderMode(GL_SELECT);
174 glMatrixMode(GL_PROJECTION);
176 glOrtho(x_left, x_right, y_top, y_bottom, -50.0, 50.0);
177 glMatrixMode(GL_MODELVIEW);
184 hits = glRenderMode(GL_RENDER);
185 processSelection(hits, selectBuf, select_buf_size, selectedRegion);
188 void GlSeqBrowser::clearSelection()
190 selected_paths.clear();
191 selected_tracks.clear();
192 selectedMode = false;
195 float GlSeqBrowser::border() const
200 float GlSeqBrowser::left() const
203 if (track_container.size() == 0)
205 return cur_ortho.left;
207 vector<boost::shared_ptr<GlSequence> >::const_iterator track_i = track_container.begin();
208 left = (*track_i)->x();
209 for( ; track_i != track_container.end(); ++track_i)
211 if ((*track_i)->x() < left) {
212 left = (*track_i)->x();
215 return left-border_width;
219 float GlSeqBrowser::right() const
222 if (track_container.size() == 0) {
223 return cur_ortho.right;
225 vector<boost::shared_ptr<GlSequence> >::const_iterator track_i = track_container.begin();
226 right = (*track_i)->right();
227 for( ; track_i != track_container.end(); ++track_i) {
228 if ((*track_i)->right() > right)
229 right = (*track_i)->right();
231 return right+border_width;
235 float GlSeqBrowser::get_pixel_width() const
238 glGetIntegerv(GL_VIEWPORT, viewport);
239 GLint vp_width = viewport[3]; // grab the viewport width
241 return round((cur_ortho.right-cur_ortho.left)/vp_width);
244 void GlSeqBrowser::setViewportCenter(float x)
246 update_viewport(x, zoom_level);
250 float GlSeqBrowser::viewportLeft() const
252 return cur_ortho.left;
255 float GlSeqBrowser::viewportCenter() const
257 return viewport_center;
260 float GlSeqBrowser::viewportRight() const
262 return cur_ortho.right;
265 float GlSeqBrowser::viewportHeight() const
267 return cur_ortho.top - cur_ortho.bottom;
270 float GlSeqBrowser::viewportWidth() const
272 return cur_ortho.right - cur_ortho.left;
275 int GlSeqBrowser::viewportPixelHeight() const
277 return viewport_size.y;
280 int GlSeqBrowser::viewportPixelWidth() const
282 return viewport_size.x;
285 double GlSeqBrowser::zoomOut()
288 if (right() - left() > 0) {
289 cur_ortho.left = left();
290 cur_ortho.right = right();
291 zoom_level = (right() - left()) / (double)viewport_size.x;
294 // made up number representing 50 bp / pixel
299 double GlSeqBrowser::zoomToSequence()
301 // (experimentally determined zoom level)
302 const double friendly_zoom = 0.10;
303 setZoom(friendly_zoom);
304 return friendly_zoom;
307 void GlSeqBrowser::setZoom(double new_zoom)
309 update_viewport(viewport_center, new_zoom);
310 zoom_level = new_zoom;
313 double GlSeqBrowser::zoom() const
318 void GlSeqBrowser::setColorMapper(AnnotationColorsRef cm)
323 const AnnotationColorsRef GlSeqBrowser::colorMapper()
328 void GlSeqBrowser::clear()
332 path_segments.clear();
333 track_container.clear();
336 void GlSeqBrowser::clear_selection()
338 selectedMode = false;
339 selectedRegion.clear();
340 selected_paths.clear();
341 selected_tracks.clear();
344 void GlSeqBrowser::push_sequence(const Sequence& s)
346 GlSequenceRef gs(new GlSequence(s, color_mapper));
350 void GlSeqBrowser::push_sequence(SequenceRef s)
352 GlSequenceRef gs(new GlSequence(*s, color_mapper));
356 void GlSeqBrowser::push_sequence(GlSequence gs)
358 GlSequenceRef new_gs(new GlSequence(gs));
359 push_sequence(new_gs);
362 void GlSeqBrowser::push_sequence(GlSequenceRef gs)
364 ColorRef default_color(GlSequence::default_gene_color());
365 GlSequenceRef new_gs(new GlSequence(gs));
366 new_gs->update_annotation_draw_function("gene", draw_narrow_track, default_color);
367 // mark where the sequence is
368 new_gs->add_annotations_for_defined_sequence(draw_summarized_track);
371 track_container.push_back(new_gs);
373 if (track_container.size() > 1)
374 path_segments.push_back(pair_segment_map());
377 const std::vector<GlSequenceRef >& GlSeqBrowser::sequences() const
379 return track_container;
382 void GlSeqBrowser::clear_links()
384 path_segments.clear();
385 for (int i = track_container.size()-1; i > 0; --i)
387 path_segments.push_back(pair_segment_map());
393 GlSeqBrowser::link(const vector<int>& path, const vector<bool>& rc, int length)
395 if (path.size() < 2) {
396 // should i throw an error instead?
399 if (path.size() != track_container.size() ) {
401 msg << "Path size [" << path.size() << "] and track size ["
402 << track_container.size() << "] don't match" << endl;
403 throw mussa_error(msg.str());
405 if (path.size() != rc.size()) {
406 throw runtime_error("path and reverse compliment must be the same length");
408 vector<int>::const_iterator path_i = path.begin();
409 vector<bool>::const_iterator rc_i = rc.begin();
411 int prev_x = *path_i; ++path_i;
412 bool prev_rc = *rc_i; ++rc_i;
413 while (path_i != path.end() and rc_i != rc.end())
415 segment_key p(prev_x, *path_i);
416 pair_segment_map::iterator found_segment = path_segments[track_i].find(p);
417 if (found_segment == path_segments[track_i].end()) {
419 float y1 = track_container[track_i]->y();
420 y1 -= track_container[track_i]->height()/2;
421 float y2 = track_container[track_i+1]->y();
422 y2 += track_container[track_i+1]->height()/2;
424 bool rcFlag = (prev_rc or *rc_i) and !(prev_rc and *rc_i);
425 Segment s(prev_x, y1, *path_i, y2, rcFlag, length);
426 s.path_ids.insert(pathid);
427 path_segments[track_i][p] = s;
430 found_segment->second.path_ids.insert(pathid);
431 // make each segment the size of the largest of any link between these
433 if (found_segment->second.length < length) {
434 found_segment->second.length = length;
443 // pathid is reset by push_sequence
447 void GlSeqBrowser::setSelectedPaths(std::vector<int> paths)
449 selected_paths.clear();
450 for(std::vector<int>::iterator itor = paths.begin();
454 selected_paths.insert(*itor);
458 const set<int>& GlSeqBrowser::selectedPaths() const
460 return selected_paths;
463 void GlSeqBrowser::appendSelectedTrack(GLuint track, int start, int stop)
465 selected_tracks.push_back(TrackRegion(track, start, stop));
468 list<TrackRegion> GlSeqBrowser::selectedTracks() const
470 return selected_tracks;
473 //! copy sequence from selected track using formating function
475 size_t GlSeqBrowser::copySelectedTracks(std::list<Item>& result,
476 Item (*formatter)(const Sequence& s, int left, int right))
478 size_t base_pairs_copied = 0;
481 for(selected_track_iterator track_i = selected_tracks.begin();
482 track_i != selected_tracks.end();
485 int track_index = track_i->track_id;
486 if (track_index >= track_container.size()) {
487 // should this be an exception instead?
488 clog << "track " << track_index << " > " << track_container.size()
492 Sequence seq(*track_container[track_index]);
493 result.push_back(formatter(seq, track_i->left, track_i->right));
494 base_pairs_copied += max(track_i->right-track_i->left, 0);
497 return base_pairs_copied;
500 //! copy sequence from selected tracks as FASTA sequences
501 size_t GlSeqBrowser::copySelectedTracksAsFasta(std::string& copy_buffer)
503 std::list<std::string> result;
505 static string formatter(const Sequence& seq, int left, int right)
508 s << ">" << seq.get_fasta_header()
509 << "|" << "subregion=" << left << "-" << right+1
511 << seq.subseq(left, right-left+1) << std::endl;
515 size_t base_pairs_copied = copySelectedTracks(result, AsFasta::formatter);
516 // I wish there was some way to use for_each and bind here
517 for (list<string>::iterator result_i = result.begin();
518 result_i != result.end();
521 copy_buffer.append(*result_i);
523 return base_pairs_copied;
526 //! copy sequence from selected tracks as new sequences
527 size_t GlSeqBrowser::copySelectedTracksAsSequences(std::list<Sequence>& result)
530 static Sequence formatter(const Sequence& seq,
534 return seq.subseq(left, right-left+1);
537 return copySelectedTracks(result, AsSequence::formatter);
540 size_t GlSeqBrowser::copySelectedTracksAsSeqLocation(
541 std::list<SequenceLocation>& result)
543 struct AsSeqLocation {
544 static SequenceLocation formatter(const Sequence& seq,
548 return SequenceLocation(seq, left, right);
551 return copySelectedTracks(result, AsSeqLocation::formatter);
554 //! copy sequence from selected tracks as plain sequences
555 size_t GlSeqBrowser::copySelectedTracksAsString(std::string& copy_buffer)
557 std::list<string> result;
559 static string formatter(const Sequence& seq,
564 s << seq.subseq(left, right-left+1);
569 size_t base_pairs_copied = copySelectedTracks(result, AsString::formatter);
570 // I wish there was some way to use for_each and bind here
571 for (list<string>::iterator result_i = result.begin();
572 result_i != result.end();
575 copy_buffer.append(*result_i);
577 return base_pairs_copied;
580 void GlSeqBrowser::centerOnPath(const vector<int>& paths)
582 if (paths.size() != track_container.size()) {
583 throw mussa_error("Path length didn't match the number of sequences");
586 for(size_t track_i = 0; track_i != track_container.size(); ++track_i)
588 // -15 = shift more to the left
589 track_container[track_i]->setX((viewport_center-15) - paths[track_i]);
593 void GlSeqBrowser::update_viewport(float center, double new_zoom)
595 // limit how close we can get
596 if (new_zoom < 0.01) {
599 double new_width = (new_zoom * (float)viewport_size.x);
600 cur_ortho.left = center-new_width/2.0;
601 cur_ortho.right = center+new_width/2.0;
604 void GlSeqBrowser::update_layout()
606 typedef std::vector<boost::shared_ptr<GlSequence> >::iterator glseq_itor_type;
607 float available_height = (float)cur_ortho.top - 2 * (float)border_width;
608 float max_base_pairs = 0;
609 size_t track_count = track_container.size();
611 if (track_count > 1) {
612 // we have several sequences
613 float track_spacing = available_height / (track_count-1);
614 float y = available_height + (float)border_width;
615 for(glseq_itor_type seq_i = track_container.begin();
616 seq_i != track_container.end();
617 ++seq_i, y-=track_spacing)
621 if ((*seq_i)->size() > max_base_pairs)
622 max_base_pairs = (*seq_i)->size();
624 } else if (track_count == 1) {
625 // center the single track
626 glseq_itor_type seq_i = track_container.begin();
628 (*seq_i)->setY(viewport_size.x /2);
629 max_base_pairs = (*seq_i)->size();
631 // nothing to do as we're empty
634 cur_ortho.right = max_base_pairs + border_width;
635 cur_ortho.left = -border_width;
636 cur_ortho.top = viewport_size.x;
637 cur_ortho.bottom = 0;
638 viewport_center = (cur_ortho.width()/2) + cur_ortho.left;
642 void GlSeqBrowser::draw() const
644 glMatrixMode(GL_MODELVIEW);
646 glPushName(MussaSegment);
648 glLoadName(MussaTrack);
651 // a selection shouldn't have a glName associated with it
655 void GlSeqBrowser::draw_selection() const
657 // draw selection box
659 glDepthMask(GL_FALSE);
661 glColor4f(0.6, 0.6, 0.6, 0.9);
662 glRectf(selectedRegion.left, selectedRegion.top,
663 selectedRegion.right, selectedRegion.bottom);
665 glDepthMask(GL_TRUE);
669 void GlSeqBrowser::draw_tracks() const
671 for(size_t track_i = 0; track_i != track_container.size(); ++track_i)
674 track_container[track_i]->draw(cur_ortho.left, cur_ortho.right);
679 void GlSeqBrowser::draw_segments() const
683 glDepthMask(GL_FALSE);
684 const float zdepth = -1.0;
685 const float min_segment_width = max((float)(1.0), get_pixel_width());
687 // each vector contains path_segment_maps of all the connections
688 // between this track and the next
689 path_segment_map_vector::const_iterator psmv_i;
690 for(psmv_i = path_segments.begin();
691 psmv_i != path_segments.end();
694 path_segment_map_vector::difference_type path_index;
695 path_index = psmv_i - path_segments.begin();
696 // these maps contain the pair index (used so we dont keep drawing the
697 // same segment) and the actual segment structure.
698 pair_segment_map::const_iterator psm_i;
699 for(psm_i = psmv_i->begin();
700 psm_i != psmv_i->end();
703 // grab the index into our segment map
704 const segment_key& key = psm_i->first;
705 // the second element of our map pair is a segment
706 const Segment &s = psm_i->second;
707 // need to do something so we can detect our selection
708 vector<int> selected;
709 set_intersection(selected_paths.begin(), selected_paths.end(),
710 s.path_ids.begin(), s.path_ids.end(),
711 back_inserter(selected));
713 if (not s.reversed) {
715 if (selected_paths.size() == 0 or selected.size() > 0) {
716 glColor4f(1.0, 0.0, 0.0, 1.0);
718 glColor4f(1.0, 0.7, 0.7, 0.4);
722 if (selected_paths.size() == 0 or selected.size() > 0) {
723 glColor4f(0.0, 0.0, 1.0, 1.0);
725 glColor4f(0.7, 0.7, 1.0, 0.4);
728 // save the multipart name for our segment
729 glPushName(path_index); glPushName(key.first); glPushName(key.second);
730 float seq_start_x = s.start.x
731 + track_container[path_index]->x();
732 float seq_end_x = s.end.x
733 + track_container[path_index+1]->x();
734 if (s.length <= min_segment_width) {
735 // use lines for elements of length <=1 or < 1 pixel.
736 // and try to center the line
737 const float offset = s.length * 0.5;
739 glVertex3f(seq_start_x+offset, s.start.y, -1);
740 glVertex3f(seq_end_x +offset, s.end.y, -1);
743 // otherwise use quads
745 float seq_start_x_length = s.start.x
747 + track_container[path_index]->x();
748 float seq_end_x_length = s.end.x
750 + track_container[path_index+1]->x();
752 glVertex3f(seq_start_x, s.start.y, zdepth);
753 glVertex3f(seq_end_x, s.end.y, zdepth);
754 glVertex3f(seq_end_x_length, s.end.y, zdepth);
755 glVertex3f(seq_start_x_length, s.start.y, zdepth);
759 glPopName(); glPopName(); glPopName();
762 glDepthMask(GL_TRUE);