1 #include "alg/glseqbrowser.hpp"
2 #include "mussa_exceptions.hpp"
10 GlSeqBrowser::GlSeqBrowser()
12 cur_ortho(400.0, 0.0, 600.0, 0.0),
13 viewport_size(600, 400),
14 viewport_center((cur_ortho.right-cur_ortho.left)/2+cur_ortho.left),
21 GlSeqBrowser::GlSeqBrowser(const GlSeqBrowser& gt)
22 : border_width(gt.border_width),
23 cur_ortho(gt.cur_ortho),
24 viewport_size(gt.viewport_size),
25 viewport_center(gt.viewport_center),
26 zoom_level(gt.zoom_level),
27 color_mapper(gt.color_mapper),
28 track_container(gt.track_container)
32 void GlSeqBrowser::initializeGL()
34 glEnable(GL_DEPTH_TEST);
35 glClearColor(1.0, 1.0, 1.0, 0.0);
36 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
37 glShadeModel(GL_FLAT);
40 void GlSeqBrowser::resizeGL(int width, int height)
42 viewport_size.x = width;
43 viewport_size.y = height;
44 glViewport(0, 0, (GLsizei)width, (GLsizei)height);
45 update_viewport(viewport_center, zoom_level);
48 void GlSeqBrowser::paintGL() const
50 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
53 glMatrixMode(GL_PROJECTION);
55 glOrtho(cur_ortho.left, cur_ortho.right,
56 cur_ortho.bottom, cur_ortho.top,
65 void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize, const rect<float>& r)
69 GLuint consumed_names = 0;
74 GLuint path_index = 0;
75 GLuint pair_key_0 = 0;
76 GLuint pair_key_1 = 0;
79 selected_paths.clear();
80 selected_tracks.clear();
82 ptr = (GLuint *) buffer;
85 for (GLuint i=0; i < hits; ++i)
87 if ((i + 5) > bufsize) {
88 std::clog << "*** selection overflow***" << std::endl;
92 z1 = ((float)*ptr++)/0x7fffffff;
93 z2 = ((float)*ptr++)/0x7fffffff;
94 objtype = *ptr++; ++consumed_names;
97 path_index = *ptr++; ++consumed_names;
98 pair_key_0 = *ptr++; ++consumed_names;
99 pair_key_1 = *ptr++; ++consumed_names;
100 if (path_index < path_segments.size()) {
101 segment_key k(pair_key_0, pair_key_1);
102 pair_segment_map::iterator psm_i;
103 psm_i = path_segments[path_index].find(k);
104 if (psm_i != path_segments[path_index].end()) {
105 Segment &seg = psm_i->second;
106 selected_paths.insert(seg.path_ids.begin(), seg.path_ids.end());
108 // else something else is wrong
110 // something wasn't right
111 clog << "invalid path_index " << path_index
112 << " should have been [0,"<<path_segments.size()
118 objid = *ptr++; ++consumed_names;
120 int left = track_container[objid]->leftbase(r.left);
121 int right = track_container[objid]->rightbase(r.right);
122 // the static_cast should be ok, since basepairs line up on
124 //TrackRegion track(objid, left, right);
125 track.set(objid, left, right);
126 selected_tracks.push_back(track);
130 cout << "unknown type " << objtype << " ";
131 for(; consumed_names < names; ++consumed_names) {
132 cout << consumed_names << "," << *ptr++ << " ";
141 void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right)
143 GLfloat x_scale = cur_ortho.width()/((float)viewport_size.x);
144 GLfloat y_scale = cur_ortho.height()/((float)viewport_size.y);
145 GLfloat x_left = cur_ortho.left + (left*x_scale);
146 GLfloat x_right = cur_ortho.left + (right * x_scale);
149 // woah, someone gave us a rectangle with the origin in the lower left
154 // swap the orientation of canvas coordinates
155 GLfloat y_top = cur_ortho.top-(bottom*y_scale);
156 GLfloat y_bottom = cur_ortho.top - top * y_scale;
157 selectedRegion = rect<float>(y_top, x_left, y_bottom, x_right);
159 // hopefully this will make a buffer big enough to receive
160 // everything being selected
161 //const size_t pathz_count = mussaAnalysis->paths().refined_pathz.size();
162 //const GLuint select_buf_size = 1 + 5 * (pathz_count + sequences.size());
163 const GLuint select_buf_size = 500000;
164 GLuint selectBuf[select_buf_size];
165 glSelectBuffer(select_buf_size, selectBuf);
168 (void)glRenderMode(GL_SELECT);
170 glMatrixMode(GL_PROJECTION);
172 glOrtho(x_left, x_right, y_top, y_bottom, -50.0, 50.0);
173 glMatrixMode(GL_MODELVIEW);
180 hits = glRenderMode(GL_RENDER);
181 processSelection(hits, selectBuf, select_buf_size, selectedRegion);
184 float GlSeqBrowser::border() const
189 float GlSeqBrowser::left() const
192 if (track_container.size() == 0)
194 return cur_ortho.left;
196 vector<boost::shared_ptr<GlSequence> >::const_iterator track_i = track_container.begin();
197 left = (*track_i)->x();
198 for( ; track_i != track_container.end(); ++track_i)
200 if ((*track_i)->x() < left) {
201 left = (*track_i)->x();
204 return left-border_width;
208 float GlSeqBrowser::right() const
211 if (track_container.size() == 0) {
212 return cur_ortho.right;
214 vector<boost::shared_ptr<GlSequence> >::const_iterator track_i = track_container.begin();
215 right = (*track_i)->right();
216 for( ; track_i != track_container.end(); ++track_i) {
217 if ((*track_i)->right() > right)
218 right = (*track_i)->right();
220 return right+border_width;
224 void GlSeqBrowser::setViewportCenter(float x)
226 update_viewport(x, zoom_level);
230 float GlSeqBrowser::viewportLeft() const
232 return cur_ortho.left;
235 float GlSeqBrowser::viewportCenter() const
237 return viewport_center;
240 float GlSeqBrowser::viewportRight() const
242 return cur_ortho.right;
245 float GlSeqBrowser::viewportHeight() const
247 return cur_ortho.top - cur_ortho.bottom;
250 float GlSeqBrowser::viewportWidth() const
252 return cur_ortho.right - cur_ortho.left;
255 double GlSeqBrowser::zoomOut()
258 if (right() - left() > 0) {
259 cur_ortho.left = left();
260 cur_ortho.right = right();
261 zoom_level = (right() - left()) / (double)viewport_size.x;
264 // made up number representing 50 bp / pixel
269 double GlSeqBrowser::zoomToSequence()
271 // (experimentally determined zoom level)
272 const double friendly_zoom = 0.10;
273 setZoom(friendly_zoom);
274 return friendly_zoom;
277 void GlSeqBrowser::setZoom(double new_zoom)
279 update_viewport(viewport_center, new_zoom);
280 zoom_level = new_zoom;
283 double GlSeqBrowser::zoom() const
288 void GlSeqBrowser::setColorMapper(boost::shared_ptr<AnnotationColors> cm)
293 const AnnotationColors& GlSeqBrowser::colorMapper()
295 return *color_mapper;
298 void GlSeqBrowser::clear()
302 path_segments.clear();
303 track_container.clear();
306 void GlSeqBrowser::clear_selection()
308 selectedMode = false;
309 selectedRegion.clear();
310 selected_paths.clear();
311 selected_tracks.clear();
314 void GlSeqBrowser::push_sequence(const Sequence& s)
316 boost::shared_ptr<Sequence> seq_copy(new Sequence(s));
317 GlSequence gs(seq_copy, color_mapper);
322 void GlSeqBrowser::push_sequence(boost::shared_ptr<Sequence> s)
324 boost::shared_ptr<GlSequence> gs(new GlSequence(s, color_mapper));
328 void GlSeqBrowser::push_sequence(GlSequence gs)
330 boost::shared_ptr<GlSequence> new_gs(new GlSequence(gs));
331 push_sequence(new_gs);
334 void GlSeqBrowser::push_sequence(boost::shared_ptr<GlSequence> gs)
337 track_container.push_back(gs);
339 if (track_container.size() > 1)
340 path_segments.push_back(pair_segment_map());
343 const std::vector<boost::shared_ptr<GlSequence> >& GlSeqBrowser::sequences() const
345 return track_container;
348 void GlSeqBrowser::clear_links()
350 path_segments.clear();
351 for (int i = track_container.size()-1; i > 0; --i)
353 path_segments.push_back(pair_segment_map());
359 GlSeqBrowser::link(const vector<int>& path, const vector<bool>& rc, int )
361 if (path.size() < 2) {
362 // should i throw an error instead?
365 if (path.size() != track_container.size() ) {
367 msg << "Path size [" << path.size() << "] and track size ["
368 << track_container.size() << "] don't match" << endl;
369 throw mussa_error(msg.str());
371 if (path.size() != rc.size()) {
372 throw runtime_error("path and reverse compliment must be the same length");
374 vector<int>::const_iterator path_i = path.begin();
375 vector<bool>::const_iterator rc_i = rc.begin();
377 int prev_x = *path_i; ++path_i;
378 bool prev_rc = *rc_i; ++rc_i;
379 while (path_i != path.end() and rc_i != rc.end())
381 segment_key p(prev_x, *path_i);
382 pair_segment_map::iterator found_segment = path_segments[track_i].find(p);
383 if (found_segment == path_segments[track_i].end()) {
385 float y1 = track_container[track_i]->y();
386 y1 -= track_container[track_i]->height()/2;
387 float y2 = track_container[track_i+1]->y();
388 y2 += track_container[track_i+1]->height()/2;
390 bool rcFlag = (prev_rc or *rc_i) and !(prev_rc and *rc_i);
391 Segment s(prev_x, y1, *path_i, y2, rcFlag);
392 s.path_ids.insert(pathid);
393 path_segments[track_i][p] = s;
396 found_segment->second.path_ids.insert(pathid);
404 // pathid is reset by push_sequence
408 void GlSeqBrowser::setSelectedPaths(std::vector<int> paths)
410 selected_paths.clear();
411 for(std::vector<int>::iterator itor = paths.begin();
415 selected_paths.insert(*itor);
419 const set<int>& GlSeqBrowser::selectedPaths() const
421 return selected_paths;
424 void GlSeqBrowser::appendSelectedTrack(GLuint track, int start, int stop)
426 selected_tracks.push_back(TrackRegion(track, start, stop));
429 list<TrackRegion> GlSeqBrowser::selectedTracks() const
431 return selected_tracks;
434 //! copy sequence from selected track using formating function
436 void GlSeqBrowser::copySelectedTracks(std::list<Item>& result,
437 Item (*formatter)(boost::shared_ptr<Sequence> s,
443 for(selected_track_iterator track_i = selected_tracks.begin();
444 track_i != selected_tracks.end();
447 int track_index = track_i->track_id;
448 if (track_index >= track_container.size()) {
449 // should this be an exception instead?
450 clog << "track " << track_index << " > " << track_container.size()
454 boost::shared_ptr<Sequence> seq = track_container[track_index]->sequence();
455 result.push_back(formatter(seq, track_i->left, track_i->right));
460 //! copy sequence from selected tracks as FASTA sequences
461 void GlSeqBrowser::copySelectedTracksAsFasta(std::string& copy_buffer)
463 std::list<std::string> result;
465 static string formatter(boost::shared_ptr<Sequence> seq,
470 s << ">" << seq->get_fasta_header()
471 << "|" << "subregion=" << left << "-" << right+1
473 << seq->subseq(left, right-left+1) << std::endl;
477 copySelectedTracks(result, AsFasta::formatter);
478 // I wish there was some way to use for_each and bind here
479 for (list<string>::iterator result_i = result.begin();
480 result_i != result.end();
483 copy_buffer.append(*result_i);
487 //! copy sequence from selected tracks as new sequences
488 void GlSeqBrowser::copySelectedTracksAsSequences(std::list<Sequence>& result)
491 static Sequence formatter(boost::shared_ptr<Sequence> seq,
495 return seq->subseq(left, right-left+1);
498 copySelectedTracks(result, AsSequence::formatter);
501 void GlSeqBrowser::copySelectedTracksAsSeqLocation(
502 std::list<SequenceLocation>& result)
504 struct AsSeqLocation {
505 static SequenceLocation formatter(boost::shared_ptr<Sequence> seq,
509 return SequenceLocation(seq, left, right);
512 copySelectedTracks(result, AsSeqLocation::formatter);
515 //! copy sequence from selected tracks as plain sequences
516 void GlSeqBrowser::copySelectedTracksAsString(std::string& copy_buffer)
518 std::list<string> result;
520 static string formatter(boost::shared_ptr<Sequence> seq,
525 s << seq->subseq(left, right-left+1) << std::endl;
530 copySelectedTracks(result, AsString::formatter);
531 // I wish there was some way to use for_each and bind here
532 for (list<string>::iterator result_i = result.begin();
533 result_i != result.end();
536 copy_buffer.append(*result_i);
541 void GlSeqBrowser::centerOnPath(const vector<int>& paths)
543 if (paths.size() != track_container.size()) {
544 throw mussa_error("Path length didn't match the number of sequences");
547 for(size_t track_i = 0; track_i != track_container.size(); ++track_i)
549 // -15 = shift more to the left
550 track_container[track_i]->setX((viewport_center-15) - paths[track_i]);
554 void GlSeqBrowser::update_viewport(float center, double new_zoom)
556 // limit how close we can get
557 if (new_zoom < 0.01) {
560 double new_width = (new_zoom * (float)viewport_size.x);
561 cur_ortho.left = center-new_width/2.0;
562 cur_ortho.right = center+new_width/2.0;
565 void GlSeqBrowser::update_layout()
567 typedef std::vector<boost::shared_ptr<GlSequence> >::iterator glseq_itor_type;
568 float available_height = (float)cur_ortho.top - 2 * (float)border_width;
569 float max_base_pairs = 0;
570 size_t track_count = track_container.size();
572 if (track_count > 1) {
573 // we have several sequences
574 float track_spacing = available_height / (track_count-1);
575 float y = available_height + (float)border_width;
576 for(glseq_itor_type seq_i = track_container.begin();
577 seq_i != track_container.end();
578 ++seq_i, y-=track_spacing)
582 if ((*seq_i)->size() > max_base_pairs)
583 max_base_pairs = (*seq_i)->size();
585 } else if (track_count == 1) {
586 // center the single track
587 glseq_itor_type seq_i = track_container.begin();
589 (*seq_i)->setY(viewport_size.x /2);
590 max_base_pairs = (*seq_i)->size();
592 // nothing to do as we're empty
595 cur_ortho.right = max_base_pairs + border_width;
596 cur_ortho.left = -border_width;
597 cur_ortho.top = viewport_size.x;
598 cur_ortho.bottom = 0;
599 viewport_center = (cur_ortho.width()/2) + cur_ortho.left;
603 void GlSeqBrowser::draw() const
605 glMatrixMode(GL_MODELVIEW);
607 glPushName(MussaSegment);
609 glLoadName(MussaTrack);
612 // a selection shouldn't have a glName associated with it
616 void GlSeqBrowser::draw_selection() const
618 // draw selection box
620 glDepthMask(GL_FALSE);
622 glColor4f(0.6, 0.6, 0.6, 0.9);
623 glRectf(selectedRegion.left, selectedRegion.top,
624 selectedRegion.right, selectedRegion.bottom);
626 glDepthMask(GL_TRUE);
630 void GlSeqBrowser::draw_tracks() const
632 for(size_t track_i = 0; track_i != track_container.size(); ++track_i)
635 track_container[track_i]->draw(cur_ortho.left, cur_ortho.right);
640 void GlSeqBrowser::draw_segments() const
644 glDepthMask(GL_FALSE);
645 // each vector contains path_segment_maps of all the connections
646 // between this track and the next
647 path_segment_map_vector::const_iterator psmv_i;
648 for(psmv_i = path_segments.begin();
649 psmv_i != path_segments.end();
652 path_segment_map_vector::difference_type path_index;
653 path_index = psmv_i - path_segments.begin();
654 // these maps contain the pair index (used so we dont keep drawing the
655 // same segment) and the actual segment structure.
656 pair_segment_map::const_iterator psm_i;
657 for(psm_i = psmv_i->begin();
658 psm_i != psmv_i->end();
661 // grab the index into our segment map
662 const segment_key& key = psm_i->first;
663 // the second element of our map pair is a segment
664 const Segment &s = psm_i->second;
665 // need to do something so we can detect our selection
666 vector<int> selected;
667 set_intersection(selected_paths.begin(), selected_paths.end(),
668 s.path_ids.begin(), s.path_ids.end(),
669 back_inserter(selected));
671 if (not s.reversed) {
672 if (selected_paths.size() == 0 or selected.size() > 0) {
673 glColor4f(1.0, 0.0, 0.0, 1.0);
675 glColor4f(1.0, 0.7, 0.7, 0.4);
678 if (selected_paths.size() == 0 or selected.size() > 0) {
679 glColor4f(0.0, 0.0, 1.0, 1.0);
681 glColor4f(0.7, 0.7, 1.0, 0.4);
684 if (selected_paths.size() == 0 or selected.size() > 0) {
685 glColor3f(0.0, 0.0, 1.0);
687 glColor3f(0.8, 0.8, 1.0);
691 // save the multipart name for our segment
692 glPushName(path_index); glPushName(key.first); glPushName(key.second);
694 float seq_start_x = track_container[path_index]->x();
695 float seq_end_x = track_container[path_index+1]->x();
696 glVertex3f(s.start.x + seq_start_x, s.start.y, -1);
697 glVertex3f(s.end.x + seq_end_x , s.end.y, -1);
700 glPopName(); glPopName(); glPopName();
703 glDepthMask(GL_TRUE);