make the path view independent of type of connection
[mussa.git] / alg / gltracks.cpp
1 #include "alg/gltracks.hpp"
2
3 #include <iostream>
4 #include <stdexcept>
5
6 using namespace std;
7
8 GlTracks::GlTracks()
9   : border(25),
10     max_ortho(400.0, 0.0, 600.0, 0.0),
11     cur_ortho(max_ortho),
12     viewport_size(600, 400),
13     viewport_center(0),
14     zoom_level(2),
15     color_mapper(),
16     track_container()
17 {
18 }
19
20 GlTracks::GlTracks(const GlTracks& gt)
21   : border(gt.border),
22     max_ortho(gt.max_ortho),
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)
29 {
30 }
31
32 void GlTracks::initializeGL()
33 {
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);
38 }
39
40 void GlTracks::resizeGL(int width, int height)
41 {
42   viewport_size.x = width;
43   viewport_size.y = height;
44   glViewport(0, 0, (GLsizei)width, (GLsizei)height);
45   //update_layout();
46 }
47
48 void GlTracks::paintGL() const
49 {
50   glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
51
52   glPushMatrix();
53   glMatrixMode(GL_PROJECTION);
54   glLoadIdentity();
55   glOrtho(cur_ortho.left, cur_ortho.right,
56           cur_ortho.bottom, cur_ortho.top,
57           -50.0, 50);
58
59   draw();
60
61   glPopMatrix();
62   glFlush();
63 }
64
65 float GlTracks::left() const
66
67   return max_ortho.left; 
68 }
69
70 float GlTracks::right() const
71
72   return max_ortho.right; 
73 }
74
75 void GlTracks::setViewportCenter(float x)
76 {
77   update_viewport(x, zoom_level);
78   viewport_center = x;
79 }
80
81 float GlTracks::viewportLeft() const
82
83   return cur_ortho.left; 
84 }
85
86 float GlTracks::viewportCenter() const
87 {
88   return viewport_center;
89 }
90
91 float GlTracks::viewportRight() const
92
93   return cur_ortho.right; 
94 }
95
96 float GlTracks::viewportHeight() const
97 {
98   return cur_ortho.top - cur_ortho.bottom;
99 }
100
101 float GlTracks::viewportWidth() const
102 {
103   return cur_ortho.right - cur_ortho.left;
104 }
105
106 void GlTracks::setZoom(int new_zoom)
107 {
108   update_viewport(viewport_center, new_zoom);
109   zoom_level = new_zoom;
110 }
111
112 int GlTracks::zoom() const
113 {
114   return zoom_level;
115 }
116
117 void GlTracks::setColorMapper(AnnotationColors& cm)
118 {
119   color_mapper = cm;
120 }
121
122 AnnotationColors& GlTracks::colorMapper()
123 {
124   return color_mapper;
125 }
126
127 void GlTracks::clear()
128 {
129   //clear_links();
130   //path_segments.clear();
131   track_container.clear();
132 }
133
134 void GlTracks::push_sequence(const Sequence &s)
135 {
136   GlSequence gs(s, color_mapper);
137   push_sequence(gs);
138 }
139
140 void GlTracks::push_sequence(GlSequence &gs)
141 {
142   //clear_links();
143   pathid = 0;
144   track_container.push_back(gs);
145   update_layout();
146   if (track_container.size() > 1)
147     path_segments.push_back(pair_segment_map());
148 }
149
150 const std::vector<GlSequence>& GlTracks::tracks() const
151 {
152   return track_container;
153 }
154
155 void GlTracks::clear_links()
156 {
157   path_segment_map_vector::iterator psmv_i;
158   for(psmv_i = path_segments.begin();
159       psmv_i != path_segments.end();
160       ++psmv_i)
161   {
162     psmv_i->clear();
163   }
164 }
165
166 void 
167 GlTracks::link(vector<int> path, vector<bool> rc, int length)
168 {
169   if (path.size() < 2) {
170     // should i throw an error instead?
171     return;
172   }
173   if (path.size() != rc.size()) {
174     throw runtime_error("path and reverse compliment must be the same length");
175   }
176   vector<int>::iterator path_i = path.begin();
177   vector<bool>::iterator rc_i = rc.begin();
178   int track_i = 0;
179   int prev_x = *path_i; ++path_i;
180   bool prev_rc = *rc_i; ++rc_i;
181   while (path_i != path.end() and rc_i != rc.end())
182   {
183     segment_key p(prev_x, *path_i);
184     pair_segment_map::iterator found_segment = path_segments[track_i].find(p);
185     if (found_segment == path_segments[track_i].end()) {
186       // not already found
187       float y1 = track_container[track_i].y();
188             y1 -= track_container[track_i].height()/2;
189       float y2 = track_container[track_i+1].y();
190             y2 -= track_container[track_i+1].height()/2;
191             
192       Segment s(prev_x, y1, *path_i, y2, prev_rc);
193       s.path_ids.push_back(pathid);
194       path_segments[track_i][p] = s;
195     } else {
196       //found
197       found_segment->second.path_ids.push_back(pathid);
198     }
199     prev_x = *path_i;
200     prev_rc = *rc_i;
201     ++track_i;
202     ++path_i;
203     ++rc_i;
204   }
205   // pathid is reset by push_sequence
206   ++pathid;
207 }
208
209 void GlTracks::update_viewport(float center, int new_zoom)
210 {
211   float max_width = max_ortho.width();
212   // division by zero is a major bummer
213   if (new_zoom < 1) {
214     new_zoom = 1;
215   }
216   float new_max_width = max_width / new_zoom;
217   cur_ortho.left = center-new_max_width;
218   cur_ortho.right = center+new_max_width;
219 }
220
221 void GlTracks::update_layout()
222 {
223   typedef std::vector<GlSequence>::iterator glseq_itor_type;
224   float available_height = (float)cur_ortho.top - 2 * (float)border;
225   float max_base_pairs = 0;
226   size_t track_count = track_container.size();
227
228   if (track_count > 1) {
229     // we have several tracks
230     float track_spacing = available_height / (track_count-1);
231     float y = available_height + (float)border;
232     for(glseq_itor_type seq_i = track_container.begin();
233         seq_i != track_container.end();
234         ++seq_i, y-=track_spacing)
235     {
236       seq_i->setX(0);
237       seq_i->setY(y);
238       if (seq_i->length() > max_base_pairs)
239         max_base_pairs = seq_i->length();
240     }
241   } else if (track_count == 1) {
242     // center the single track
243     glseq_itor_type seq_i = track_container.begin();
244     seq_i->setX(0);
245     seq_i->setY(viewport_size.x /2);
246     max_base_pairs = seq_i->length();
247   } else {
248     // nothing to do as we're empty
249     return;
250   }
251   max_ortho.right = max_base_pairs + border;
252   max_ortho.left = -border;
253   max_ortho.top = viewport_size.x;
254   max_ortho.bottom = 0;
255   cur_ortho = max_ortho;
256   viewport_center = (cur_ortho.width()/2) + cur_ortho.left;
257 }
258
259 void GlTracks::draw() const
260 {
261   glMatrixMode(GL_MODELVIEW);
262   //glInitNames();
263   //glPushName(MussaPaths);
264   draw_segments();
265   draw_tracks();
266   draw_selection();
267   //glPopName();
268 }
269
270 void GlTracks::draw_selection() const
271 {
272   /* draw selection box
273   glEnable(GL_BLEND);
274   glDepthMask(GL_FALSE);
275   if (selectedMode && !drawingBand) {
276     glColor4f(0.6, 0.6, 0.6, 0.9);
277     glRectf(previousBand.x(), previousBand.y(),
278             previousBand.right(), previousBand.bottom());
279   }
280   glDepthMask(GL_TRUE);
281   glDisable(GL_BLEND);
282   */
283 }
284
285 void GlTracks::draw_tracks() const
286 {
287   typedef std::vector<GlSequence>::const_iterator glseq_citor_type;
288   for(glseq_citor_type seq_i = track_container.begin();
289       seq_i != track_container.end();
290       ++seq_i)
291   {
292     seq_i->draw(cur_ortho.left, cur_ortho.right);
293   }
294 }
295
296 void GlTracks::draw_segments() const
297 {
298   glLineWidth(0.1);
299   glBegin(GL_LINES);
300   // each vector contains path_segment_maps of all the connections
301   // between this track and the next
302   path_segment_map_vector::const_iterator psmv_i;
303   for(psmv_i = path_segments.begin();
304       psmv_i != path_segments.end();
305       ++psmv_i)
306   {
307     // these maps contain the pair index (used so we dont keep drawing the
308     // same segment) and the actual segment structure.
309     pair_segment_map::const_iterator psm_i;
310     for(psm_i = psmv_i->begin();
311         psm_i != psmv_i->end();
312         ++psm_i)
313     {
314       // the second element of our map pair is a segment
315       const Segment &s = psm_i->second;
316       // need to do something so we can detect our selection
317       if (not s.reversed) {
318         glColor3f(1.0, 0.0, 0.0);
319       } else { 
320         glColor3f(0.0, 0.0, 1.0);
321       }
322       glVertex3f(s.start.x, s.start.y, -1);
323       glVertex3f(s.end.x, s.end.y, -1);
324     }
325   }
326   glEnd();
327 }