X-Git-Url: http://woldlab.caltech.edu/gitweb/?a=blobdiff_plain;f=alg%2Fglseqbrowser.cpp;h=04cc792ec441e3eeb375fb2ac47bf842d509b3c0;hb=a4ad0c27cefe62ecb4610706fae5aa34479cc9a7;hp=7a5d683ce22132782bb63d2cbeb52f573c9acca9;hpb=c31ec388dd157f6f88b75e6fa5ef8a6797996fd7;p=mussa.git diff --git a/alg/glseqbrowser.cpp b/alg/glseqbrowser.cpp index 7a5d683..04cc792 100644 --- a/alg/glseqbrowser.cpp +++ b/alg/glseqbrowser.cpp @@ -1,7 +1,9 @@ #include "alg/glseqbrowser.hpp" #include "mussa_exceptions.hpp" +#include #include +#include #include using namespace std; @@ -24,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) { } @@ -61,7 +64,7 @@ void GlSeqBrowser::paintGL() const glFlush(); } -void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize) +void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize, const rect& r) { GLuint *ptr; GLuint names; @@ -73,11 +76,11 @@ void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize GLuint path_index = 0; GLuint pair_key_0 = 0; GLuint pair_key_1 = 0; + TrackRegion track; selected_paths.clear(); selected_tracks.clear(); - std::cout << "hits = " << hits << std::endl; ptr = (GLuint *) buffer; if (hits > 0) selectedMode = true; @@ -113,8 +116,19 @@ void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize } break; case MussaTrack: + { objid = *ptr++; ++consumed_names; - selected_tracks.insert(objid); + + int left = track_container[objid]->leftbase(r.left); + int right = track_container[objid]->rightbase(r.right); + // the static_cast should be ok, since basepairs line up on + // integral values + //TrackRegion track(objid, left, right); + track.set(objid, left, right); + selected_tracks.push_back(track); + //clog << "selected track " << objid + // << "(" << left << ", " << right << ")" << endl; + } break; default: cout << "unknown type " << objtype << " "; @@ -133,7 +147,7 @@ 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 = right * x_scale; + GLfloat x_right = cur_ortho.left + (right * x_scale); if (top > bottom) { // woah, someone gave us a rectangle with the origin in the lower left @@ -168,7 +182,7 @@ void GlSeqBrowser::selectRegion(int top, int left, int bottom, int right) glFlush(); glPopMatrix(); hits = glRenderMode(GL_RENDER); - processSelection(hits, selectBuf, select_buf_size); + processSelection(hits, selectBuf, select_buf_size, selectedRegion); } float GlSeqBrowser::border() const @@ -183,12 +197,12 @@ float GlSeqBrowser::left() const { return cur_ortho.left; } else { - vector::const_iterator track_i = track_container.begin(); - left = track_i->x(); + vector >::const_iterator track_i = track_container.begin(); + left = (*track_i)->x(); for( ; track_i != track_container.end(); ++track_i) { - if (track_i->x() < left) { - left = track_i->x(); + if ((*track_i)->x() < left) { + left = (*track_i)->x(); } } return left-border_width; @@ -201,16 +215,25 @@ float GlSeqBrowser::right() const if (track_container.size() == 0) { return cur_ortho.right; } else { - vector::const_iterator track_i = track_container.begin(); - right = track_i->right(); + vector >::const_iterator track_i = track_container.begin(); + right = (*track_i)->right(); for( ; track_i != track_container.end(); ++track_i) { - if (track_i->right() > right) - right = track_i->right(); + if ((*track_i)->right() > right) + right = (*track_i)->right(); } return right+border_width; } } +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); @@ -242,71 +265,105 @@ float GlSeqBrowser::viewportWidth() const return cur_ortho.right - cur_ortho.left; } -int GlSeqBrowser::zoomOut() +int GlSeqBrowser::viewportPixelHeight() const +{ + return viewport_size.y; +} + +int GlSeqBrowser::viewportPixelWidth() const +{ + return viewport_size.x; +} + +double GlSeqBrowser::zoomOut() { if (right() - left() > 0) { cur_ortho.left = left(); cur_ortho.right = right(); - zoom_level = (int)(( (right() - left()) / viewport_size.x) * 100); + zoom_level = (right() - left()) / (double)viewport_size.x; return zoom_level; } else { // made up number representing 50 bp / pixel - return 5000; + return 50.0; } } -int GlSeqBrowser::zoomToSequence() + +double GlSeqBrowser::zoomToSequence() { // (experimentally determined zoom level) - const int friendly_zoom = 7; + const double friendly_zoom = 0.10; setZoom(friendly_zoom); return friendly_zoom; } -void GlSeqBrowser::setZoom(int new_zoom) + +void GlSeqBrowser::setZoom(double new_zoom) { update_viewport(viewport_center, new_zoom); zoom_level = new_zoom; } -int GlSeqBrowser::zoom() const +double GlSeqBrowser::zoom() const { return zoom_level; } -void GlSeqBrowser::setColorMapper(AnnotationColors& cm) +void GlSeqBrowser::setColorMapper(boost::shared_ptr cm) { color_mapper = cm; } -AnnotationColors& GlSeqBrowser::colorMapper() +const AnnotationColors& GlSeqBrowser::colorMapper() { - return color_mapper; + return *color_mapper; } void GlSeqBrowser::clear() { + clear_selection(); clear_links(); path_segments.clear(); track_container.clear(); } -void GlSeqBrowser::push_sequence(const Sequence &s) +void GlSeqBrowser::clear_selection() +{ + selectedMode = false; + selectedRegion.clear(); + selected_paths.clear(); + selected_tracks.clear(); +} + +void GlSeqBrowser::push_sequence(const Sequence& s) { - GlSequence gs(s, color_mapper); + boost::shared_ptr seq_copy(new Sequence(s)); + GlSequence gs(seq_copy, color_mapper); push_sequence(gs); } -void GlSeqBrowser::push_sequence(GlSequence &gs) + +void GlSeqBrowser::push_sequence(boost::shared_ptr s) +{ + boost::shared_ptr gs(new GlSequence(s, color_mapper)); + push_sequence(gs); +} + +void GlSeqBrowser::push_sequence(GlSequence gs) +{ + boost::shared_ptr new_gs(new GlSequence(gs)); + push_sequence(new_gs); +} + +void GlSeqBrowser::push_sequence(boost::shared_ptr gs) { clear_links(); - pathid = 0; track_container.push_back(gs); update_layout(); if (track_container.size() > 1) path_segments.push_back(pair_segment_map()); } -const std::vector& GlSeqBrowser::sequences() const +const std::vector >& GlSeqBrowser::sequences() const { return track_container; } @@ -318,15 +375,22 @@ void GlSeqBrowser::clear_links() { path_segments.push_back(pair_segment_map()); } + pathid = 0; } void -GlSeqBrowser::link(const vector& path, const vector& rc, int ) +GlSeqBrowser::link(const vector& path, const vector& rc, int length) { if (path.size() < 2) { // should i throw an error instead? return; } + if (path.size() != track_container.size() ) { + stringstream msg; + msg << "Path size [" << path.size() << "] and track size [" + << track_container.size() << "] don't match" << endl; + throw mussa_error(msg.str()); + } if (path.size() != rc.size()) { throw runtime_error("path and reverse compliment must be the same length"); } @@ -341,17 +405,23 @@ GlSeqBrowser::link(const vector& path, const vector& rc, int ) pair_segment_map::iterator found_segment = path_segments[track_i].find(p); if (found_segment == path_segments[track_i].end()) { // not already found - float y1 = track_container[track_i].y(); - y1 -= track_container[track_i].height()/2; - float y2 = track_container[track_i+1].y(); - y2 += track_container[track_i+1].height()/2; - - Segment s(prev_x, y1, *path_i, y2, prev_rc); + float y1 = track_container[track_i]->y(); + y1 -= track_container[track_i]->height()/2; + float y2 = track_container[track_i+1]->y(); + 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, 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; @@ -363,11 +433,143 @@ GlSeqBrowser::link(const vector& path, const vector& rc, int ) ++pathid; } +void GlSeqBrowser::setSelectedPaths(std::vector paths) +{ + selected_paths.clear(); + for(std::vector::iterator itor = paths.begin(); + itor != paths.end(); + ++itor) + { + selected_paths.insert(*itor); + } +} + const set& GlSeqBrowser::selectedPaths() const { return selected_paths; } +void GlSeqBrowser::appendSelectedTrack(GLuint track, int start, int stop) +{ + selected_tracks.push_back(TrackRegion(track, start, stop)); +} + +list GlSeqBrowser::selectedTracks() const +{ + return selected_tracks; +} + +//! copy sequence from selected track using formating function +template +size_t GlSeqBrowser::copySelectedTracks(std::list& result, + Item (*formatter)(boost::shared_ptr s, + int left, + int right)) +{ + size_t base_pairs_copied = 0; + result.clear(); + + for(selected_track_iterator track_i = selected_tracks.begin(); + track_i != selected_tracks.end(); + ++track_i) + { + int track_index = track_i->track_id; + if (track_index >= track_container.size()) { + // should this be an exception instead? + clog << "track " << track_index << " > " << track_container.size() + << endl; + } else { + // we should be safe + boost::shared_ptr seq = track_container[track_index]->sequence(); + 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 +size_t GlSeqBrowser::copySelectedTracksAsFasta(std::string& copy_buffer) +{ + std::list result; + struct AsFasta { + static string formatter(boost::shared_ptr seq, + int left, + int right) + { + stringstream s; + s << ">" << seq->get_fasta_header() + << "|" << "subregion=" << left << "-" << right+1 + << std::endl + << seq->subseq(left, right-left+1) << std::endl; + return s.str(); + } + }; + size_t base_pairs_copied = copySelectedTracks(result, AsFasta::formatter); + // I wish there was some way to use for_each and bind here + for (list::iterator result_i = result.begin(); + result_i != result.end(); + ++result_i) + { + copy_buffer.append(*result_i); + } + return base_pairs_copied; +} + +//! copy sequence from selected tracks as new sequences +size_t GlSeqBrowser::copySelectedTracksAsSequences(std::list& result) +{ + struct AsSequence { + static Sequence formatter(boost::shared_ptr seq, + int left, + int right) + { + return seq->subseq(left, right-left+1); + } + }; + return copySelectedTracks(result, AsSequence::formatter); +} + +size_t GlSeqBrowser::copySelectedTracksAsSeqLocation( + std::list& result) +{ + struct AsSeqLocation { + static SequenceLocation formatter(boost::shared_ptr seq, + int left, + int right) + { + return SequenceLocation(seq, left, right); + } + }; + return copySelectedTracks(result, AsSeqLocation::formatter); +} + +//! copy sequence from selected tracks as plain sequences +size_t GlSeqBrowser::copySelectedTracksAsString(std::string& copy_buffer) +{ + std::list result; + struct AsString { + static string formatter(boost::shared_ptr seq, + int left, + int right) + { + stringstream s; + s << seq->subseq(left, right-left+1); + return s.str(); + } + }; + + size_t base_pairs_copied = copySelectedTracks(result, AsString::formatter); + // I wish there was some way to use for_each and bind here + for (list::iterator result_i = result.begin(); + result_i != result.end(); + ++result_i) + { + copy_buffer.append(*result_i); + } + return base_pairs_copied; +} + void GlSeqBrowser::centerOnPath(const vector& paths) { if (paths.size() != track_container.size()) { @@ -377,24 +579,24 @@ void GlSeqBrowser::centerOnPath(const vector& paths) for(size_t track_i = 0; track_i != track_container.size(); ++track_i) { // -15 = shift more to the left - track_container[track_i].setX((viewport_center-15) - paths[track_i]); + track_container[track_i]->setX((viewport_center-15) - paths[track_i]); } } -void GlSeqBrowser::update_viewport(float center, int new_zoom) +void GlSeqBrowser::update_viewport(float center, double new_zoom) { // limit how close we can get - if (new_zoom < 1) { - new_zoom = 1; + if (new_zoom < 0.01) { + new_zoom = 0.01; } - float new_width = (((float)new_zoom / 100.0) * (float)viewport_size.x); + double new_width = (new_zoom * (float)viewport_size.x); cur_ortho.left = center-new_width/2.0; cur_ortho.right = center+new_width/2.0; } void GlSeqBrowser::update_layout() { - typedef std::vector::iterator glseq_itor_type; + typedef std::vector >::iterator glseq_itor_type; float available_height = (float)cur_ortho.top - 2 * (float)border_width; float max_base_pairs = 0; size_t track_count = track_container.size(); @@ -407,17 +609,17 @@ void GlSeqBrowser::update_layout() seq_i != track_container.end(); ++seq_i, y-=track_spacing) { - seq_i->setX(0); - seq_i->setY(y); - if (seq_i->length() > max_base_pairs) - max_base_pairs = seq_i->length(); + (*seq_i)->setX(0); + (*seq_i)->setY(y); + if ((*seq_i)->size() > max_base_pairs) + max_base_pairs = (*seq_i)->size(); } } else if (track_count == 1) { // center the single track glseq_itor_type seq_i = track_container.begin(); - seq_i->setX(0); - seq_i->setY(viewport_size.x /2); - max_base_pairs = seq_i->length(); + (*seq_i)->setX(0); + (*seq_i)->setY(viewport_size.x /2); + max_base_pairs = (*seq_i)->size(); } else { // nothing to do as we're empty return; @@ -462,14 +664,19 @@ void GlSeqBrowser::draw_tracks() const for(size_t track_i = 0; track_i != track_container.size(); ++track_i) { glPushName(track_i); - track_container[track_i].draw(cur_ortho.left, cur_ortho.right); + track_container[track_i]->draw(cur_ortho.left, cur_ortho.right); glPopName(); } } void GlSeqBrowser::draw_segments() const { - glLineWidth(0.1); + 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; @@ -497,28 +704,54 @@ void GlSeqBrowser::draw_segments() const back_inserter(selected)); if (not s.reversed) { + // forward if (selected_paths.size() == 0 or selected.size() > 0) { - glColor3f(1.0, 0.0, 0.0); + glColor4f(1.0, 0.0, 0.0, 1.0); } else { - glColor3f(1.0, 0.8, 0.8); + glColor4f(1.0, 0.7, 0.7, 0.4); } } else { + // reverse if (selected_paths.size() == 0 or selected.size() > 0) { - glColor3f(0.0, 0.0, 1.0); + glColor4f(0.0, 0.0, 1.0, 1.0); } else { - glColor3f(0.8, 0.8, 1.0); + glColor4f(0.7, 0.7, 1.0, 0.4); } } // 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(); } } + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); }