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