add function for marking 'N' regions
[mussa.git] / alg / glsequence.cpp
1 #include "alg/glsequence.hpp"
2
3 #include <iostream>
4 #include <cassert>
5 #include <math.h>
6 #include <stdexcept>
7 using namespace std;
8
9 GlSequence::GlSequence(const Sequence &s, 
10                        boost::shared_ptr<AnnotationColors> cm) 
11   : Sequence(s),
12     color_mapper(cm),
13     char_pix_per_world_unit(2.5)
14 {
15   seq->setDrawable(default_drawable());
16 }
17
18 GlSequence::GlSequence(const GlSequence &s)
19   : Sequence(s),
20     color_mapper(s.color_mapper),
21     char_pix_per_world_unit(s.char_pix_per_world_unit)
22 {
23   seq->setDrawable(copy_drawable(s.seq->drawable()));
24 }
25
26 GlSequence::GlSequence(const GlSequence *s)
27   : Sequence(s),
28     color_mapper(s->color_mapper),
29     char_pix_per_world_unit(s->char_pix_per_world_unit)
30 {
31   seq->setDrawable(copy_drawable(s->seq->drawable()));
32 }
33
34 GlSequence::GlSequence(const GlSequenceRef s)
35   : Sequence( (SequenceRef)s ),
36     color_mapper(s->color_mapper),
37     char_pix_per_world_unit(s->char_pix_per_world_unit)
38 {
39   seq->setDrawable(copy_drawable(s->seq->drawable()));
40 }
41
42 GlSequence &GlSequence::operator=(const GlSequence & s)
43 {
44   if (this != &s) {
45     Sequence::operator=(s);
46     seq->setDrawable(copy_drawable(s.seq->drawable()));
47     color_mapper = s.color_mapper;
48     assert(char_pix_per_world_unit == s.char_pix_per_world_unit);
49   }
50   return *this;
51 }
52
53 DrawableRef GlSequence::default_drawable()
54 {
55   ColorRef c(new Color(0.0, 0.0, 0.0));
56   DrawableRef d(new Drawable(0.0, 0.0, 1.0, 12.0, c));
57   return d;
58 }
59
60 DrawableRef GlSequence::copy_drawable(DrawableRef old_d)
61 {
62   ColorRef c(old_d->color());
63   DrawableRef d(new Drawable(old_d));
64   // use the same color
65   d->setColor(c);
66   return d;
67 }
68
69 SeqSpanRef GlSequence::make_undefined_sequence_annotation(
70   Drawable::draw_func_ptr draw,
71   size_type start,
72   size_type count)
73 {
74   // create all the components of our annotation
75   // (should seq_i-start_i 
76   SeqSpanRef empty_seq(seq->subseq(start, count)); 
77   AnnotationsRef empty_seq_annot(new Annotations("null"));
78   DrawableRef drawable(default_drawable());
79   // glue everything to gether
80   drawable->setDrawFunction(draw);
81   empty_seq->setAnnotations(empty_seq_annot);
82   empty_seq->setDrawable(drawable);
83   return empty_seq;
84 }
85
86 void GlSequence::add_annotations_for_undefined_sequence(Drawable::draw_func_ptr draw)
87 {
88   Sequence::const_iterator start_i = begin();
89   Sequence::const_iterator seq_i = begin();
90   Sequence::const_iterator end_i = end();
91   
92   Sequence::const_iterator start_block_i = end();
93   
94   for(; seq_i != end_i; ++seq_i)
95   {
96     // need a better set of characters to serch for
97     if (*seq_i == 'N' or *seq_i == 'n') {
98       if (start_block_i == end_i) {
99         start_block_i = seq_i;
100       }
101     } else {
102       if (start_block_i != end_i) {
103         // we got one.
104         size_type start = start_block_i - start_i;
105         size_type count = seq_i - start_block_i;
106         // add the annotation
107         add_annotation(make_undefined_sequence_annotation(draw, start, count));        
108         // reset our counter...
109         start_block_i = end_i;
110       }
111     }
112   }
113   // catch stuff at the end
114   if( start_block_i != end_i ) {
115     size_type start = start_block_i - start_i;
116     size_type count = seq_i - start_block_i;
117     add_annotation(make_undefined_sequence_annotation(draw, start, count));        
118   }
119 }
120
121
122 void GlSequence::setX(float value)
123 {
124   seq->drawable()->setX(value);
125 }
126
127 float GlSequence::x() const
128 {
129   return seq->drawable()->x();
130 }
131
132 void GlSequence::setY(GLfloat value)
133 {
134   seq->drawable()->setY(value);
135 }
136
137 float GlSequence::y() const
138 {
139   return seq->drawable()->y();
140 }
141
142 float GlSequence::z() const
143 {
144   return seq->drawable()->z();
145 }
146
147 float GlSequence::height() const
148 {
149   return seq->drawable()->height();
150 }
151
152 GLfloat GlSequence::right() const
153 {
154   return size()+x();
155 }
156
157 GLfloat GlSequence::size() const
158 {
159   return Sequence::size();
160 }
161
162 Sequence::size_type GlSequence::leftbase(GLfloat left) const
163 {
164   left = ceil(left - x());
165   if (left < 0)
166     return 0;
167   else if (left > Sequence::size() )
168     return Sequence::size();
169   else
170     return (Sequence::size_type)left;
171 }
172
173 Sequence::size_type GlSequence::rightbase(GLfloat right) const
174 {
175   right = floor(right) - x();
176   if (right > Sequence::size())
177     return Sequence::size();
178   else if ( right < 0) 
179     return 0;
180   else 
181     return (Sequence::size_type)right;
182 }
183
184 Sequence::const_iterator 
185 GlSequence::region_begin(GLfloat left, GLfloat right) const
186 {
187   if ( leftbase(left) > Sequence::size() or left > right )
188     return Sequence::end();
189   else
190     return Sequence::begin() + leftbase(left);
191 }
192
193 Sequence::const_iterator 
194 GlSequence::region_end(GLfloat left, GLfloat right) const
195 {
196   if ( rightbase(right) > Sequence::size() or left > right )
197     return Sequence::end();
198   else
199     return Sequence::begin() + rightbase(right); 
200 }
201
202 GlSequence GlSequence::subseq(size_type start, size_type count) const
203 {
204   GlSequence new_seq(*this);
205   new_seq.seq = seq->subseq(start, count);
206   // make sure our subseq has a drawable attached to it
207   // perhaps we should figure out correct x,y,z,h coords
208   DrawableRef d(default_drawable());
209   // and default to our current color
210   ColorRef c(color());
211   d->setColor(c);
212   new_seq.seq->setDrawable(d);
213   copy_children(new_seq, start, count);
214   
215   return new_seq;
216 }
217
218 void GlSequence::setColor(ColorRef &c)
219 {
220   seq->drawable()->setColor(c);
221 }
222
223 ColorRef GlSequence::color()
224 {
225   return seq->drawable()->color();
226 }
227
228 const ColorRef GlSequence::color() const
229 {
230   return seq->drawable()->color();
231 }
232
233 int GlSequence::get_viewport_width_in_pixels()
234 {
235   GLint viewport[4];
236   glGetIntegerv(GL_VIEWPORT, viewport);
237   return viewport[3]; // grab the viewport width
238 }
239
240 GLfloat GlSequence::pixelWidth(GLfloat left, GLfloat right) const
241 {
242   return pixelWidth(left, right, get_viewport_width_in_pixels());
243 }
244
245 GLfloat
246 GlSequence::pixelWidth(GLfloat left, GLfloat right, int vp_width) const
247 {
248   return round((right-left)/vp_width);
249 }
250
251 bool GlSequence::is_sequence_renderable(GLfloat left, GLfloat right) const
252 {
253   return is_sequence_renderable(left, right, get_viewport_width_in_pixels());
254 }
255
256 bool GlSequence::is_sequence_renderable(GLfloat left, 
257                                         GLfloat right, 
258                                         int viewport_width) const
259 {
260   GLfloat world_width = right - left;
261   GLfloat pixels_needed = (char_pix_per_world_unit * world_width);
262
263   // if the number of pixels taken up by rendering the characters 
264   // that'd show up in the current ortho width is less than the window
265   // width we can actually draw something 
266    return pixels_needed < viewport_width;
267 }
268
269
270 void GlSequence::draw(GLfloat left, GLfloat right) const
271 {
272   if ( not is_sequence_renderable(left, right) ) {
273     draw_track(left, right);
274   } else {
275     draw_sequence(left, right);
276   }
277   draw_annotations(left, right);
278 }
279
280 void GlSequence::draw_box(GLfloat world_left, GLfloat world_right,
281                           GLfloat left, GLfloat right, 
282                           GLfloat height, GLfloat z) const
283 {
284   GLfloat pixel_width = pixelWidth(world_left, world_right);
285   GLfloat offset = height/2.0;
286   GLfloat top = y() + offset;
287   GLfloat bottom = y() - offset;
288   
289   // make our box be at least 1 pixel
290   if ((right-left) < pixel_width) {
291     right = left + pixel_width;
292   }
293   glBegin(GL_QUADS);
294     glVertex3f(left,  top,    z);
295     glVertex3f(left,  bottom, z);
296     glVertex3f(right, bottom, z);
297     glVertex3f(right, top,    z);
298   glEnd();
299 }
300
301 void GlSequence::draw_track(GLfloat left, GLfloat right) const
302 {
303   glColor3fv(color()->get());
304   // draw main sequence track
305   draw_box(left, right, x(), x()+Sequence::size(), height(), 0.0);
306 }
307
308 void GlSequence::draw_annotations(GLfloat left, GLfloat right) const
309 {
310   // draw annotations
311   GLfloat annotation_z = z() + 10.0;
312   const SeqSpanRefList& annots = Sequence::annotations();
313   const MotifList& motifs = Sequence::motifs();
314   for (SeqSpanRefList::const_iterator annot_itor = annots.begin();
315        annot_itor != annots.end();
316        ++annot_itor)
317   {
318     glColor3f(0.0, 0.8, 0.0);
319     draw_box(left, right, x()+(*annot_itor)->start(), x()+(*annot_itor)->stop(), 
320              height(), annotation_z);
321   }
322   // if motifs?
323   for (MotifList::const_iterator motifs_itor = motifs.begin();
324        motifs_itor != motifs.end();
325        ++motifs_itor)
326   {
327     glColor3fv(color_mapper->lookup("motif", motifs_itor->sequence).get());
328     draw_box(left, right, x()+motifs_itor->begin, x()+motifs_itor->end, 
329              height(), annotation_z+1.0);
330   }
331 }
332
333 // this way of drawing characters, came from the red open gl book
334 const int PT = 1;
335 const int STROKE = 2;
336 const int END =3;
337
338 typedef struct charpoint {
339   GLfloat x, y;
340   int type;
341 } CP;
342
343 CP Adata[] = {
344   {0, -5, PT}, {2.5, 5, PT}, {5, -5, STROKE}, 
345   {0.75, -2, PT}, {4.25, -2, END}
346 };
347
348 CP Tdata[] = {
349   {2.5, -5, PT}, {2.5,5, STROKE}, {0, 5, PT}, {5, 5, END}
350 };
351
352 CP Gdata[] = {
353   {5, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
354   {2, -5, PT}, {3, -5, PT}, {5, -3, STROKE}, 
355   {2.5, -1, PT}, {5, -1,PT}, {5, -5, END}
356 };
357
358 CP Cdata[] = {
359   {4.9, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
360   {2, -5, PT}, {3, -5, PT}, {5, -3, END}
361 };
362
363 CP Xdata[] = {{ 0, 5, PT}, {5, -5,STROKE},{0,-5,PT},{5, 5, END}};
364 CP Ndata[] = {{ 0, -5, PT}, {0, 5, PT}, {5, -5, PT}, {5, 5, END}};
365
366 //! the maximum width used for a charcter glyph
367 const int max_glyph_width = 5; // unit ( glyph_coord )
368
369 static void drawLetter(CP *l, GLfloat z)
370 {
371   glBegin(GL_LINE_STRIP);
372   while(1) {
373     switch (l->type) {
374       case PT:
375         glVertex3f(l->x, l->y, z);
376         break;
377       case STROKE:
378         glVertex3f(l->x, l->y, z);
379         glEnd();
380         glBegin(GL_LINE_STRIP);
381         break;
382       case END:
383         glVertex3f(l->x, l->y, z);
384         glEnd();
385         return;
386         break;
387       default:
388          throw runtime_error("data structure failure");
389     }
390     l++;
391   }
392 }
393
394 void GlSequence::draw_sequence(GLfloat left, GLfloat right) const
395 {
396   // FIXME: basically this needs to be greater than the number of annotations
397   const GLfloat z = 30;
398   glLineWidth(1);
399   glColor3fv(color()->get());
400
401   Sequence::const_iterator seq_itor = region_begin(left, right);
402   Sequence::const_iterator seq_end = region_end(left, right);
403   Sequence::size_type basepair = 0;
404   const float bp_per_world = 1.0; //( world coord )
405   const float glyph_x_scale = 0.125; // unit = ( world coord / glyph coord )
406   // compute how much space there should be to either size of a letter
407   const float glyph_margin = (bp_per_world - glyph_x_scale * max_glyph_width) 
408                            / 2.0;  
409
410   assert(seq_end - seq_itor >= 0);
411   while(seq_itor != seq_end)
412   {
413     assert ( basepair < Sequence::size() );
414     glPushMatrix();
415     glTranslatef( x()+leftbase(left) + basepair + glyph_margin, y(), 1.0 );
416     glScalef(glyph_x_scale, 1.0, 1.0);
417     switch (*seq_itor) {
418       case 'A': case 'a':
419         drawLetter(Adata, z);
420         break;
421       case 'T': case 't':
422         drawLetter(Tdata, z);
423         break;
424       case 'G': case 'g':
425         drawLetter(Gdata, z);
426         break;
427       case 'C': case 'c':
428         drawLetter(Cdata, z);
429         break;
430       case 'N': case 'n':
431         drawLetter(Ndata, z);
432         break;
433       default:
434         drawLetter(Xdata, z);
435         break;
436     }
437     glPopMatrix();
438     ++seq_itor;
439     ++basepair;
440   }
441 }
442
443 bool operator==(const GlSequence &left, const GlSequence &right)
444 {
445   return ( (left.x() == right.x()) and
446            (left.y() == right.y()) and
447            (left.z() == right.z()) and
448            (left.height() == right.height()) and
449            (left.color() == right.color()));
450 }
451