Allow changing the name for a sequence in the sequence browser.
[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
153 int GlSequence::get_viewport_width_in_pixels()
154 {
155   GLint viewport[4];
156   glGetIntegerv(GL_VIEWPORT, viewport);
157   return viewport[3]; // grab the viewport width
158 }
159
160 GLfloat GlSequence::get_pixel_width(GLfloat left, GLfloat right) const
161 {
162   return get_pixel_width(left, right, get_viewport_width_in_pixels());
163 }
164
165 GLfloat
166 GlSequence::get_pixel_width(GLfloat left, GLfloat right, int vp_width) const
167 {
168   return round((right-left)/vp_width);
169 }
170
171 bool GlSequence::is_sequence_renderable(GLfloat left, GLfloat right) const
172 {
173   return is_sequence_renderable(left, right, get_viewport_width_in_pixels());
174 }
175
176 bool GlSequence::is_sequence_renderable(GLfloat left, 
177                                         GLfloat right, 
178                                         int viewport_width) const
179 {
180   GLfloat world_width = right - left;
181   GLfloat pixels_needed = (char_pix_per_world_unit * world_width);
182
183   // if the number of pixels taken up by rendering the characters 
184   // that'd show up in the current ortho width is less than the window
185   // width we can actually draw something 
186    return pixels_needed < viewport_width;
187 }
188
189
190 void GlSequence::draw(GLfloat left, GLfloat right) const
191 {
192   if ( not is_sequence_renderable(left, right) ) {
193     draw_track(left, right);
194   } else {
195     draw_sequence(left, right);
196   }
197   draw_annotations(left, right);
198 }
199
200 void GlSequence::draw_box(GLfloat world_left, GLfloat world_right,
201                           GLfloat left, GLfloat right, 
202                           GLfloat height, GLfloat z) const
203 {
204   GLfloat pixel_width = get_pixel_width(world_left, world_right);
205   GLfloat offset = height/2.0;
206   GLfloat top = seq_y + offset;
207   GLfloat bottom = seq_y - offset;
208   
209   // make our box be at least 1 pixel
210   if ((right-left) < pixel_width) {
211     right = left + pixel_width;
212   }
213   glBegin(GL_QUADS);
214     glVertex3f(left,  top,    z);
215     glVertex3f(left,  bottom, z);
216     glVertex3f(right, bottom, z);
217     glVertex3f(right, top,    z);
218   glEnd();
219 }
220
221 void GlSequence::draw_track(GLfloat left, GLfloat right) const
222 {
223   glColor3fv(drawColor.get());
224   // draw main sequence track
225   draw_box(left, right, seq_x, seq_x+seq->size(), seq_height, 0.0);
226 }
227
228 void GlSequence::draw_annotations(GLfloat left, GLfloat right) const
229 {
230   // draw annotations
231   GLfloat annotation_z = seq_z + 10.0;
232   const std::list<annot>& annots = seq->annotations();
233   const std::list<motif>& motifs = seq->motifs();
234   for (std::list<annot>::const_iterator annot_itor = annots.begin();
235        annot_itor != annots.end();
236        ++annot_itor)
237   {
238     glColor3f(0.0, 0.8, 0.0);
239     draw_box(left, right, seq_x+annot_itor->begin, seq_x+annot_itor->end, 
240              seq_height, annotation_z);
241   }
242   // if motifs?
243   for (std::list<motif>::const_iterator motifs_itor = motifs.begin();
244        motifs_itor != motifs.end();
245        ++motifs_itor)
246   {
247     glColor3fv(color_mapper->lookup("motif", motifs_itor->sequence).get());
248     draw_box(left, right, seq_x+motifs_itor->begin, seq_x+motifs_itor->end, 
249              seq_height, annotation_z+1.0);
250   }
251
252 }
253
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 static void drawLetter(CP *l, GLfloat z)
287 {
288   glBegin(GL_LINE_STRIP);
289   while(1) {
290     switch (l->type) {
291       case PT:
292         glVertex3f(l->x, l->y, z);
293         break;
294       case STROKE:
295         glVertex3f(l->x, l->y, z);
296         glEnd();
297         glBegin(GL_LINE_STRIP);
298         break;
299       case END:
300         glVertex3f(l->x, l->y, z);
301         glEnd();
302         return;
303         break;
304       default:
305          throw runtime_error("data structure failure");
306     }
307     l++;
308   }
309 }
310
311 void GlSequence::draw_sequence(GLfloat left, GLfloat right) const
312 {
313   // FIXME: basically this needs to be greater than the number of annotations
314   const GLfloat z = 30;
315   glLineWidth(1);
316   glColor3fv(drawColor.get());
317
318   Sequence::const_iterator seq_itor = sequence_begin(left, right);
319   Sequence::const_iterator seq_end = sequence_end(left, right);
320   Sequence::size_type basepair = 0;
321
322   assert(seq_end - seq_itor >= 0);
323   while(seq_itor != seq_end)
324   {
325     assert ( basepair < seq->size() );
326     glPushMatrix();
327     glTranslatef( seq_x+leftbase(left) + basepair, seq_y, 1.0 );
328     glScalef(0.1, 1.0, 1.0);
329     switch (*seq_itor) {
330       case 'A': case 'a':
331         drawLetter(Adata, z);
332         break;
333       case 'T': case 't':
334         drawLetter(Tdata, z);
335         break;
336       case 'G': case 'g':
337         drawLetter(Gdata, z);
338         break;
339       case 'C': case 'c':
340         drawLetter(Cdata, z);
341         break;
342       case 'N': case 'n':
343         drawLetter(Ndata, z);
344         break;
345       default:
346         drawLetter(Xdata, z);
347         break;
348     }
349     glPopMatrix();
350     ++seq_itor;
351     ++basepair;
352   }
353 }
354
355 bool operator==(const GlSequence &left, const GlSequence &right)
356 {
357   return ( (left.seq_x == right.seq_x) and
358            (left.seq_y == right.seq_y) and
359            (left.seq_z == right.seq_z) and
360            (left.seq_height == right.seq_height) and
361            (left.drawColor == right.drawColor));
362 }
363