+#include "alg/glseqbrowser.hpp"
+#include "mussa_exceptions.hpp"
+
+#include <iostream>
+#include <stdexcept>
+
+using namespace std;
+
+GlSeqBrowser::GlSeqBrowser()
+ : border(25),
+ max_ortho(400.0, 0.0, 600.0, 0.0),
+ cur_ortho(max_ortho),
+ viewport_size(600, 400),
+ viewport_center(0),
+ zoom_level(2),
+ color_mapper(),
+ track_container()
+{
+}
+
+GlSeqBrowser::GlSeqBrowser(const GlSeqBrowser& gt)
+ : border(gt.border),
+ max_ortho(gt.max_ortho),
+ cur_ortho(gt.cur_ortho),
+ viewport_size(gt.viewport_size),
+ viewport_center(gt.viewport_center),
+ zoom_level(gt.zoom_level),
+ color_mapper(gt.color_mapper),
+ track_container(gt.track_container)
+{
+}
+
+void GlSeqBrowser::initializeGL()
+{
+ glEnable(GL_DEPTH_TEST);
+ glClearColor(1.0, 1.0, 1.0, 0.0);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glShadeModel(GL_FLAT);
+}
+
+void GlSeqBrowser::resizeGL(int width, int height)
+{
+ viewport_size.x = width;
+ viewport_size.y = height;
+ glViewport(0, 0, (GLsizei)width, (GLsizei)height);
+ //update_layout();
+}
+
+void GlSeqBrowser::paintGL() const
+{
+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
+
+ glPushMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(cur_ortho.left, cur_ortho.right,
+ cur_ortho.bottom, cur_ortho.top,
+ -50.0, 50);
+
+ draw();
+
+ glPopMatrix();
+ glFlush();
+}
+
+void GlSeqBrowser::processSelection(GLuint hits, GLuint buffer[], GLuint bufsize)
+{
+ GLuint *ptr;
+ GLuint names;
+ GLuint consumed_names = 0;
+ float z1;
+ float z2;
+ GLuint objtype;
+ GLuint objid;
+ GLuint path_index = 0;
+ GLuint pair_key_0 = 0;
+ GLuint pair_key_1 = 0;
+
+ selectedPaths.clear();
+ selectedTracks.clear();
+
+ std::cout << "hits = " << hits << std::endl;
+ ptr = (GLuint *) buffer;
+ if (hits > 0)
+ selectedMode = true;
+ for (GLuint i=0; i < hits; ++i)
+ {
+ if ((i + 5) > bufsize) {
+ std::clog << "*** selection overflow***" << std::endl;
+ } else {
+ consumed_names = 0;
+ names = *ptr++;
+ z1 = ((float)*ptr++)/0x7fffffff;
+ z2 = ((float)*ptr++)/0x7fffffff;
+ objtype = *ptr++; ++consumed_names;
+ switch (objtype) {
+ case MussaSegment:
+ path_index = *ptr++; ++consumed_names;
+ pair_key_0 = *ptr++; ++consumed_names;
+ pair_key_1 = *ptr++; ++consumed_names;
+ if (path_index < path_segments.size()) {
+ segment_key k(pair_key_0, pair_key_1);
+ pair_segment_map::iterator psm_i;
+ psm_i = path_segments[path_index].find(k);
+ if (psm_i != path_segments[path_index].end()) {
+ Segment &seg = psm_i->second;
+ selectedPaths.insert(seg.path_ids.begin(), seg.path_ids.end());
+ }
+ // else something else is wrong
+ } else {
+ // something wasn't right
+ clog << "invalid path_index " << path_index
+ << " should have been [0,"<<path_segments.size()
+ << ") " << endl;
+ }
+ break;
+ case MussaTrack:
+ objid = *ptr++; ++consumed_names;
+ selectedTracks.insert(objid);
+ break;
+ default:
+ cout << "unknown type " << objtype << " ";
+ for(; consumed_names < names; ++consumed_names) {
+ cout << consumed_names << "," << *ptr++ << " ";
+ }
+ cout << endl;
+ break;
+ }
+ }
+ }
+}
+
+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;
+
+ if (top > bottom) {
+ // woah, someone gave us a rectangle with the origin in the lower left
+ int temp = top;
+ bottom = top;
+ top = temp;
+ }
+ // swap the orientation of canvas coordinates
+ GLfloat y_top = cur_ortho.top-(bottom*y_scale);
+ GLfloat y_bottom = cur_ortho.top - top * y_scale;
+ selectedRegion = rect<float>(y_top, x_left, y_bottom, x_right);
+
+ // hopefully this will make a buffer big enough to receive
+ // everything being selected
+ //const size_t pathz_count = mussaAnalysis->paths().refined_pathz.size();
+ //const GLuint select_buf_size = 1 + 5 * (pathz_count + sequences.size());
+ const GLuint select_buf_size = 500000;
+ GLuint selectBuf[select_buf_size];
+ glSelectBuffer(select_buf_size, selectBuf);
+ GLint hits;
+
+ (void)glRenderMode(GL_SELECT);
+ glPushMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(x_left, x_right, y_top, y_bottom, -50.0, 50.0);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ draw();
+
+ glFlush();
+ glPopMatrix();
+ hits = glRenderMode(GL_RENDER);
+ processSelection(hits, selectBuf, select_buf_size);
+}
+
+float GlSeqBrowser::left() const
+{
+ return max_ortho.left;
+}
+
+float GlSeqBrowser::right() const
+{
+ return max_ortho.right;
+}
+
+void GlSeqBrowser::setViewportCenter(float x)
+{
+ update_viewport(x, zoom_level);
+ viewport_center = x;
+}
+
+float GlSeqBrowser::viewportLeft() const
+{
+ return cur_ortho.left;
+}
+
+float GlSeqBrowser::viewportCenter() const
+{
+ return viewport_center;
+}
+
+float GlSeqBrowser::viewportRight() const
+{
+ return cur_ortho.right;
+}
+
+float GlSeqBrowser::viewportHeight() const
+{
+ return cur_ortho.top - cur_ortho.bottom;
+}
+
+float GlSeqBrowser::viewportWidth() const
+{
+ return cur_ortho.right - cur_ortho.left;
+}
+
+void GlSeqBrowser::setZoom(int new_zoom)
+{
+ update_viewport(viewport_center, new_zoom);
+ zoom_level = new_zoom;
+}
+
+int GlSeqBrowser::zoom() const
+{
+ return zoom_level;
+}
+
+void GlSeqBrowser::setColorMapper(AnnotationColors& cm)
+{
+ color_mapper = cm;
+}
+
+AnnotationColors& GlSeqBrowser::colorMapper()
+{
+ return color_mapper;
+}
+
+void GlSeqBrowser::clear()
+{
+ clear_links();
+ path_segments.clear();
+ track_container.clear();
+}
+
+void GlSeqBrowser::push_sequence(const Sequence &s)
+{
+ GlSequence gs(s, color_mapper);
+ push_sequence(gs);
+}
+
+void GlSeqBrowser::push_sequence(GlSequence &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<GlSequence>& GlSeqBrowser::sequences() const
+{
+ return track_container;
+}
+
+void GlSeqBrowser::clear_links()
+{
+ path_segment_map_vector::iterator psmv_i;
+ for(psmv_i = path_segments.begin();
+ psmv_i != path_segments.end();
+ ++psmv_i)
+ {
+ psmv_i->clear();
+ }
+}
+
+void
+GlSeqBrowser::link(const vector<int>& path, const vector<bool>& rc, int length)
+{
+ if (path.size() < 2) {
+ // should i throw an error instead?
+ return;
+ }
+ if (path.size() != rc.size()) {
+ throw runtime_error("path and reverse compliment must be the same length");
+ }
+ vector<int>::const_iterator path_i = path.begin();
+ vector<bool>::const_iterator rc_i = rc.begin();
+ int track_i = 0;
+ int prev_x = *path_i; ++path_i;
+ bool prev_rc = *rc_i; ++rc_i;
+ while (path_i != path.end() and rc_i != rc.end())
+ {
+ segment_key p(prev_x, *path_i);
+ 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);
+ s.path_ids.insert(pathid);
+ path_segments[track_i][p] = s;
+ } else {
+ //found
+ found_segment->second.path_ids.insert(pathid);
+ }
+ prev_x = *path_i;
+ prev_rc = *rc_i;
+ ++track_i;
+ ++path_i;
+ ++rc_i;
+ }
+ // pathid is reset by push_sequence
+ ++pathid;
+}
+
+void GlSeqBrowser::update_viewport(float center, int new_zoom)
+{
+ float max_width = max_ortho.width();
+ // division by zero is a major bummer
+ if (new_zoom < 1) {
+ new_zoom = 1;
+ }
+ float new_max_width = max_width / new_zoom;
+ cur_ortho.left = center-new_max_width;
+ cur_ortho.right = center+new_max_width;
+}
+
+void GlSeqBrowser::update_layout()
+{
+ typedef std::vector<GlSequence>::iterator glseq_itor_type;
+ float available_height = (float)cur_ortho.top - 2 * (float)border;
+ float max_base_pairs = 0;
+ size_t track_count = track_container.size();
+
+ if (track_count > 1) {
+ // we have several sequences
+ float track_spacing = available_height / (track_count-1);
+ float y = available_height + (float)border;
+ for(glseq_itor_type seq_i = track_container.begin();
+ 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();
+ }
+ } 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();
+ } else {
+ // nothing to do as we're empty
+ return;
+ }
+ max_ortho.right = max_base_pairs + border;
+ max_ortho.left = -border;
+ max_ortho.top = viewport_size.x;
+ max_ortho.bottom = 0;
+ cur_ortho = max_ortho;
+ viewport_center = (cur_ortho.width()/2) + cur_ortho.left;
+}
+
+void GlSeqBrowser::draw() const
+{
+ glMatrixMode(GL_MODELVIEW);
+ glInitNames();
+ glPushName(MussaSegment);
+ draw_segments();
+ glLoadName(MussaTrack);
+ draw_tracks();
+ glPopName();
+ // a selection shouldn't have a glName associated with it
+ draw_selection();
+}
+
+void GlSeqBrowser::draw_selection() const
+{
+ // draw selection box
+ glEnable(GL_BLEND);
+ glDepthMask(GL_FALSE);
+ if (selectedMode) {
+ glColor4f(0.6, 0.6, 0.6, 0.9);
+ glRectf(selectedRegion.left, selectedRegion.top,
+ selectedRegion.right, selectedRegion.bottom);
+ }
+ glDepthMask(GL_TRUE);
+ glDisable(GL_BLEND);
+}
+
+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);
+ glPopName();
+ }
+}
+
+void GlSeqBrowser::draw_segments() const
+{
+ glLineWidth(0.1);
+ // each vector contains path_segment_maps of all the connections
+ // between this track and the next
+ path_segment_map_vector::const_iterator psmv_i;
+ for(psmv_i = path_segments.begin();
+ psmv_i != path_segments.end();
+ ++psmv_i)
+ {
+ path_segment_map_vector::difference_type path_index;
+ path_index = psmv_i - path_segments.begin();
+ // these maps contain the pair index (used so we dont keep drawing the
+ // same segment) and the actual segment structure.
+ pair_segment_map::const_iterator psm_i;
+ for(psm_i = psmv_i->begin();
+ psm_i != psmv_i->end();
+ ++psm_i)
+ {
+ // grab the index into our segment map
+ const segment_key& key = psm_i->first;
+ // the second element of our map pair is a segment
+ const Segment &s = psm_i->second;
+ // need to do something so we can detect our selection
+ vector<int> selected;
+ set_intersection(selectedPaths.begin(), selectedPaths.end(),
+ s.path_ids.begin(), s.path_ids.end(),
+ back_inserter(selected));
+
+ if (not s.reversed) {
+ if (selectedPaths.size() == 0 or selected.size() > 0) {
+ glColor3f(1.0, 0.0, 0.0);
+ } else {
+ glColor3f(1.0, 0.8, 0.8);
+ }
+ } else {
+ if (selectedPaths.size() == 0 or selected.size() > 0) {
+ glColor3f(0.0, 0.0, 1.0);
+ } else {
+ glColor3f(0.8, 0.8, 1.0);
+ }
+ }
+ // save the multipart name for our segment
+ glPushName(path_index); glPushName(key.first); glPushName(key.second);
+ glBegin(GL_LINES);
+ glVertex3f(s.start.x, s.start.y, -1);
+ glVertex3f(s.end.x, s.end.y, -1);
+ glEnd();
+ // clear the names
+ glPopName(); glPopName(); glPopName();
+ }
+ }
+}