73fc4daaeeac20b95fe27769ca2c613748cd0534
[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     seq_x(0.0), 
13     seq_y(0.0), 
14     seq_z(1.0), 
15     seq_height(12.0),
16     color_mapper(cm),
17     drawColor(new Color(0.0, 0.0, 0.0)),
18     char_pix_per_world_unit(2.5)
19 {
20 }
21
22 GlSequence::GlSequence(const GlSequence &s)
23   : Sequence(s),
24     seq_x(s.seq_x),
25     seq_y(s.seq_y),
26     seq_z(s.seq_z),
27     seq_height(s.seq_height),
28     color_mapper(s.color_mapper),
29     drawColor(s.drawColor),
30     char_pix_per_world_unit(s.char_pix_per_world_unit)
31 {
32 }
33
34 GlSequence::GlSequence(const GlSequence *s)
35   : Sequence(s),
36     seq_x(s->seq_x),
37     seq_y(s->seq_y),
38     seq_z(s->seq_z),
39     seq_height(s->seq_height),
40     color_mapper(s->color_mapper),
41     drawColor(s->drawColor),
42     char_pix_per_world_unit(s->char_pix_per_world_unit)
43 {
44 }
45
46 GlSequence &GlSequence::operator=(const GlSequence & s)
47 {
48   if (this != &s) {
49     Sequence::operator=(s);
50     seq_x = s.seq_x;
51     seq_y = s.seq_y;
52     seq_z = s.seq_z;
53     seq_height = s.seq_height;
54     color_mapper = s.color_mapper;
55     drawColor = s.drawColor;
56     assert(char_pix_per_world_unit == s.char_pix_per_world_unit);
57   }
58   return *this;
59 }
60
61 void GlSequence::setX(GLfloat value)
62 {
63   seq_x = value;
64 }
65
66 GLfloat GlSequence::x() const
67 {
68   return seq_x;
69 }
70
71 GLfloat GlSequence::right() const
72 {
73   return size()+seq_x;
74 }
75
76 void GlSequence::setY(GLfloat value)
77 {
78   seq_y = value;
79 }
80
81 GLfloat GlSequence::y() const
82 {
83   return seq_y;
84 }
85
86 GLfloat GlSequence::height() const
87 {
88   return seq_height;
89 }
90
91 GLfloat GlSequence::size() const
92 {
93   return Sequence::size();
94 }
95
96 Sequence::size_type GlSequence::leftbase(GLfloat left) const
97 {
98   left = ceil(left - seq_x);
99   if (left < 0)
100     return 0;
101   else if (left > Sequence::size() )
102     return Sequence::size();
103   else
104     return (Sequence::size_type)left;
105 }
106
107 Sequence::size_type GlSequence::rightbase(GLfloat right) const
108 {
109   right = floor(right) - seq_x;
110   if (right > Sequence::size())
111     return Sequence::size();
112   else if ( right < 0) 
113     return 0;
114   else 
115     return (Sequence::size_type)right;
116 }
117
118 Sequence::const_iterator 
119 GlSequence::region_begin(GLfloat left, GLfloat right) const
120 {
121   if ( leftbase(left) > Sequence::size() or left > right )
122     return Sequence::end();
123   else
124     return Sequence::begin() + leftbase(left);
125 }
126
127 Sequence::const_iterator 
128 GlSequence::region_end(GLfloat left, GLfloat right) const
129 {
130   if ( rightbase(right) > Sequence::size() or left > right )
131     return Sequence::end();
132   else
133     return Sequence::begin() + rightbase(right); 
134 }
135
136 GlSequence GlSequence::subseq(size_type start, size_type count) const
137 {
138   GlSequence new_seq(*this);
139   new_seq.seq = seq->subseq(start, count);
140   copy_children(new_seq, start, count);
141   
142   return new_seq;
143 }
144
145 //! set default track draw color 
146 void GlSequence::setColor(boost::shared_ptr<Color> &c)
147 {
148   drawColor = c;
149 }
150
151 //! get default track draw color
152 boost::shared_ptr<Color> GlSequence::color()
153 {
154   return drawColor;
155 }
156
157 int GlSequence::get_viewport_width_in_pixels()
158 {
159   GLint viewport[4];
160   glGetIntegerv(GL_VIEWPORT, viewport);
161   return viewport[3]; // grab the viewport width
162 }
163
164 GLfloat GlSequence::pixelWidth(GLfloat left, GLfloat right) const
165 {
166   return pixelWidth(left, right, get_viewport_width_in_pixels());
167 }
168
169 GLfloat
170 GlSequence::pixelWidth(GLfloat left, GLfloat right, int vp_width) const
171 {
172   return round((right-left)/vp_width);
173 }
174
175 bool GlSequence::is_sequence_renderable(GLfloat left, GLfloat right) const
176 {
177   return is_sequence_renderable(left, right, get_viewport_width_in_pixels());
178 }
179
180 bool GlSequence::is_sequence_renderable(GLfloat left, 
181                                         GLfloat right, 
182                                         int viewport_width) const
183 {
184   GLfloat world_width = right - left;
185   GLfloat pixels_needed = (char_pix_per_world_unit * world_width);
186
187   // if the number of pixels taken up by rendering the characters 
188   // that'd show up in the current ortho width is less than the window
189   // width we can actually draw something 
190    return pixels_needed < viewport_width;
191 }
192
193
194 void GlSequence::draw(GLfloat left, GLfloat right) const
195 {
196   if ( not is_sequence_renderable(left, right) ) {
197     draw_track(left, right);
198   } else {
199     draw_sequence(left, right);
200   }
201   draw_annotations(left, right);
202 }
203
204 void GlSequence::draw_box(GLfloat world_left, GLfloat world_right,
205                           GLfloat left, GLfloat right, 
206                           GLfloat height, GLfloat z) const
207 {
208   GLfloat pixel_width = pixelWidth(world_left, world_right);
209   GLfloat offset = height/2.0;
210   GLfloat top = seq_y + offset;
211   GLfloat bottom = seq_y - offset;
212   
213   // make our box be at least 1 pixel
214   if ((right-left) < pixel_width) {
215     right = left + pixel_width;
216   }
217   glBegin(GL_QUADS);
218     glVertex3f(left,  top,    z);
219     glVertex3f(left,  bottom, z);
220     glVertex3f(right, bottom, z);
221     glVertex3f(right, top,    z);
222   glEnd();
223 }
224
225 void GlSequence::draw_track(GLfloat left, GLfloat right) const
226 {
227   glColor3fv(drawColor->get());
228   // draw main sequence track
229   draw_box(left, right, seq_x, seq_x+Sequence::size(), seq_height, 0.0);
230 }
231
232 void GlSequence::draw_annotations(GLfloat left, GLfloat right) const
233 {
234   // draw annotations
235   GLfloat annotation_z = seq_z + 10.0;
236   const std::list<annot>& annots = Sequence::annotations();
237   const std::list<motif>& motifs = Sequence::motifs();
238   for (std::list<annot>::const_iterator annot_itor = annots.begin();
239        annot_itor != annots.end();
240        ++annot_itor)
241   {
242     glColor3f(0.0, 0.8, 0.0);
243     draw_box(left, right, seq_x+annot_itor->begin, seq_x+annot_itor->end, 
244              seq_height, annotation_z);
245   }
246   // if motifs?
247   for (std::list<motif>::const_iterator motifs_itor = motifs.begin();
248        motifs_itor != motifs.end();
249        ++motifs_itor)
250   {
251     glColor3fv(color_mapper->lookup("motif", motifs_itor->sequence).get());
252     draw_box(left, right, seq_x+motifs_itor->begin, seq_x+motifs_itor->end, 
253              seq_height, annotation_z+1.0);
254   }
255
256 }
257
258 // this way of drawing characters, came from the red open gl book
259 const int PT = 1;
260 const int STROKE = 2;
261 const int END =3;
262
263 typedef struct charpoint {
264   GLfloat x, y;
265   int type;
266 } CP;
267
268 CP Adata[] = {
269   {0, -5, PT}, {2.5, 5, PT}, {5, -5, STROKE}, 
270   {0.75, -2, PT}, {4.25, -2, END}
271 };
272
273 CP Tdata[] = {
274   {2.5, -5, PT}, {2.5,5, STROKE}, {0, 5, PT}, {5, 5, END}
275 };
276
277 CP Gdata[] = {
278   {5, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
279   {2, -5, PT}, {3, -5, PT}, {5, -3, STROKE}, 
280   {2.5, -1, PT}, {5, -1,PT}, {5, -5, END}
281 };
282
283 CP Cdata[] = {
284   {4.9, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
285   {2, -5, PT}, {3, -5, PT}, {5, -3, END}
286 };
287
288 CP Xdata[] = {{ 0, 5, PT}, {5, -5,STROKE},{0,-5,PT},{5, 5, END}};
289 CP Ndata[] = {{ 0, -5, PT}, {0, 5, PT}, {5, -5, PT}, {5, 5, END}};
290
291 //! the maximum width used for a charcter glyph
292 const int max_glyph_width = 5; // unit ( glyph_coord )
293
294 static void drawLetter(CP *l, GLfloat z)
295 {
296   glBegin(GL_LINE_STRIP);
297   while(1) {
298     switch (l->type) {
299       case PT:
300         glVertex3f(l->x, l->y, z);
301         break;
302       case STROKE:
303         glVertex3f(l->x, l->y, z);
304         glEnd();
305         glBegin(GL_LINE_STRIP);
306         break;
307       case END:
308         glVertex3f(l->x, l->y, z);
309         glEnd();
310         return;
311         break;
312       default:
313          throw runtime_error("data structure failure");
314     }
315     l++;
316   }
317 }
318
319 void GlSequence::draw_sequence(GLfloat left, GLfloat right) const
320 {
321   // FIXME: basically this needs to be greater than the number of annotations
322   const GLfloat z = 30;
323   glLineWidth(1);
324   glColor3fv(drawColor->get());
325
326   Sequence::const_iterator seq_itor = region_begin(left, right);
327   Sequence::const_iterator seq_end = region_end(left, right);
328   Sequence::size_type basepair = 0;
329   const float bp_per_world = 1.0; //( world coord )
330   const float glyph_x_scale = 0.125; // unit = ( world coord / glyph coord )
331   // compute how much space there should be to either size of a letter
332   const float glyph_margin = (bp_per_world - glyph_x_scale * max_glyph_width) 
333                            / 2.0;  
334
335   assert(seq_end - seq_itor >= 0);
336   while(seq_itor != seq_end)
337   {
338     assert ( basepair < Sequence::size() );
339     glPushMatrix();
340     glTranslatef( seq_x+leftbase(left) + basepair + glyph_margin, seq_y, 1.0 );
341     glScalef(glyph_x_scale, 1.0, 1.0);
342     switch (*seq_itor) {
343       case 'A': case 'a':
344         drawLetter(Adata, z);
345         break;
346       case 'T': case 't':
347         drawLetter(Tdata, z);
348         break;
349       case 'G': case 'g':
350         drawLetter(Gdata, z);
351         break;
352       case 'C': case 'c':
353         drawLetter(Cdata, z);
354         break;
355       case 'N': case 'n':
356         drawLetter(Ndata, z);
357         break;
358       default:
359         drawLetter(Xdata, z);
360         break;
361     }
362     glPopMatrix();
363     ++seq_itor;
364     ++basepair;
365   }
366 }
367
368 bool operator==(const GlSequence &left, const GlSequence &right)
369 {
370   return ( (left.seq_x == right.seq_x) and
371            (left.seq_y == right.seq_y) and
372            (left.seq_z == right.seq_z) and
373            (left.seq_height == right.seq_height) and
374            (left.drawColor == right.drawColor));
375 }
376