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