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