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