make annotations big enough to show up
[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, AnnotationColors& cm) 
10   : seq(s),
11     seq_x(0.0), 
12     seq_y(0.0), 
13     seq_z(1.0), 
14     seq_height(12.0),
15     color_mapper(cm),
16     drawColor(0.0, 0.0, 0.0),
17     char_pix_per_world_unit(5.0)
18 {
19 }
20
21 GlSequence::GlSequence(const GlSequence &s)
22   : seq(s.seq),
23     seq_x(s.seq_x),
24     seq_y(s.seq_y),
25     seq_z(s.seq_z),
26     seq_height(s.seq_height),
27     color_mapper(s.color_mapper),
28     drawColor(s.drawColor),
29     char_pix_per_world_unit(s.char_pix_per_world_unit)
30 {
31 }
32
33 GlSequence &GlSequence::operator=(const GlSequence & s)
34 {
35   if (this != &s) {
36     const_cast<Sequence &>(seq) = s.seq;
37     seq_x = s.seq_x;
38     seq_y = s.seq_y;
39     seq_z = s.seq_z;
40     seq_height = s.seq_height;
41     color_mapper = s.color_mapper;
42     drawColor = s.drawColor;
43     assert(char_pix_per_world_unit == s.char_pix_per_world_unit);
44   }
45   return *this;
46 }
47
48 const Sequence& GlSequence::sequence() const
49 {
50   return seq;
51 }
52
53 void GlSequence::setX(GLfloat value)
54 {
55   seq_x = value;
56 }
57
58 GLfloat GlSequence::x() const
59 {
60   return seq_x;
61 }
62
63 GLfloat GlSequence::right() const
64 {
65   return length()+seq_x;
66 }
67
68 void GlSequence::setY(GLfloat value)
69 {
70   seq_y = value;
71 }
72
73 GLfloat GlSequence::y() const
74 {
75   return seq_y;
76 }
77
78 GLfloat GlSequence::height() const
79 {
80   return seq_height;
81 }
82
83 GLfloat GlSequence::length() const
84 {
85   return seq.size();
86 }
87
88 Sequence::size_type GlSequence::leftbase(GLfloat left) const
89 {
90   left = ceil(left - seq_x);
91   if (left < 0)
92     return 0;
93   else if (left > seq.size() )
94     return seq.size();
95   else
96     return (Sequence::size_type)left;
97 }
98
99 Sequence::size_type GlSequence::rightbase(GLfloat right) const
100 {
101   right = floor(right) - seq_x;
102   if (right > seq.size())
103     return seq.size();
104   else if ( right < 0) 
105     return 0;
106   else 
107     return (Sequence::size_type)right;
108 }
109
110 Sequence::const_iterator GlSequence::sequence_begin() const
111 {
112   return seq.begin();
113 }
114
115 Sequence::const_iterator GlSequence::sequence_end() const
116 {
117   return seq.end();
118 }
119
120 Sequence::const_iterator 
121 GlSequence::sequence_begin(GLfloat left, GLfloat right) const
122 {
123   if ( leftbase(left) > seq.size() or left > right )
124     return seq.end();
125   else
126     return seq.begin() + leftbase(left);
127 }
128
129 Sequence::const_iterator 
130 GlSequence::sequence_end(GLfloat left, GLfloat right) const
131 {
132   if ( rightbase(right) > seq.size() or left > right )
133     return seq.end();
134   else
135     return seq.begin() + rightbase(right); 
136 }
137
138
139 //! set default track draw color 
140 void GlSequence::setColor(Color &c)
141 {
142   drawColor = c;
143 }
144
145 //! get default track draw color
146 Color GlSequence::color()
147 {
148   return drawColor;
149 }
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->start, 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->start, seq_x+motifs_itor->end, 
248              seq_height, annotation_z);
249   }
250
251 }
252
253 const int PT = 1;
254 const int STROKE = 2;
255 const int END =3;
256
257 typedef struct charpoint {
258   GLfloat x, y;
259   int type;
260 } CP;
261
262 CP Adata[] = {
263   {0, -5, PT}, {2.5, 5, PT}, {5, -5, STROKE}, 
264   {0.75, -2, PT}, {4.25, -2, END}
265 };
266
267 CP Tdata[] = {
268   {2.5, -5, PT}, {2.5,5, STROKE}, {0, 5, PT}, {5, 5, END}
269 };
270
271 CP Gdata[] = {
272   {5, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
273   {2, -5, PT}, {3, -5, PT}, {5, -3, STROKE}, 
274   {2.5, -1, PT}, {5, -1,PT}, {5, -5, END}
275 };
276
277 CP Cdata[] = {
278   {4.9, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
279   {2, -5, PT}, {3, -5, PT}, {5, -3, END}
280 };
281
282 CP Xdata[] = {{ 0, 5, PT}, {5, -5,STROKE},{0,-5,PT},{5, 5, END}};
283 CP Ndata[] = {{ 0, -5, PT}, {0, 5, PT}, {5, -5, PT}, {5, 5, END}};
284
285 static void drawLetter(CP *l, GLfloat z)
286 {
287   glBegin(GL_LINE_STRIP);
288   while(1) {
289     switch (l->type) {
290       case PT:
291         glVertex3f(l->x, l->y, z);
292         break;
293       case STROKE:
294         glVertex3f(l->x, l->y, z);
295         glEnd();
296         glBegin(GL_LINE_STRIP);
297         break;
298       case END:
299         glVertex3f(l->x, l->y, z);
300         glEnd();
301         return;
302         break;
303       default:
304          throw runtime_error("data structure failure");
305     }
306     l++;
307   }
308 }
309
310 void GlSequence::draw_sequence(GLfloat left, GLfloat right) const
311 {
312   // FIXME: basically this needs to be greater than the number of annotations
313   const GLfloat z = 30;
314   glLineWidth(1);
315   glColor3fv(drawColor.get());
316
317   Sequence::const_iterator seq_itor = sequence_begin(left, right);
318   Sequence::const_iterator seq_end = sequence_end(left, right);
319   Sequence::size_type basepair = 0;
320
321   assert(seq_end - seq_itor >= 0);
322   while(seq_itor != seq_end)
323   {
324     assert ( basepair < seq.size() );
325     glPushMatrix();
326     glTranslatef( seq_x+leftbase(left) + basepair, seq_y, 1.0 );
327     glScalef(0.1, 1.0, 1.0);
328     switch (*seq_itor) {
329       case 'A': case 'a':
330         drawLetter(Adata, z);
331         break;
332       case 'T': case 't':
333         drawLetter(Tdata, z);
334         break;
335       case 'G': case 'g':
336         drawLetter(Gdata, z);
337         break;
338       case 'C': case 'c':
339         drawLetter(Cdata, z);
340         break;
341       case 'N': case 'n':
342         drawLetter(Ndata, z);
343         break;
344       default:
345         drawLetter(Xdata, z);
346         break;
347     }
348     glPopMatrix();
349     ++seq_itor;
350     ++basepair;
351   }
352 }
353
354 bool operator==(const GlSequence &left, const GlSequence &right)
355 {
356   return ( (left.seq_x == right.seq_x) and
357            (left.seq_y == right.seq_y) and
358            (left.seq_z == right.seq_z) and
359            (left.seq_height == right.seq_height) and
360            (left.drawColor == right.drawColor));
361 }
362