HACKISH: Display chipseq peak window track
[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 static const float default_height = 12.0;
10 static const float char_pix_per_world_unit = 2.5;
11
12 GlSequence::GlSequence(const Sequence &s, 
13                        boost::shared_ptr<AnnotationColors> cm) 
14   : Sequence(s),
15     color_mapper(cm)
16 {
17   seq->setDrawable(default_drawable());
18 }
19
20 GlSequence::GlSequence(const GlSequence &s)
21   : Sequence(s),
22     color_mapper(s.color_mapper)
23 {
24   seq->setDrawable(copy_drawable(s.seq->drawable()));
25 }
26
27 GlSequence::GlSequence(const GlSequence *s)
28   : Sequence(s),
29     color_mapper(s->color_mapper)
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 {
38   seq->setDrawable(copy_drawable(s->seq->drawable()));
39 }
40
41 GlSequence &GlSequence::operator=(const GlSequence & s)
42 {
43   if (this != &s) {
44     Sequence::operator=(s);
45     seq->setDrawable(copy_drawable(s.seq->drawable()));
46     color_mapper = s.color_mapper;
47   }
48   return *this;
49 }
50
51 DrawableRef GlSequence::default_drawable()
52 {
53   ColorRef c(new Color(0.0, 0.0, 0.0));
54   DrawableRef d(new Drawable(0.0, 0.0, 1.0, default_height, c));
55   return d;
56 }
57
58 DrawableRef GlSequence::copy_drawable(DrawableRef old_d)
59 {
60   ColorRef c(old_d->color());
61   DrawableRef d(new Drawable(old_d));
62   // use the same color
63   d->setColor(c);
64   return d;
65 }
66
67 SeqSpanRef GlSequence::make_drawable_annotation(
68   Drawable::draw_func_ptr draw,
69   std::string name, 
70   size_type start,
71   size_type count,
72   ColorRef color)
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(name));
78   DrawableRef drawable(default_drawable());
79   // glue everything to gether
80   drawable->setDrawFunction(draw);
81   drawable->setColor(color);
82   empty_seq->setAnnotations(empty_seq_annot);
83   empty_seq->setDrawable(drawable);
84   return empty_seq;
85 }
86
87 void GlSequence::add_annotations_for_defined_sequence(Drawable::draw_func_ptr draw)
88 {
89   ColorRef sequence_color(new Color(0.0, 0.0, 0.0));
90   Sequence::const_iterator start_i = begin();
91   Sequence::const_iterator seq_i = begin();
92   Sequence::const_iterator end_i = end();
93   
94   Sequence::const_iterator start_block_i = end();
95   for(; seq_i != end_i; ++seq_i)
96   {
97     // need a better set of characters to serch for
98     if (not (*seq_i == 'N' or *seq_i == 'n')) {
99       if (start_block_i == end_i) {
100         start_block_i = seq_i;
101       }
102     } else {
103       if (start_block_i != end_i) {
104         // we got one.
105         size_type start = start_block_i - start_i;
106         size_type count = seq_i - start_block_i;
107         // add the annotation
108         add_annotation(
109           make_drawable_annotation(draw, "sequence", start, count, 
110                                    sequence_color)
111         );        
112         // reset our counter...
113         start_block_i = end_i;
114       }
115     }
116   }
117   // catch stuff at the end
118   if( start_block_i != end_i ) {
119     size_type start = start_block_i - start_i;
120     size_type count = seq_i - start_block_i;
121     add_annotation(make_drawable_annotation(draw, "sequence", start, count, 
122                                             sequence_color)
123                   );        
124   }
125 }
126
127 void GlSequence::update_annotation_draw_function(
128        std::string type, 
129        Drawable::draw_func_ptr draw_func,
130        ColorRef color
131 )
132 {
133   for(SeqSpanRefList::iterator annot_i = annotation_list->begin();
134       annot_i != annotation_list->end();
135       ++annot_i)
136   {
137     AnnotationsRef metadata( (*annot_i)->annotations() );
138      
139     if (metadata->has_key("type") and metadata->get("type") == type) {
140       // we should update
141       DrawableRef d((*annot_i)->drawable());
142       if (!d) {
143         d = default_drawable();
144         (*annot_i)->setDrawable(d);
145       }
146       d->setDrawFunction(draw_func);
147       d->setColor(color);
148     }
149   }
150 }
151
152 void GlSequence::setX(float value)
153 {
154   seq->drawable()->setX(value);
155 }
156
157 float GlSequence::x() const
158 {
159   return seq->drawable()->x();
160 }
161
162 void GlSequence::setY(GLfloat value)
163 {
164   seq->drawable()->setY(value);
165 }
166
167 float GlSequence::y() const
168 {
169   return seq->drawable()->y();
170 }
171
172 float GlSequence::z() const
173 {
174   return seq->drawable()->z();
175 }
176
177 float GlSequence::height() const
178 {
179   return seq->drawable()->height();
180 }
181
182 GLfloat GlSequence::right() const
183 {
184   return size()+x();
185 }
186
187 Sequence::size_type GlSequence::size() const
188 {
189   return Sequence::size();
190 }
191
192 Sequence::size_type GlSequence::leftbase(GLfloat left) const
193 {
194   left = ceil(left - x());
195   if (left < 0)
196     return 0;
197   else if (left > Sequence::size() )
198     return Sequence::size();
199   else
200     return (Sequence::size_type)left;
201 }
202
203 Sequence::size_type GlSequence::rightbase(GLfloat right) const
204 {
205   right = floor(right) - x();
206   if (right > Sequence::size())
207     return Sequence::size();
208   else if ( right < 0) 
209     return 0;
210   else 
211     return (Sequence::size_type)right;
212 }
213
214 Sequence::const_iterator 
215 GlSequence::region_begin(GLfloat left, GLfloat right) const
216 {
217   if ( leftbase(left) > Sequence::size() or left > right )
218     return Sequence::end();
219   else
220     return Sequence::begin() + leftbase(left);
221 }
222
223 Sequence::const_iterator 
224 GlSequence::region_end(GLfloat left, GLfloat right) const
225 {
226   if ( rightbase(right) > Sequence::size() or left > right )
227     return Sequence::end();
228   else
229     return Sequence::begin() + rightbase(right); 
230 }
231
232 GlSequence GlSequence::subseq(size_type start, size_type count) const
233 {
234   GlSequence new_seq(*this);
235   new_seq.seq = seq->subseq(start, count);
236   // make sure our subseq has a drawable attached to it
237   // perhaps we should figure out correct x,y,z,h coords
238   DrawableRef d(default_drawable());
239   // and default to our current color
240   ColorRef c(color());
241   d->setColor(c);
242   new_seq.seq->setDrawable(d);
243   copy_children(new_seq, start, count);
244   
245   return new_seq;
246 }
247
248 void GlSequence::setColor(ColorRef &c)
249 {
250   seq->drawable()->setColor(c);
251 }
252
253 ColorRef GlSequence::color()
254 {
255   return seq->drawable()->color();
256 }
257
258 const ColorRef GlSequence::color() const
259 {
260   return seq->drawable()->color();
261 }
262
263 ColorRef GlSequence::default_gene_color()
264 {
265   static ColorRef default_color;
266   if (not default_color) {
267     default_color.reset(new Color(0.0, 0.8, 0.0));
268   }
269   return default_color;
270 }
271
272 ColorRef GlSequence::default_track_color()
273 {
274   static ColorRef default_color;
275   if (not default_color) {
276     default_color.reset(new Color(0.0, 0.0, 0.0));
277   }
278   return default_color;
279 }
280
281 ColorRef GlSequence::default_chipseq_color()
282 {
283   static ColorRef default_color;
284   if (not default_color) {
285     default_color.reset(new Color(1.0, 0.5, 0.0));
286   }
287   return default_color;
288 }
289
290 int GlSequence::get_viewport_width_in_pixels()
291 {
292   GLint viewport[4];
293   glGetIntegerv(GL_VIEWPORT, viewport);
294   return viewport[3]; // grab the viewport width
295 }
296
297 GLfloat GlSequence::pixelWidth(GLfloat left, GLfloat right)
298 {
299   return pixelWidth(left, right, get_viewport_width_in_pixels());
300 }
301
302 GLfloat
303 GlSequence::pixelWidth(GLfloat left, GLfloat right, int vp_width)
304 {
305   return round((right-left)/vp_width);
306 }
307
308 bool GlSequence::is_sequence_renderable(GLfloat left, GLfloat right)
309 {
310   return is_sequence_renderable(left, right, get_viewport_width_in_pixels());
311 }
312
313 bool GlSequence::is_sequence_renderable(GLfloat left, 
314                                         GLfloat right, 
315                                         int viewport_width)
316 {
317   GLfloat world_width = right - left;
318   GLfloat pixels_needed = (char_pix_per_world_unit * world_width);
319
320   // if the number of pixels taken up by rendering the characters 
321   // that'd show up in the current ortho width is less than the window
322   // width we can actually draw something 
323    return pixels_needed < viewport_width;
324 }
325
326
327 void GlSequence::draw(GLfloat left, GLfloat right) const
328 {
329   if ( not is_sequence_renderable(left, right) ) {
330     draw_track(left, right);
331   } else {
332     draw_sequence(left, right);
333   }
334   draw_annotations(left, right);
335 }
336
337 void GlSequence::draw_box(GLfloat world_left, GLfloat world_right,
338                           GLfloat left, GLfloat right, 
339                           GLfloat height, GLfloat y, GLfloat z,
340                           GLint primitive)
341 {
342   GLfloat pixel_width = pixelWidth(world_left, world_right);
343   GLfloat offset = height/2.0;
344   GLfloat top = y + offset;
345   GLfloat bottom = y - offset;
346   
347   // make our box be at least 1 pixel
348   if ((right-left) < pixel_width) {
349     right = left + pixel_width;
350   }
351   glBegin(primitive);
352     glVertex3f(left,  top,    z);
353     glVertex3f(left,  bottom, z);
354     glVertex3f(right, bottom, z);
355     glVertex3f(right, top,    z);
356   glEnd();
357 }
358
359 void GlSequence::draw_track(GLfloat left, GLfloat right) const
360 {
361   // draw main sequence track
362   glColor3fv(color()->get());
363   draw_box(left, right, x(), x()+Sequence::size(), height(), y(), 0.0, GL_LINE_LOOP);
364
365   glColor3f(0.8, 0.8, 0.8);
366   draw_box(left, right, x(), x()+Sequence::size(), height(), y(), 0.0);
367 }
368
369 void GlSequence::draw_annotations(GLfloat left, GLfloat right) const
370 {
371   // draw annotations
372   GLfloat annotation_z = z() + 10.0;
373   const SeqSpanRefList& annots = Sequence::annotations();
374   const MotifList& motifs = Sequence::motifs();
375   for (SeqSpanRefList::const_iterator annot_itor = annots.begin();
376        annot_itor != annots.end();
377        ++annot_itor)
378   {
379     DrawableRef drawable((*annot_itor)->drawable());
380     if (drawable and drawable->drawFunction()) {
381       assert((*annot_itor)->parent() == seq);
382       drawable->drawFunction()((*annot_itor), left, right); 
383     } else {
384       glColor3fv(default_gene_color()->get());
385       draw_box(left, right, x()+(*annot_itor)->start(), x()+(*annot_itor)->stop(), 
386                height(), y(), annotation_z);
387     }
388   }
389   // if motifs?
390   for (MotifList::const_iterator motifs_itor = motifs.begin();
391        motifs_itor != motifs.end();
392        ++motifs_itor)
393   {
394     glColor3fv(color_mapper->lookup("motif", motifs_itor->sequence).get());
395     draw_box(left, right, x()+motifs_itor->begin, x()+motifs_itor->end, 
396              height(), y(), annotation_z+1.0);
397   }
398 }
399
400 // this way of drawing characters, came from the red open gl book
401 const int PT = 1;
402 const int STROKE = 2;
403 const int END =3;
404
405 typedef struct charpoint {
406   GLfloat x, y;
407   int type;
408 } CP;
409
410 CP Adata[] = {
411   {0, -5, PT}, {2.5, 5, PT}, {5, -5, STROKE}, 
412   {0.75, -2, PT}, {4.25, -2, END}
413 };
414
415 CP Tdata[] = {
416   {2.5, -5, PT}, {2.5,5, STROKE}, {0, 5, PT}, {5, 5, END}
417 };
418
419 CP Gdata[] = {
420   {5, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
421   {2, -5, PT}, {3, -5, PT}, {5, -3, STROKE}, 
422   {2.5, -1, PT}, {5, -1,PT}, {5, -5, END}
423 };
424
425 CP Cdata[] = {
426   {4.9, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
427   {2, -5, PT}, {3, -5, PT}, {5, -3, END}
428 };
429
430 CP Xdata[] = {{ 0, 5, PT}, {5, -5,STROKE},{0,-5,PT},{5, 5, END}};
431 CP Ndata[] = {{ 0, -5, PT}, {0, 5, PT}, {5, -5, PT}, {5, 5, END}};
432
433 //! the maximum width used for a charcter glyph
434 const int max_glyph_width = 5; // unit ( glyph_coord )
435
436 static void drawLetter(CP *l, GLfloat z)
437 {
438   glBegin(GL_LINE_STRIP);
439   while(1) {
440     switch (l->type) {
441       case PT:
442         glVertex3f(l->x, l->y, z);
443         break;
444       case STROKE:
445         glVertex3f(l->x, l->y, z);
446         glEnd();
447         glBegin(GL_LINE_STRIP);
448         break;
449       case END:
450         glVertex3f(l->x, l->y, z);
451         glEnd();
452         return;
453         break;
454       default:
455          throw runtime_error("data structure failure");
456     }
457     l++;
458   }
459 }
460
461 void GlSequence::draw_sequence(GLfloat left, GLfloat right) const
462 {
463   // FIXME: basically this needs to be greater than the number of annotations
464   const GLfloat z = 30;
465   glLineWidth(1);
466   glColor3fv(color()->get());
467
468   Sequence::const_iterator seq_itor = region_begin(left, right);
469   Sequence::const_iterator seq_end = region_end(left, right);
470   Sequence::size_type basepair = 0;
471   const float bp_per_world = 1.0; //( world coord )
472   const float glyph_x_scale = 0.125; // unit = ( world coord / glyph coord )
473   // compute how much space there should be to either size of a letter
474   const float glyph_margin = (bp_per_world - glyph_x_scale * max_glyph_width) 
475                            / 2.0;  
476
477   assert(seq_end - seq_itor >= 0);
478   while(seq_itor != seq_end)
479   {
480     assert ( basepair < Sequence::size() );
481     glPushMatrix();
482     glTranslatef( x()+leftbase(left) + basepair + glyph_margin, y(), 1.0 );
483     glScalef(glyph_x_scale, 1.0, 1.0);
484     switch (*seq_itor) {
485       case 'A': case 'a':
486         drawLetter(Adata, z);
487         break;
488       case 'T': case 't':
489         drawLetter(Tdata, z);
490         break;
491       case 'G': case 'g':
492         drawLetter(Gdata, z);
493         break;
494       case 'C': case 'c':
495         drawLetter(Cdata, z);
496         break;
497       case 'N': case 'n':
498         drawLetter(Ndata, z);
499         break;
500       default:
501         drawLetter(Xdata, z);
502         break;
503     }
504     glPopMatrix();
505     ++seq_itor;
506     ++basepair;
507   }
508 }
509
510 bool operator==(const GlSequence &left, const GlSequence &right)
511 {
512   return ( (left.x() == right.x()) and
513            (left.y() == right.y()) and
514            (left.z() == right.z()) and
515            (left.height() == right.height()) and
516            (left.color() == right.color()));
517 }
518
519 void draw_narrow_track(SeqSpanRef s, float left, float right)
520 {
521   SeqSpanRef parent(s->parent());
522   DrawableRef parent_draw(parent->drawable());
523   float x( (parent_draw) ? parent_draw->x() : 0);
524   float y( (parent_draw) ? parent_draw->y() : 0);
525   float z( (parent_draw) ? parent_draw->z() : 10 );
526   float height( (parent_draw) ? parent_draw->height() : default_height ); 
527   Color c( (s->drawable()) ? s->drawable()->color() : *GlSequence::default_track_color() );
528   glColor3fv(c.get());
529   
530   float hsmall = height * 0.25;
531   GlSequence::draw_box(left, right, x+s->start(), x+s->stop(), 
532                        hsmall, y, z+10);
533 }
534
535 void draw_chipseq_window(SeqSpanRef s, float left, float right)
536 {
537   SeqSpanRef parent(s->parent());
538   DrawableRef parent_draw(parent->drawable());
539   float x( (parent_draw) ? parent_draw->x() : 0);
540   float y( (parent_draw) ? parent_draw->y() : 0);
541   float z( (parent_draw) ? parent_draw->z() : 10 );
542   float height( (parent_draw) ? parent_draw->height() : default_height ); 
543   Color c( (s->drawable()) ? s->drawable()->color() : *GlSequence::default_track_color() );
544   glColor3fv(c.get());
545   
546   float hsmall = height * 2.0;
547   GlSequence::draw_box(left, right, x+s->start(), x+s->stop(), 
548                        hsmall, y, z+9.0);
549 }
550
551 void draw_summarized_track(SeqSpanRef s, float left, float right)
552 {
553   // if we can see the sequence text (AGCTN), don't draw the black boxes
554   if (not GlSequence::is_sequence_renderable(left, right)) { 
555     SeqSpanRef parent(s->parent());
556     DrawableRef parent_draw(parent->drawable());
557     float x( (parent_draw) ? parent_draw->x() : 0);
558     float y( (parent_draw) ? parent_draw->y() : 0);
559     float z( (parent_draw) ? parent_draw->z() : 10 );
560     float height( (parent_draw) ? parent_draw->height() : default_height ); 
561     Color c( (s->drawable()) ? s->drawable()->color() : *GlSequence::default_track_color() );
562
563     glColor3fv( c.get() );
564     GlSequence::draw_box(left, right, x+s->start(), x+s->stop(), 
565                          height, y, z+10);
566   }
567 }