hopefully this fixes the off by one error
[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_pixel_width()
153 {
154   int viewport[4];
155   glGetIntegerv(GL_VIEWPORT, viewport);
156   return viewport[3]; // grab the viewport width
157 }
158
159 bool GlSequence::is_sequence_renderable(GLfloat left, 
160                                         GLfloat right, 
161                                         int viewport_width) const
162 {
163   // if called with default argument, go get the viewable width
164   if (viewport_width == -1) {
165     viewport_width = get_viewport_pixel_width();
166   }
167   GLfloat world_width = right - left;
168   GLfloat pixels_needed = (char_pix_per_world_unit * world_width);
169
170   // if the number of pixels taken up by rendering the characters 
171   // that'd show up in the current ortho width is less than the window
172   // width we can actually draw something 
173    return pixels_needed < viewport_width;
174 }
175
176
177 void GlSequence::draw(GLfloat left, GLfloat right) const
178 {
179   if ( not is_sequence_renderable(left, right) ) {
180     draw_track(left, right);
181   } else {
182     draw_sequence(left, right);
183   }
184   draw_annotations(left, right);
185 }
186
187 void GlSequence::draw_box(GLfloat left, GLfloat right, 
188                           GLfloat height, GLfloat z) const
189 {
190   GLfloat offset = height/2.0;
191   GLfloat top = seq_y + offset;
192   GLfloat bottom = seq_y - offset;
193     
194   glBegin(GL_QUADS);
195     glVertex3f(left,  top,    z);
196     glVertex3f(left,  bottom, z);
197     glVertex3f(right, bottom, z);
198     glVertex3f(right, top,    z);
199   glEnd();
200 }
201
202 void GlSequence::draw_track(GLfloat left, GLfloat right) const
203 {
204   glColor3fv(drawColor.get());
205   // draw main sequence track
206   draw_box(seq_x, seq_x+seq.size(), seq_height, 0.0);
207 }
208
209 void GlSequence::draw_annotations(GLfloat left, GLfloat right) const
210 {
211   // draw annotations
212   glLineWidth(seq_height);
213   GLfloat annotation_z = seq_z + 1.0;
214   const std::list<annot>& annots = seq.annotations();
215   const std::list<motif>& motifs = seq.motifs();
216   for (std::list<annot>::const_iterator annot_itor = annots.begin();
217        annot_itor != annots.end();
218        ++annot_itor, ++annotation_z)
219   {
220     glColor3f(0.0, 0.5, 0.0);
221     draw_box(seq_x+annot_itor->start, seq_x+annot_itor->end, 
222              seq_height, annotation_z);
223   }
224   // if motifs?
225   for (std::list<motif>::const_iterator motifs_itor = motifs.begin();
226        motifs_itor != motifs.end();
227        ++motifs_itor, ++annotation_z)
228   {
229     glColor3fv(color_mapper.lookup("motif", motifs_itor->sequence).get());
230     draw_box(seq_x+motifs_itor->start, seq_x+motifs_itor->end, 
231              seq_height, annotation_z);
232   }
233
234 }
235
236 const int PT = 1;
237 const int STROKE = 2;
238 const int END =3;
239
240 typedef struct charpoint {
241   GLfloat x, y;
242   int type;
243 } CP;
244
245 CP Adata[] = {
246   {0, -5, PT}, {2.5, 5, PT}, {5, -5, STROKE}, 
247   {0.75, -2, PT}, {4.25, -2, END}
248 };
249
250 CP Tdata[] = {
251   {2.5, -5, PT}, {2.5,5, STROKE}, {0, 5, PT}, {5, 5, END}
252 };
253
254 CP Gdata[] = {
255   {5, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
256   {2, -5, PT}, {3, -5, PT}, {5, -3, STROKE}, 
257   {2.5, -1, PT}, {5, -1,PT}, {5, -5, END}
258 };
259
260 CP Cdata[] = {
261   {4.9, 3, PT}, {3, 5, PT}, {2, 5, PT}, {0, 3, PT}, {0, -3, PT},
262   {2, -5, PT}, {3, -5, PT}, {5, -3, END}
263 };
264
265 CP Xdata[] = {{ 0, 5, PT}, {5, -5,STROKE},{0,-5,PT},{5, 5, END}};
266 CP Ndata[] = {{ 0, -5, PT}, {0, 5, PT}, {5, -5, PT}, {5, 5, END}};
267
268 static void drawLetter(CP *l, GLfloat z)
269 {
270   glBegin(GL_LINE_STRIP);
271   while(1) {
272     switch (l->type) {
273       case PT:
274         glVertex3f(l->x, l->y, z);
275         break;
276       case STROKE:
277         glVertex3f(l->x, l->y, z);
278         glEnd();
279         glBegin(GL_LINE_STRIP);
280         break;
281       case END:
282         glVertex3f(l->x, l->y, z);
283         glEnd();
284         return;
285         break;
286       default:
287          throw runtime_error("data structure failure");
288     }
289     l++;
290   }
291 }
292
293 void GlSequence::draw_sequence(GLfloat left, GLfloat right) const
294 {
295   // FIXME: basically this needs to be greater than the number of annotations
296   const GLfloat z = 30;
297   glLineWidth(0.3);
298   glColor3fv(drawColor.get());
299
300   Sequence::const_iterator seq_itor = sequence_begin(left, right);
301   Sequence::const_iterator seq_end = sequence_end(left, right);
302   Sequence::size_type basepair = 0;
303
304   assert(seq_end - seq_itor >= 0);
305   while(seq_itor != seq_end)
306   {
307     assert ( basepair < seq.size() );
308     glPushMatrix();
309     glTranslatef( seq_x+leftbase(left) + basepair, seq_y, 1.0 );
310     glScalef(0.1, 1.0, 1.0);
311     switch (*seq_itor) {
312       case 'A': case 'a':
313         drawLetter(Adata, z);
314         break;
315       case 'T': case 't':
316         drawLetter(Tdata, z);
317         break;
318       case 'G': case 'g':
319         drawLetter(Gdata, z);
320         break;
321       case 'C': case 'c':
322         drawLetter(Cdata, z);
323         break;
324       case 'N': case 'n':
325         drawLetter(Ndata, z);
326         break;
327       default:
328         drawLetter(Xdata, z);
329         break;
330     }
331     glPopMatrix();
332     ++seq_itor;
333     ++basepair;
334   }
335 }
336
337 bool operator==(const GlSequence &left, const GlSequence &right)
338 {
339   return ( (left.seq_x == right.seq_x) and
340            (left.seq_y == right.seq_y) and
341            (left.seq_z == right.seq_z) and
342            (left.seq_height == right.seq_height) and
343            (left.drawColor == right.drawColor));
344 }
345