dont draw sequence box when we can see sequence text
[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 GLfloat 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 int GlSequence::get_viewport_width_in_pixels()
282 {
283   GLint viewport[4];
284   glGetIntegerv(GL_VIEWPORT, viewport);
285   return viewport[3]; // grab the viewport width
286 }
287
288 GLfloat GlSequence::pixelWidth(GLfloat left, GLfloat right)
289 {
290   return pixelWidth(left, right, get_viewport_width_in_pixels());
291 }
292
293 GLfloat
294 GlSequence::pixelWidth(GLfloat left, GLfloat right, int vp_width)
295 {
296   return round((right-left)/vp_width);
297 }
298
299 bool GlSequence::is_sequence_renderable(GLfloat left, GLfloat right)
300 {
301   return is_sequence_renderable(left, right, get_viewport_width_in_pixels());
302 }
303
304 bool GlSequence::is_sequence_renderable(GLfloat left, 
305                                         GLfloat right, 
306                                         int viewport_width)
307 {
308   GLfloat world_width = right - left;
309   GLfloat pixels_needed = (char_pix_per_world_unit * world_width);
310
311   // if the number of pixels taken up by rendering the characters 
312   // that'd show up in the current ortho width is less than the window
313   // width we can actually draw something 
314    return pixels_needed < viewport_width;
315 }
316
317
318 void GlSequence::draw(GLfloat left, GLfloat right) const
319 {
320   if ( not is_sequence_renderable(left, right) ) {
321     draw_track(left, right);
322   } else {
323     draw_sequence(left, right);
324   }
325   draw_annotations(left, right);
326 }
327
328 void GlSequence::draw_box(GLfloat world_left, GLfloat world_right,
329                           GLfloat left, GLfloat right, 
330                           GLfloat height, GLfloat y, GLfloat z,
331                           GLint primitive)
332 {
333   GLfloat pixel_width = pixelWidth(world_left, world_right);
334   GLfloat offset = height/2.0;
335   GLfloat top = y + offset;
336   GLfloat bottom = y - offset;
337   
338   // make our box be at least 1 pixel
339   if ((right-left) < pixel_width) {
340     right = left + pixel_width;
341   }
342   glBegin(primitive);
343     glVertex3f(left,  top,    z);
344     glVertex3f(left,  bottom, z);
345     glVertex3f(right, bottom, z);
346     glVertex3f(right, top,    z);
347   glEnd();
348 }
349
350 void GlSequence::draw_track(GLfloat left, GLfloat right) const
351 {
352   // draw main sequence track
353   glColor3fv(color()->get());
354   draw_box(left, right, x(), x()+Sequence::size(), height(), y(), 0.0, GL_LINE_LOOP);
355
356   glColor3f(0.8, 0.8, 0.8);
357   draw_box(left, right, x(), x()+Sequence::size(), height(), y(), 0.0);
358 }
359
360 void GlSequence::draw_annotations(GLfloat left, GLfloat right) const
361 {
362   // draw annotations
363   GLfloat annotation_z = z() + 10.0;
364   const SeqSpanRefList& annots = Sequence::annotations();
365   const MotifList& motifs = Sequence::motifs();
366   for (SeqSpanRefList::const_iterator annot_itor = annots.begin();
367        annot_itor != annots.end();
368        ++annot_itor)
369   {
370     DrawableRef drawable((*annot_itor)->drawable());
371     if (drawable and drawable->drawFunction()) {
372       assert((*annot_itor)->parent() == seq);
373       drawable->drawFunction()((*annot_itor), left, right); 
374     } else {
375       glColor3fv(default_gene_color()->get());
376       draw_box(left, right, x()+(*annot_itor)->start(), x()+(*annot_itor)->stop(), 
377                height(), y(), annotation_z);
378     }
379   }
380   // if motifs?
381   for (MotifList::const_iterator motifs_itor = motifs.begin();
382        motifs_itor != motifs.end();
383        ++motifs_itor)
384   {
385     glColor3fv(color_mapper->lookup("motif", motifs_itor->sequence).get());
386     draw_box(left, right, x()+motifs_itor->begin, x()+motifs_itor->end, 
387              height(), y(), annotation_z+1.0);
388   }
389 }
390
391 // this way of drawing characters, came from the red open gl book
392 const int PT = 1;
393 const int STROKE = 2;
394 const int END =3;
395
396 typedef struct charpoint {
397   GLfloat x, y;
398   int type;
399 } CP;
400
401 CP Adata[] = {
402   {0, -5, PT}, {2.5, 5, PT}, {5, -5, STROKE}, 
403   {0.75, -2, PT}, {4.25, -2, END}
404 };
405
406 CP Tdata[] = {
407   {2.5, -5, PT}, {2.5,5, STROKE}, {0, 5, PT}, {5, 5, END}
408 };
409
410 CP Gdata[] = {
411   {5, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
412   {2, -5, PT}, {3, -5, PT}, {5, -3, STROKE}, 
413   {2.5, -1, PT}, {5, -1,PT}, {5, -5, END}
414 };
415
416 CP Cdata[] = {
417   {4.9, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
418   {2, -5, PT}, {3, -5, PT}, {5, -3, END}
419 };
420
421 CP Xdata[] = {{ 0, 5, PT}, {5, -5,STROKE},{0,-5,PT},{5, 5, END}};
422 CP Ndata[] = {{ 0, -5, PT}, {0, 5, PT}, {5, -5, PT}, {5, 5, END}};
423
424 //! the maximum width used for a charcter glyph
425 const int max_glyph_width = 5; // unit ( glyph_coord )
426
427 static void drawLetter(CP *l, GLfloat z)
428 {
429   glBegin(GL_LINE_STRIP);
430   while(1) {
431     switch (l->type) {
432       case PT:
433         glVertex3f(l->x, l->y, z);
434         break;
435       case STROKE:
436         glVertex3f(l->x, l->y, z);
437         glEnd();
438         glBegin(GL_LINE_STRIP);
439         break;
440       case END:
441         glVertex3f(l->x, l->y, z);
442         glEnd();
443         return;
444         break;
445       default:
446          throw runtime_error("data structure failure");
447     }
448     l++;
449   }
450 }
451
452 void GlSequence::draw_sequence(GLfloat left, GLfloat right) const
453 {
454   // FIXME: basically this needs to be greater than the number of annotations
455   const GLfloat z = 30;
456   glLineWidth(1);
457   glColor3fv(color()->get());
458
459   Sequence::const_iterator seq_itor = region_begin(left, right);
460   Sequence::const_iterator seq_end = region_end(left, right);
461   Sequence::size_type basepair = 0;
462   const float bp_per_world = 1.0; //( world coord )
463   const float glyph_x_scale = 0.125; // unit = ( world coord / glyph coord )
464   // compute how much space there should be to either size of a letter
465   const float glyph_margin = (bp_per_world - glyph_x_scale * max_glyph_width) 
466                            / 2.0;  
467
468   assert(seq_end - seq_itor >= 0);
469   while(seq_itor != seq_end)
470   {
471     assert ( basepair < Sequence::size() );
472     glPushMatrix();
473     glTranslatef( x()+leftbase(left) + basepair + glyph_margin, y(), 1.0 );
474     glScalef(glyph_x_scale, 1.0, 1.0);
475     switch (*seq_itor) {
476       case 'A': case 'a':
477         drawLetter(Adata, z);
478         break;
479       case 'T': case 't':
480         drawLetter(Tdata, z);
481         break;
482       case 'G': case 'g':
483         drawLetter(Gdata, z);
484         break;
485       case 'C': case 'c':
486         drawLetter(Cdata, z);
487         break;
488       case 'N': case 'n':
489         drawLetter(Ndata, z);
490         break;
491       default:
492         drawLetter(Xdata, z);
493         break;
494     }
495     glPopMatrix();
496     ++seq_itor;
497     ++basepair;
498   }
499 }
500
501 bool operator==(const GlSequence &left, const GlSequence &right)
502 {
503   return ( (left.x() == right.x()) and
504            (left.y() == right.y()) and
505            (left.z() == right.z()) and
506            (left.height() == right.height()) and
507            (left.color() == right.color()));
508 }
509
510 void draw_narrow_track(SeqSpanRef s, float left, float right)
511 {
512   SeqSpanRef parent(s->parent());
513   DrawableRef parent_draw(parent->drawable());
514   float x( (parent_draw) ? parent_draw->x() : 0);
515   float y( (parent_draw) ? parent_draw->y() : 0);
516   float z( (parent_draw) ? parent_draw->z() : 10 );
517   float height( (parent_draw) ? parent_draw->height() : default_height ); 
518   Color c( (s->drawable()) ? s->drawable()->color() : *GlSequence::default_track_color() );
519   glColor3fv(c.get());
520   
521   float hsmall = height * 0.25;
522   GlSequence::draw_box(left, right, x+s->start(), x+s->stop(), 
523                        hsmall, y, z+10);
524 }
525
526 void draw_summarized_track(SeqSpanRef s, float left, float right)
527 {
528   // if we can see the sequence text (AGCTN), don't draw the black boxes
529   if (not GlSequence::is_sequence_renderable(left, right)) { 
530     SeqSpanRef parent(s->parent());
531     DrawableRef parent_draw(parent->drawable());
532     float x( (parent_draw) ? parent_draw->x() : 0);
533     float y( (parent_draw) ? parent_draw->y() : 0);
534     float z( (parent_draw) ? parent_draw->z() : 10 );
535     float height( (parent_draw) ? parent_draw->height() : default_height ); 
536     Color c( (s->drawable()) ? s->drawable()->color() : *GlSequence::default_track_color() );
537
538     glColor3fv( c.get() );
539     GlSequence::draw_box(left, right, x+s->start(), x+s->stop(), 
540                          height, y, z+10);
541   }
542 }