Thicker bars, better letters
[mussa.git] / gui / ConnView.cxx
1 //  This file is part of the Mussa source distribution.
2 //  http://mussa.caltech.edu/
3 //  Contact author: Tristan  De Buysscher, tristan@caltech.edu
4
5 // This program and all associated source code files are Copyright (C) 2005
6 // the California Institute of Technology, Pasadena, CA, 91125 USA.  It is
7 // under the GNU Public License; please see the included LICENSE.txt
8 // file for more information, or contact Tristan directly.
9
10
11 #include "ConnView.hh"
12
13 #include <iomanip>
14 #include <math.h>
15
16 #include <FL/Fl.H>
17 #include <FL/fl_draw.H>
18
19 using namespace std;
20
21 void
22 ConnView::setup(string name, int sq_num, int win_len,
23                 vector<Sequence> *some_seqs,
24                 NwayPaths *some_paths)
25 {
26   int i, i2, seq_length;
27   Sequence a_seq;
28   motif blank_motif;
29   annot_color new_annot;
30
31
32   analysis_name = name;
33   seq_num = sq_num;
34   window = win_len;
35   S = some_seqs;
36   P = some_paths;
37
38   cout << "num of paths = " << some_paths->refined_pathz.size() << endl;
39
40   cout << "x()=" << x() << " y()=" << y() << " w()=" << w() << " h()=" << h();
41   cout << endl;
42
43   max_seq_len = 0;
44   for(i = 0; i < seq_num; ++i)
45   {
46     a_seq = (*S)[i];
47     cout << a_seq.size() << endl;
48     seq_length = a_seq.size();
49     seq_lens.push_back(seq_length);
50     if (seq_length > max_seq_len)
51       max_seq_len = seq_length;
52     if (seq_length < 1000)
53       seq_scales.push_back(1);
54     else if (seq_length < 1000000)
55       seq_scales.push_back(2);
56     else
57       seq_scales.push_back(3);
58   }
59
60   name_pad = 80;
61   y_pad = 20;
62
63   x_scale_factor = (float) max_seq_len / (w() - name_pad);
64   cout << "scale factor is " << x_scale_factor << endl;
65   y_seq_incre = (h()-(y_pad*2)) / (seq_num - 1);
66
67
68   drag_start = -1000;
69   drag_end = -1000;
70   ref_seq_num = 0;
71
72   dragging = false;
73   selected = false;
74
75   highlight.clear();
76   show_bars = false;
77   show_lines = false;
78   bar_interval = max_seq_len / 20;
79   line_interval = max_seq_len / 20;
80   ref_seq_num = 0;
81
82
83   some_motifs.clear();
84   for(i = 0; i < 5; ++i)
85   {
86     blank_motif = new_blank_motif();
87     some_motifs.push_back(blank_motif);
88   }
89
90   // find all unique annotation type ids
91   some_annots.clear();
92   vector<annot_color>::iterator annot_type_i;
93   list<annot>::iterator annot_i;
94
95   // loop thru all sequences
96   for(i2 = 0; i2 < seq_num; i2++)
97   {
98     cout << "ConnView: annotation loop\n";
99     // loop thru all annotation entries in this sequence
100     for(annot_i = (*S)[i2].annots.begin(); annot_i != (*S)[i2].annots.end(); ++annot_i)
101     {
102       cout << annot_i->start << ", " << annot_i->end << " : ";
103       cout << annot_i->name << "  " << annot_i->type << endl;
104       // loop thru annotation types
105       annot_type_i = some_annots.begin();
106       bool type_unfound = true;
107       while ((annot_type_i != some_annots.end()) && type_unfound)
108       {
109         if (annot_i->type == annot_type_i->type)
110           type_unfound = false;  // stop search if found
111         ++annot_type_i;
112       }
113       //if unfound, then add to list
114       if (type_unfound)
115       {
116         new_annot = new_blank_annot();
117         new_annot.type = annot_i->type;
118         some_annots.push_back(new_annot);
119       } 
120     }
121   }
122 }
123
124
125 void
126 ConnView::scale_paths()
127 {
128   ExtendedConservedPath a_path;
129   list<ExtendedConservedPath >::iterator pathz_i;
130   int i2;
131
132
133   scaled_pathz.clear();
134
135   for(pathz_i = P->refined_pathz.begin(); pathz_i != P->refined_pathz.end(); ++pathz_i)
136   {
137     a_path = *pathz_i;
138     a_path.window_size = (int)(a_path.window_size / x_scale_factor);
139     for(i2 = 0; i2 != a_path.size(); i2++)
140     {
141       a_path[i2] = (int) (a_path[i2] / x_scale_factor);
142     }
143     scaled_pathz.push_back(a_path);
144   }
145 }
146
147 void
148 ConnView::toggle_bars()
149 {
150   show_bars = !show_bars;
151   redraw();
152 }
153
154 void
155 ConnView::set_bar_interval(int new_bar_len)
156 {
157   bar_interval = new_bar_len;
158   cout << "bar interval = " << bar_interval << endl;
159   redraw();
160 }
161
162 void
163 ConnView::toggle_lines()
164 {
165   show_lines = !show_lines;
166   redraw();
167 }
168
169 void
170 ConnView::set_line_interval(int new_line_len)
171 {
172   line_interval = new_line_len;
173   cout << "line interval = " << line_interval << endl;
174   redraw();
175 }
176
177
178 void
179 ConnView::draw()
180 {
181   // this is temporary - check if new motifs have been added
182   check_new_motifs();
183
184   // clear drawing area and set background to white
185   fl_color(FL_WHITE);
186   fl_rectf(x(), y(), w(), h());
187
188   // draw the scale indicators if they are on
189   // put into own method soon...
190   int i, div_num, x_loc; 
191   float div_len_scaled;
192
193   if (show_bars)
194   {
195     div_num = max_seq_len / bar_interval;
196     div_len_scaled = ((float) bar_interval) / x_scale_factor;
197     fl_color(230,230,230);
198     for(i = 0; i <= div_num; i+=2)
199     {
200       x_loc = (int)(i * div_len_scaled+x());
201       fl_rectf(x_loc, y(), (int) div_len_scaled, h());
202     }
203   }
204
205   if (show_lines)
206   {  
207     div_num = max_seq_len / line_interval;
208     div_len_scaled = ((float) line_interval) / x_scale_factor;
209     fl_color(0,0,0);
210     fl_line_style(FL_SOLID, 1, NULL);
211     for(i = 0; i <= div_num; i++)
212     {
213       x_loc = (int)(i * div_len_scaled+x());
214       fl_line(x_loc, y(), x_loc, h()+y());
215     }
216   }
217
218
219   // divide up the space with some light blue lines
220   fl_color(150,200,255);
221   fl_line_style(FL_SOLID, 2, NULL);
222   fl_rect(x(), y(), w(), h());
223   fl_line(w()+x()-name_pad, y(), w()+x()-name_pad, h()+y());
224
225   draw_paths();
226
227   // white out any overdraw from path area into name/info area
228   fl_color(FL_WHITE);
229   fl_rectf(w()+x()-name_pad, y(), w()+x(), h());
230
231   draw_sequence_stuff();
232
233   // draw selection box
234   /*
235   if (selected)
236   {
237     fl_color(FL_BLACK);
238     fl_line_style(FL_SOLID, 1, NULL);
239     fl_rect(drag_start,(ref_seq_num*y_seq_incre)+y(),drag_end-drag_start,
240             16);
241   }
242   */
243 }
244
245
246 void
247 ConnView::draw_paths()
248 {
249   list<ExtendedConservedPath >::iterator i;
250   int i2, i3, y_loc, x_loc, x_start, x_end;
251   list<bool>::iterator highlight_i;
252   int window_size;
253   bool rc_color;
254   int path_start, path_end;
255
256   // determine which paths to highlight
257   highlight.clear();
258   for(i = scaled_pathz.begin(); i != scaled_pathz.end(); ++i)
259   {
260     ExtendedConservedPath& a_path = *i;
261     // determine if path falls within the selected region and mark it for
262     // highlighted color
263     path_start = abs(a_path[ref_seq_num]);
264     path_end = path_start + a_path[0];
265     if ( ( (path_start >= drag_start-x()) && (path_end <= drag_end-x())  ) ||
266          ( (path_start < drag_start-x())  && (path_end > drag_end-x())   ) ||
267          ( (path_start < drag_start-x())  && (path_end > drag_start-x()) ) ||
268          ( (path_start < drag_end-x())    && (path_end > drag_end-x())   )    )
269       highlight.push_back(true);
270     else
271       highlight.push_back(false);
272   }
273
274   fl_line_style(FL_SOLID, 1, NULL);
275   // draw non-highlight paths (ie not in the selection box)
276   highlight_i = highlight.begin();
277   for(i = scaled_pathz.begin(); i != scaled_pathz.end(); ++i)
278   {
279     ExtendedConservedPath& a_path = *i;
280     y_loc = y()+y_pad;
281
282     window_size = a_path.window_size;
283     // make sure width is at least 1  - might be zero to my slack rounding
284     if (window_size == 0)
285       window_size = 1;
286
287     if (!(*highlight_i))
288       for(i2 = 0; i2 < a_path.size()-1; i2++)
289       {
290         // RC case handling
291         // ugh, an xor...only want blue if one of the nodes is rc
292         if ( ((a_path[i2] < 0) || (a_path[i2+1] < 0)) &&
293              !((a_path[i2] < 0) && (a_path[i2+1] < 0)) )
294           fl_color(200,200,255);
295         else
296           fl_color(255,200,200);
297           
298         fl_line_style(FL_SOLID, 1, NULL);
299         fl_polygon((int)abs(a_path[i2])+x(), y_loc + 3,
300                    (int)abs(a_path[i2])+window_size+x(), y_loc + 3,
301                    (int)abs(a_path[i2+1])+window_size+x(), y_loc + y_seq_incre - 3,
302                    (int)abs(a_path[i2+1])+x(), y_loc + y_seq_incre - 3);
303
304         y_loc += y_seq_incre;
305       }
306     ++highlight_i;
307   }
308
309   // draw highlighted paths (ie in or partially in selection)
310   // drawing these separately and after other paths so they are on top
311   highlight_i = highlight.begin();
312   for(i = scaled_pathz.begin(); i != scaled_pathz.end(); ++i)
313   {
314     ExtendedConservedPath& a_path = *i;
315     y_loc = y()+y_pad;
316
317     // get window size to determine line width
318     window_size = a_path.window_size;
319     // make sure width is at least 1  - might be zero to my slack rounding
320     if (window_size == 0)
321       window_size = 1;
322
323     if (*highlight_i)
324       for(i2 = 0; i2 != a_path.size()-1 ; i2++)
325       {
326         // RC case handling
327         // ugh, an xor...only want blue if one of the nodes is rc
328         if ( ((a_path[i2] < 0) || (a_path[i2+1] < 0)) &&
329              !((a_path[i2] < 0) && (a_path[i2+1] < 0)) )
330           fl_color(FL_BLUE);
331         else
332           fl_color(FL_RED);
333           
334         fl_polygon((int)abs(a_path[i2])+x(), y_loc + 3,
335                    (int)abs(a_path[i2])+window_size+x(), y_loc + 3,
336                    (int)abs(a_path[i2+1])+window_size+x(), y_loc + y_seq_incre - 3,
337                    (int)abs(a_path[i2+1])+x(), y_loc + y_seq_incre - 3);
338
339         y_loc += y_seq_incre;
340       }
341     ++highlight_i;
342   }
343 }
344
345 /*
346   list<vector<int> >::iterator i;
347   int window_size;
348   bool rc_color;
349   int path_start, path_end;
350   Sequence a_seq;
351   vector<int> a_path;
352 */
353
354 void
355 ConnView::draw_sequence_stuff()
356 {
357   int i2, i3, y_loc, y_offset, x_loc, x_start, x_end, mv_offset;
358   list<annot>::iterator annot_i;
359   string species_name, seq_length;
360   stringstream raw_length;
361
362
363   // draw sequence representation lines
364   fl_font(FL_COURIER, 14);
365   //fl_color(FL_BLACK);
366   fl_color(100,100,100);
367   // normally size 7, adjust for various screenshotage
368   fl_line_style(FL_SOLID, 11, NULL);
369   y_loc = y()+y_pad;
370   y_offset = 5;
371   for(i2 = 0; i2 < seq_num; i2++)
372   {
373     if (i2 == seq_num - 1)
374       y_offset = -10;
375     x_loc = (int)(seq_lens[i2] / x_scale_factor) + x();
376     //report_float("seq scaled len", x_loc);
377     fl_line(x(),y_loc,x_loc,y_loc);
378
379     species_name = (*S)[i2].species;
380     fl_draw(species_name.c_str(), x()+w()-name_pad+5, y_loc+y_offset);
381
382     // funkiness to figure out which genomic scale to report size in
383     if (seq_scales[i2] == 1)
384       raw_length << setprecision(3) << seq_lens[i2] << " bp";
385     else if (seq_scales[i2] == 2)
386       raw_length << setprecision(3) << seq_lens[i2] / 1000.0 << " Kb";
387     else if (seq_scales[i2] == 3)
388       raw_length << setprecision(3) << seq_lens[i2] /1000000.0<< " Mb";
389     seq_length = raw_length.str();
390     fl_draw(seq_length.c_str(), x()+w()-name_pad+5, y_loc+y_offset+15);
391     raw_length.str("");
392
393     y_loc += y_seq_incre;
394   }
395
396   //fl_line(x(),y()+y_pad/2,w()-name_pad,y()+y_pad/2);
397   //fl_line(x(),y()+h()-y_pad/2,w()-name_pad,y()+h()-y_pad/2);
398
399   // draw annotations
400   vector<annot_color>::iterator annot_type_i;
401
402   fl_color(FL_GREEN);
403   fl_line_style(FL_SOLID, 3, NULL);
404   y_loc = y()+y_pad;
405   for(i2 = 0; i2 < seq_num; i2++)
406   {
407     // loop thru all annotation entries
408     for(annot_i = (*S)[i2].annots.begin(); annot_i != (*S)[i2].annots.end(); ++annot_i)
409     {
410       fl_line_style(FL_SOLID, 7, NULL);
411       mv_offset = 0;
412
413       // loop thru annotation types to determine color
414       annot_type_i = some_annots.begin();
415       bool type_unfound = true;
416       while ((annot_type_i != some_annots.end()) && type_unfound)
417       {
418         if (annot_i->type == annot_type_i->type)
419         {  
420           fl_color(annot_type_i->color);
421           type_unfound = false;
422         }
423         else
424           fl_color(FL_GREEN);
425         ++annot_type_i;
426       }
427
428       // calculate scaled start and end, and draw
429       x_start = (int)(annot_i->start / x_scale_factor) + x();
430       x_end = (int)(annot_i->end / x_scale_factor) + x();
431       fl_line(x_start,y_loc+mv_offset,x_end,y_loc+mv_offset);
432     }
433     y_loc += y_seq_incre;
434   }
435
436
437   // draw motifs found
438   vector<motif>::iterator motif_i;
439   vector<int> some_motif_locs;
440   vector<int>::iterator i_locs;
441   int scale_len, motif_len;
442
443   fl_color(255,0,255);
444   fl_line_style(FL_SOLID, 9, NULL);
445   motif_i = some_motifs.begin();
446   while (motif_i != some_motifs.end())
447   {
448     fl_color(motif_i->color);
449     motif_len = motif_i->seq.length();
450     scale_len = (int) (motif_len / x_scale_factor);
451     if (scale_len == 0)
452       scale_len = 1;
453     y_loc = y()+y_pad;
454     if (!motif_i->locations.empty())
455     for(i2 = 0; i2 < seq_num; i2++)
456     {
457       some_motif_locs = (*motif_i).locations[i2];
458
459       i_locs = some_motif_locs.begin();
460       while (i_locs != some_motif_locs.end())
461       {
462         x_start = (int)(*i_locs / x_scale_factor) + x();
463         fl_line(x_start,y_loc,x_start+scale_len,y_loc);
464         ++i_locs;
465       }
466     y_loc += y_seq_incre;      
467     }
468     ++motif_i;
469   }
470 }
471
472
473 void
474 ConnView::resize(int new_x, int new_y, int new_w, int new_h)
475 {
476
477   x(new_x);
478   y(new_y);
479   w(new_w);
480   h(new_h);
481
482   x_scale_factor = (float) max_seq_len / (w() - name_pad);
483   cout << "scale factor is " << x_scale_factor << endl;
484   y_seq_incre = (h()-(2*y_pad)) / (seq_num - 1);
485
486   scale_paths();
487 }
488
489
490 void
491 ConnView::reporter(string var, int value)
492 {
493   cout << var << " : " << value << endl;
494 }
495
496
497 void
498 ConnView::report_float(string var, float value)
499 {
500   cout << var << " : " << value << endl;
501 }
502
503 int
504 ConnView::handle(int e)
505 {
506   int return_value;
507   float y_calc_tmp;
508
509   // this empty string needs to be put on cout, otherwise the -O optimize
510   // compile option seems to throw this function away with the following:
511   // gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)
512   cout << "";
513
514   // why do I need a -O for this module?  Well, originally I just used the 
515   // same compile flags for all modules.  I changed that.  However, on debian
516   // systems I need the -O otherwise the behavior I observe on redhat machines
517   // with above compiler happens if I don't have that empty cout.  WTF
518
519   switch(e)
520   {
521   case FL_PUSH:
522     if (Fl::event_button3())
523     {
524       spawnSeq();
525       return_value = 1;
526     }
527     else if (Fl::event_button2())
528     {
529       find_motifs();
530       return_value = 1;
531     }
532     break;
533   case FL_DRAG:
534     if (!dragging)
535     {
536       drag_start = Fl::event_x();
537       y_drag_start = (float) Fl::event_y();
538       ref_seq_num = (int) (round ( (y_drag_start/y_seq_incre) ) ); 
539       dragging = true;
540       selected = false;
541       return_value = 1;
542     }
543     fl_line_style(FL_SOLID, 1, NULL);
544     fl_overlay_rect(drag_start, (ref_seq_num*y_seq_incre)+y(),
545                     Fl::event_x()-drag_start, 16);
546     
547     break;
548   case FL_RELEASE:
549     if (dragging)
550     {
551       drag_end = Fl::event_x();
552       dragging = false;
553       selected = true;
554       redraw();
555       return_value = 1;
556     }
557     break;
558   default:
559     return_value = Fl_Widget::handle(e);
560   }
561
562   return return_value;
563 }
564
565
566
567 void
568 ConnView::check_new_motifs()
569 {
570   vector<motif>::iterator i;
571   int i2;
572   vector<int> some_motif_locs;
573   vector<int>::iterator i_locs;
574
575   i = some_motifs.begin();
576   while (i != some_motifs.end())
577   {
578     if (i->dirty)
579     {
580       cout << i->seq << " is new\n";
581       i->locations.clear();
582       //i_locs = i->locations;
583       for(i2 = 0; i2 < seq_num; i2++)
584       {
585         some_motif_locs = (*S)[i2].find_motif(i->seq);
586         (*i).locations.push_back(some_motif_locs);
587
588         
589         i_locs = some_motif_locs.begin();
590         while (i_locs != some_motif_locs.end())
591         {
592           cout << *i_locs << " ";
593           ++i_locs;
594         }
595         cout << endl;
596         
597       }
598
599       i->dirty = false;
600     }
601     ++i;
602   }
603 }
604
605
606 void
607 ConnView::spawnSeq()
608 {
609   list<ExtendedConservedPath > selected_paths;
610   list<ExtendedConservedPath >::iterator pathz_i;
611   int i2, i3, y_loc, x_loc, x_start, x_end;
612   list<bool>::iterator highlight_i;
613   int y_max;
614   string window_name;
615
616   if (selected)
617   {
618     // make new list of connections that are highlighted
619     selected_paths.clear();
620     highlight_i = highlight.begin();
621     for(pathz_i = P->refined_pathz.begin(); 
622         pathz_i != P->refined_pathz.end(); ++pathz_i)
623     {
624       if (*highlight_i)
625       {
626         ExtendedConservedPath& a_path = *pathz_i;
627         selected_paths.push_back(a_path);
628       }
629       ++highlight_i;
630     }
631
632     // give 50 pixels of height per sequence
633     y_max = seq_num * 50;
634     window_name = "Mussa Sequence: " + analysis_name;
635
636     a_seq_win = new SeqWindow(800, y_max, (const char*) window_name.c_str(),
637                               seq_num, S, selected_paths, seq_lens, 
638                               &some_motifs);
639   }
640 }
641
642
643 void
644 ConnView::find_motifs()
645 {
646   motif_find_window = new MotifWindow(300, 300, "Motifs", &some_motifs);
647 }
648
649 void
650 ConnView::annot_win()
651 {
652   annot_color_window = new AnnotWindow(300, 300, "Annotations", &some_annots);
653 }
654
655 // @!@! special hacked in color coding for the myf5/6 region annots
656
657 /*
658       if (annot_i->type == "mvista")
659       {
660         fl_color(255,230,0);
661         fl_line_style(FL_SOLID, 8, NULL);
662         mv_offset = -4;
663       }
664       else if (annot_i->type == "reg")
665       {
666         fl_line_style(FL_SOLID, 10, NULL);
667         mv_offset = -8;
668
669         if (annot_i->name == "CNS")
670           fl_color(255,150,0);
671         else if (annot_i->name == "ELA")
672           fl_color(0,200,120);
673         else if (annot_i->name == "ES")
674           fl_color(0,200,200);
675         else if (annot_i->name == "NA")
676           fl_color(120,120,120);
677         else if (annot_i->name == "EA")
678         {
679           fl_line_style(FL_SOLID, 7, NULL);
680           mv_offset = -13;
681           fl_color(0,200,120);
682         }
683         else if (annot_i->name == "VS")
684         {
685           fl_line_style(FL_SOLID, 7, NULL);
686           mv_offset = -6;
687           fl_color(255,255,0);
688         }
689         else if (annot_i->name == "SP")
690         {
691           fl_line_style(FL_SOLID, 7, NULL);
692           mv_offset = -13;
693           fl_color(255,230,150);
694         }
695         else if (annot_i->name == "L")
696         {
697           fl_line_style(FL_SOLID, 7, NULL);
698           mv_offset = -6;
699           fl_color(0,0,100);
700         }
701         else if (annot_i->name == "TCM")
702           fl_color(0,130,230);
703       }
704 */