1 #include "alg/glseqbrowser.hpp"
2 #include "mussa_exceptions.hpp"
10 GlSeqBrowser::SequenceLocation::SequenceLocation(
14 ) : sequence(s), left(l), count(c)
18 GlSeqBrowser::GlSeqBrowser()
20 cur_ortho(400.0, 0.0, 600.0, 0.0),
21 viewport_size(600, 400),
22 viewport_center((cur_ortho.right-cur_ortho.left)/2+cur_ortho.left),
29 GlSeqBrowser::GlSeqBrowser(const GlSeqBrowser& gt)
30 : border_width(gt.border_width),
31 cur_ortho(gt.cur_ortho),
32 viewport_size(gt.viewport_size),
33 viewport_center(gt.viewport_center),
34 zoom_level(gt.zoom_level),
35 color_mapper(gt.color_mapper),
36 track_container(gt.track_container)
40 void GlSeqBrowser::initializeGL()
42 glEnable(GL_DEPTH_TEST);
43 glClearColor(1.0, 1.0, 1.0, 0.0);
44 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
45 glShadeModel(GL_FLAT);
48 void GlSeqBrowser::resizeGL(int width, int height)
50 viewport_size.x = width;
51 viewport_size.y = height;
52 glViewport(0, 0, (GLsizei)width, (GLsizei)height);
53 update_viewport(viewport_center, zoom_level);
56 void GlSeqBrowser::paintGL() const
58 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
61 glMatrixMode(GL_PROJECTION);
63 glOrtho(cur_ortho.left, cur_ortho.right,
64 cur_ortho.bottom, cur_ortho.top,
73 void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize, const rect<float>& r)
77 GLuint consumed_names = 0;
82 GLuint path_index = 0;
83 GLuint pair_key_0 = 0;
84 GLuint pair_key_1 = 0;
87 selected_paths.clear();
88 selected_tracks.clear();
90 ptr = (GLuint *) buffer;
93 for (GLuint i=0; i < hits; ++i)
95 if ((i + 5) > bufsize) {
96 std::clog << "*** selection overflow***" << std::endl;
100 z1 = ((float)*ptr++)/0x7fffffff;
101 z2 = ((float)*ptr++)/0x7fffffff;
102 objtype = *ptr++; ++consumed_names;
105 path_index = *ptr++; ++consumed_names;
106 pair_key_0 = *ptr++; ++consumed_names;
107 pair_key_1 = *ptr++; ++consumed_names;
108 if (path_index < path_segments.size()) {
109 segment_key k(pair_key_0, pair_key_1);
110 pair_segment_map::iterator psm_i;
111 psm_i = path_segments[path_index].find(k);
112 if (psm_i != path_segments[path_index].end()) {
113 Segment &seg = psm_i->second;
114 selected_paths.insert(seg.path_ids.begin(), seg.path_ids.end());
116 // else something else is wrong
118 // something wasn't right
119 clog << "invalid path_index " << path_index
120 << " should have been [0,"<<path_segments.size()
126 objid = *ptr++; ++consumed_names;
128 int left = track_container[objid].leftbase(r.left);
129 int right = track_container[objid].rightbase(r.right);
130 // the static_cast should be ok, since basepairs line up on
132 //TrackRegion track(objid, left, right);
133 track.set(objid, left, right);
134 selected_tracks.push_back(track);
138 cout << "unknown type " << objtype << " ";
139 for(; consumed_names < names; ++consumed_names) {
140 cout << consumed_names << "," << *ptr++ << " ";
149 void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right)
151 GLfloat x_scale = cur_ortho.width()/((float)viewport_size.x);
152 GLfloat y_scale = cur_ortho.height()/((float)viewport_size.y);
153 GLfloat x_left = cur_ortho.left + (left*x_scale);
154 GLfloat x_right = cur_ortho.left + (right * x_scale);
157 // woah, someone gave us a rectangle with the origin in the lower left
162 // swap the orientation of canvas coordinates
163 GLfloat y_top = cur_ortho.top-(bottom*y_scale);
164 GLfloat y_bottom = cur_ortho.top - top * y_scale;
165 selectedRegion = rect<float>(y_top, x_left, y_bottom, x_right);
167 // hopefully this will make a buffer big enough to receive
168 // everything being selected
169 //const size_t pathz_count = mussaAnalysis->paths().refined_pathz.size();
170 //const GLuint select_buf_size = 1 + 5 * (pathz_count + sequences.size());
171 const GLuint select_buf_size = 500000;
172 GLuint selectBuf[select_buf_size];
173 glSelectBuffer(select_buf_size, selectBuf);
176 (void)glRenderMode(GL_SELECT);
178 glMatrixMode(GL_PROJECTION);
180 glOrtho(x_left, x_right, y_top, y_bottom, -50.0, 50.0);
181 glMatrixMode(GL_MODELVIEW);
188 hits = glRenderMode(GL_RENDER);
189 processSelection(hits, selectBuf, select_buf_size, selectedRegion);
192 float GlSeqBrowser::border() const
197 float GlSeqBrowser::left() const
200 if (track_container.size() == 0)
202 return cur_ortho.left;
204 vector<GlSequence>::const_iterator track_i = track_container.begin();
206 for( ; track_i != track_container.end(); ++track_i)
208 if (track_i->x() < left) {
212 return left-border_width;
216 float GlSeqBrowser::right() const
219 if (track_container.size() == 0) {
220 return cur_ortho.right;
222 vector<GlSequence>::const_iterator track_i = track_container.begin();
223 right = track_i->right();
224 for( ; track_i != track_container.end(); ++track_i) {
225 if (track_i->right() > right)
226 right = track_i->right();
228 return right+border_width;
232 void GlSeqBrowser::setViewportCenter(float x)
234 update_viewport(x, zoom_level);
238 float GlSeqBrowser::viewportLeft() const
240 return cur_ortho.left;
243 float GlSeqBrowser::viewportCenter() const
245 return viewport_center;
248 float GlSeqBrowser::viewportRight() const
250 return cur_ortho.right;
253 float GlSeqBrowser::viewportHeight() const
255 return cur_ortho.top - cur_ortho.bottom;
258 float GlSeqBrowser::viewportWidth() const
260 return cur_ortho.right - cur_ortho.left;
263 double GlSeqBrowser::zoomOut()
266 if (right() - left() > 0) {
267 cur_ortho.left = left();
268 cur_ortho.right = right();
269 zoom_level = (right() - left()) / (double)viewport_size.x;
272 // made up number representing 50 bp / pixel
277 double GlSeqBrowser::zoomToSequence()
279 // (experimentally determined zoom level)
280 const double friendly_zoom = 0.10;
281 setZoom(friendly_zoom);
282 return friendly_zoom;
285 void GlSeqBrowser::setZoom(double new_zoom)
287 update_viewport(viewport_center, new_zoom);
288 zoom_level = new_zoom;
291 double GlSeqBrowser::zoom() const
296 void GlSeqBrowser::setColorMapper(AnnotationColors& cm)
301 AnnotationColors& GlSeqBrowser::colorMapper()
306 void GlSeqBrowser::clear()
310 path_segments.clear();
311 track_container.clear();
314 void GlSeqBrowser::clear_selection()
316 selectedMode = false;
317 selectedRegion.clear();
318 selected_paths.clear();
319 selected_tracks.clear();
322 void GlSeqBrowser::push_sequence(const Sequence &s)
324 GlSequence gs(s, color_mapper);
328 void GlSeqBrowser::push_sequence(GlSequence &gs)
331 track_container.push_back(gs);
333 if (track_container.size() > 1)
334 path_segments.push_back(pair_segment_map());
337 const std::vector<GlSequence>& GlSeqBrowser::sequences() const
339 return track_container;
342 void GlSeqBrowser::clear_links()
344 path_segments.clear();
345 for (int i = track_container.size()-1; i > 0; --i)
347 path_segments.push_back(pair_segment_map());
353 GlSeqBrowser::link(const vector<int>& path, const vector<bool>& rc, int )
355 if (path.size() < 2) {
356 // should i throw an error instead?
359 if (path.size() != track_container.size() ) {
361 msg << "Path size [" << path.size() << "] and track size ["
362 << track_container.size() << "] don't match" << endl;
363 throw mussa_error(msg.str());
365 if (path.size() != rc.size()) {
366 throw runtime_error("path and reverse compliment must be the same length");
368 vector<int>::const_iterator path_i = path.begin();
369 vector<bool>::const_iterator rc_i = rc.begin();
371 int prev_x = *path_i; ++path_i;
372 bool prev_rc = *rc_i; ++rc_i;
373 while (path_i != path.end() and rc_i != rc.end())
375 segment_key p(prev_x, *path_i);
376 pair_segment_map::iterator found_segment = path_segments[track_i].find(p);
377 if (found_segment == path_segments[track_i].end()) {
379 float y1 = track_container[track_i].y();
380 y1 -= track_container[track_i].height()/2;
381 float y2 = track_container[track_i+1].y();
382 y2 += track_container[track_i+1].height()/2;
384 bool rcFlag = (prev_rc or *rc_i) and !(prev_rc and *rc_i);
385 Segment s(prev_x, y1, *path_i, y2, rcFlag);
386 s.path_ids.insert(pathid);
387 path_segments[track_i][p] = s;
390 found_segment->second.path_ids.insert(pathid);
398 // pathid is reset by push_sequence
402 const set<int>& GlSeqBrowser::selectedPaths() const
404 return selected_paths;
407 //! copy sequence from selected track using formating function
409 void GlSeqBrowser::copySelectedTracks(std::list<Item>& result,
410 Item (*formatter)(const Sequence& s, int left, int right))
414 for(selected_track_iterator track_i = selected_tracks.begin();
415 track_i != selected_tracks.end();
418 int track_index = track_i->track_id;
419 if (track_index >= track_container.size()) {
420 // should this be an exception instead?
421 clog << "track " << track_index << " > " << track_container.size()
425 const Sequence& seq = track_container[track_index].sequence();
426 result.push_back(formatter(seq, track_i->left, track_i->right));
431 //! copy sequence from selected tracks as FASTA sequences
432 void GlSeqBrowser::copySelectedTracksAsFasta(std::string& copy_buffer)
434 std::list<std::string> result;
436 static string formatter(const Sequence& seq, int left, int right)
439 s << ">" << seq.get_header()
440 << "|" << "subregion=" << left << "-" << right+1
442 << seq.subseq(left, right-left+1) << std::endl;
446 copySelectedTracks(result, AsFasta::formatter);
447 // I wish there was some way to use for_each and bind here
448 for (list<string>::iterator result_i = result.begin();
449 result_i != result.end();
452 copy_buffer.append(*result_i);
456 //! copy sequence from selected tracks as new sequences
457 void GlSeqBrowser::copySelectedTracksAsSequences(std::list<Sequence>& result)
460 static Sequence formatter(const Sequence& seq, int left, int right)
462 return seq.subseq(left, right-left+1);
465 copySelectedTracks(result, AsSequence::formatter);
468 void GlSeqBrowser::copySelectedTracksAsSeqLocation(
469 std::list<GlSeqBrowser::SequenceLocation>& result)
471 struct AsSeqLocation {
472 static GlSeqBrowser::SequenceLocation
473 formatter(const Sequence& seq, int left, int right)
475 return SequenceLocation(seq, left, right);
478 copySelectedTracks(result, AsSeqLocation::formatter);
481 //! copy sequence from selected tracks as plain sequences
482 void GlSeqBrowser::copySelectedTracksAsString(std::string& copy_buffer)
484 std::list<string> result;
486 static string formatter(const Sequence& seq, int left, int right)
489 s << seq.subseq(left, right-left+1) << std::endl;
494 copySelectedTracks(result, AsString::formatter);
495 // I wish there was some way to use for_each and bind here
496 for (list<string>::iterator result_i = result.begin();
497 result_i != result.end();
500 copy_buffer.append(*result_i);
505 void GlSeqBrowser::centerOnPath(const vector<int>& paths)
507 if (paths.size() != track_container.size()) {
508 throw mussa_error("Path length didn't match the number of sequences");
511 for(size_t track_i = 0; track_i != track_container.size(); ++track_i)
513 // -15 = shift more to the left
514 track_container[track_i].setX((viewport_center-15) - paths[track_i]);
518 void GlSeqBrowser::update_viewport(float center, double new_zoom)
520 // limit how close we can get
521 if (new_zoom < 0.01) {
524 double new_width = (new_zoom * (float)viewport_size.x);
525 cur_ortho.left = center-new_width/2.0;
526 cur_ortho.right = center+new_width/2.0;
529 void GlSeqBrowser::update_layout()
531 typedef std::vector<GlSequence>::iterator glseq_itor_type;
532 float available_height = (float)cur_ortho.top - 2 * (float)border_width;
533 float max_base_pairs = 0;
534 size_t track_count = track_container.size();
536 if (track_count > 1) {
537 // we have several sequences
538 float track_spacing = available_height / (track_count-1);
539 float y = available_height + (float)border_width;
540 for(glseq_itor_type seq_i = track_container.begin();
541 seq_i != track_container.end();
542 ++seq_i, y-=track_spacing)
546 if (seq_i->length() > max_base_pairs)
547 max_base_pairs = seq_i->length();
549 } else if (track_count == 1) {
550 // center the single track
551 glseq_itor_type seq_i = track_container.begin();
553 seq_i->setY(viewport_size.x /2);
554 max_base_pairs = seq_i->length();
556 // nothing to do as we're empty
559 cur_ortho.right = max_base_pairs + border_width;
560 cur_ortho.left = -border_width;
561 cur_ortho.top = viewport_size.x;
562 cur_ortho.bottom = 0;
563 viewport_center = (cur_ortho.width()/2) + cur_ortho.left;
567 void GlSeqBrowser::draw() const
569 glMatrixMode(GL_MODELVIEW);
571 glPushName(MussaSegment);
573 glLoadName(MussaTrack);
576 // a selection shouldn't have a glName associated with it
580 void GlSeqBrowser::draw_selection() const
582 // draw selection box
584 glDepthMask(GL_FALSE);
586 glColor4f(0.6, 0.6, 0.6, 0.9);
587 glRectf(selectedRegion.left, selectedRegion.top,
588 selectedRegion.right, selectedRegion.bottom);
590 glDepthMask(GL_TRUE);
594 void GlSeqBrowser::draw_tracks() const
596 for(size_t track_i = 0; track_i != track_container.size(); ++track_i)
599 track_container[track_i].draw(cur_ortho.left, cur_ortho.right);
604 void GlSeqBrowser::draw_segments() const
607 // each vector contains path_segment_maps of all the connections
608 // between this track and the next
609 path_segment_map_vector::const_iterator psmv_i;
610 for(psmv_i = path_segments.begin();
611 psmv_i != path_segments.end();
614 path_segment_map_vector::difference_type path_index;
615 path_index = psmv_i - path_segments.begin();
616 // these maps contain the pair index (used so we dont keep drawing the
617 // same segment) and the actual segment structure.
618 pair_segment_map::const_iterator psm_i;
619 for(psm_i = psmv_i->begin();
620 psm_i != psmv_i->end();
623 // grab the index into our segment map
624 const segment_key& key = psm_i->first;
625 // the second element of our map pair is a segment
626 const Segment &s = psm_i->second;
627 // need to do something so we can detect our selection
628 vector<int> selected;
629 set_intersection(selected_paths.begin(), selected_paths.end(),
630 s.path_ids.begin(), s.path_ids.end(),
631 back_inserter(selected));
633 if (not s.reversed) {
634 if (selected_paths.size() == 0 or selected.size() > 0) {
635 glColor3f(1.0, 0.0, 0.0);
637 glColor3f(1.0, 0.8, 0.8);
640 if (selected_paths.size() == 0 or selected.size() > 0) {
641 glColor3f(0.0, 0.0, 1.0);
643 glColor3f(0.8, 0.8, 1.0);
646 if (selected_paths.size() == 0 or selected.size() > 0) {
647 glColor3f(0.0, 0.0, 1.0);
649 glColor3f(0.8, 0.8, 1.0);
653 // save the multipart name for our segment
654 glPushName(path_index); glPushName(key.first); glPushName(key.second);
656 float seq_start_x = track_container[path_index].x();
657 float seq_end_x = track_container[path_index+1].x();
658 glVertex3f(s.start.x + seq_start_x, s.start.y, -1);
659 glVertex3f(s.end.x + seq_end_x , s.end.y, -1);
662 glPopName(); glPopName(); glPopName();