Add support RNAML format
[jalview.git] / src / jalview / renderer / AnnotationRenderer.java
1 package jalview.renderer;
2
3 import jalview.analysis.AAFrequency;
4 import jalview.analysis.StructureFrequency;
5 import jalview.api.AlignViewportI;
6 import jalview.datamodel.AlignmentAnnotation;
7 import jalview.datamodel.Annotation;
8 import jalview.datamodel.ColumnSelection;
9 import jalview.schemes.ColourSchemeI;
10
11 import java.awt.BasicStroke;
12 import java.awt.Color;
13 import java.awt.Font;
14 import java.awt.FontMetrics;
15 import java.awt.Graphics;
16 import java.awt.Graphics2D;
17 import java.awt.Image;
18 import java.awt.font.LineMetrics;
19 import java.awt.geom.AffineTransform;
20 import java.awt.image.ImageObserver;
21 import java.util.Hashtable;
22
23 import com.stevesoft.pat.Regex;
24
25 public class AnnotationRenderer
26 {
27
28   public AnnotationRenderer()
29   {
30     // TODO Auto-generated constructor stub
31   }
32
33   public void drawStemAnnot(Graphics g, Annotation[] row_annotations,
34           int lastSSX, int x, int y, int iconOffset, int startRes,
35           int column, boolean validRes, boolean validEnd)
36   {
37         
38     g.setColor(STEM_COLOUR);
39     int sCol = (lastSSX / charWidth) + startRes;
40     int x1 = lastSSX;
41     int x2 = (x * charWidth);
42     Regex closeparen = new Regex("(\\))");
43
44     String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
45             : row_annotations[column - 1].displayCharacter;
46
47     boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
48             || !dc.equals(row_annotations[sCol - 1].displayCharacter);
49     boolean diffdownstream = !validRes || !validEnd
50             || row_annotations[column] == null
51             || !dc.equals(row_annotations[column].displayCharacter);
52     // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
53     // If a closing base pair half of the stem, display a backward arrow
54     if (column > 0 && closeparen.search(dc))
55     {
56         
57       if (diffupstream)
58       // if (validRes && column>1 && row_annotations[column-2]!=null &&
59       // dc.equals(row_annotations[column-2].displayCharacter))
60       {
61         g.fillPolygon(new int[]
62         { lastSSX + 5, lastSSX + 5, lastSSX }, new int[]
63         { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
64         x1 += 5;
65       }
66       if (diffdownstream)
67       {
68         x2 -= 1;
69       }
70     }
71     else
72     {
73         
74       // display a forward arrow
75       if (diffdownstream)
76       {
77         g.fillPolygon(new int[]
78         { x2 - 5, x2 - 5, x2 }, new int[]
79         { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
80         x2 -= 5;
81       }
82       if (diffupstream)
83       {
84         x1 += 1;
85       }
86     }
87     // draw arrow body
88     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
89   }
90
91   private int charWidth, endRes, charHeight;
92
93   private boolean validCharWidth, hasHiddenColumns;
94
95   private FontMetrics fm;
96
97   private final boolean MAC = new jalview.util.Platform().isAMac();
98
99   boolean av_renderHistogram = true, av_renderProfile = true,
100           av_normaliseProfile = false;
101
102   ColourSchemeI profcolour = null;
103
104   private ColumnSelection columnSelection;
105
106   private Hashtable[] hconsensus;
107
108   private Hashtable[] hStrucConsensus;
109
110   private boolean av_ignoreGapsConsensus;
111
112   /**
113    * attributes set from AwtRenderPanelI
114    */
115   /**
116    * old image used when data is currently being calculated and cannot be
117    * rendered
118    */
119   private Image fadedImage;
120
121   /**
122    * panel being rendered into
123    */
124   private ImageObserver annotationPanel;
125
126   /**
127    * width of image to render in panel
128    */
129   private int imgWidth;
130
131   
132   public void drawNotCanonicalAnnot(Graphics g, Annotation[] row_annotations,
133           int lastSSX, int x, int y, int iconOffset, int startRes,
134           int column, boolean validRes, boolean validEnd)
135   {
136         
137     g.setColor(NOTCANONICAL_COLOUR);
138     int sCol = (lastSSX / charWidth) + startRes;
139     int x1 = lastSSX;
140     int x2 = (x * charWidth);
141     Regex closeparen = new Regex("(}|]|a|b|c|d)");
142     
143     String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
144             : row_annotations[column - 1].displayCharacter;
145
146     boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
147             || !dc.equals(row_annotations[sCol - 1].displayCharacter);
148     boolean diffdownstream = !validRes || !validEnd
149             || row_annotations[column] == null
150             || !dc.equals(row_annotations[column].displayCharacter);
151     // System.out.println("Column "+column+" diff up: "+diffupstream+" down:"+diffdownstream);
152     // If a closing base pair half of the stem, display a backward arrow
153     if (column > 0 && closeparen.search(dc))//  closeletter_b.search(dc)||closeletter_c.search(dc)||closeletter_d.search(dc)||closecrochet.search(dc)) )
154     {
155         
156       if (diffupstream)
157       // if (validRes && column>1 && row_annotations[column-2]!=null &&
158       // dc.equals(row_annotations[column-2].displayCharacter))
159       {
160         g.fillPolygon(new int[]
161         { lastSSX + 5, lastSSX + 5, lastSSX }, new int[]
162         { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
163         x1 += 5;
164       }
165       if (diffdownstream)
166       {
167         x2 -= 1;
168       }
169     }
170     else
171     {
172         
173       // display a forward arrow
174       if (diffdownstream)
175       {
176         g.fillPolygon(new int[]
177         { x2 - 5, x2 - 5, x2 }, new int[]
178         { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3);
179         x2 -= 5;
180       }
181       if (diffupstream)
182       {
183         x1 += 1;
184       }
185     }
186     // draw arrow body
187     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7);
188   }
189   // public void updateFromAnnotationPanel(FontMetrics annotFM, AlignViewportI
190   // av)
191   public void updateFromAwtRenderPanel(AwtRenderPanelI annotPanel,
192           AlignViewportI av)
193   {
194     fm = annotPanel.getFontMetrics();
195     annotationPanel = annotPanel;
196     fadedImage = annotPanel.getFadedImage();
197     imgWidth = annotPanel.getFadedImageWidth();
198     updateFromAlignViewport(av);
199   }
200
201   public void updateFromAlignViewport(AlignViewportI av)
202   {
203     charWidth = av.getCharWidth();
204     endRes = av.getEndRes();
205     charHeight = av.getCharHeight();
206     hasHiddenColumns = av.hasHiddenColumns();
207     validCharWidth = av.isValidCharWidth();
208     av_renderHistogram = av.isShowConsensusHistogram();
209     av_renderProfile = av.isShowSequenceLogo();
210     av_normaliseProfile = av.isNormaliseSequenceLogo();
211     profcolour = av.getGlobalColourScheme();
212     if (profcolour == null)
213     {
214       // Set the default colour for sequence logo if the alignnent has no
215       // colourscheme set
216       profcolour = av.getAlignment().isNucleotide() ? new jalview.schemes.NucleotideColourScheme()
217               : new jalview.schemes.ZappoColourScheme();
218     }
219     columnSelection = av.getColumnSelection();
220     hconsensus = av.getSequenceConsensusHash();// hconsensus;
221     hStrucConsensus = av.getRnaStructureConsensusHash(); // hStrucConsensus;
222     av_ignoreGapsConsensus = av.getIgnoreGapsConsensus();
223   }
224
225   public int[] getProfileFor(AlignmentAnnotation aa, int column)
226   {
227     // TODO : consider refactoring the global alignment calculation
228     // properties/rendering attributes as a global 'alignment group' which holds
229     // all vis settings for the alignment as a whole rather than a subset
230     //
231     if (aa.autoCalculated && aa.label.startsWith("Consensus"))
232     {
233       if (aa.groupRef != null && aa.groupRef.consensusData != null
234               && aa.groupRef.isShowSequenceLogo())
235       {
236         return AAFrequency.extractProfile(
237                 aa.groupRef.consensusData[column],
238                 aa.groupRef.getIgnoreGapsConsensus());
239       }
240       // TODO extend annotation row to enable dynamic and static profile data to
241       // be stored
242       if (aa.groupRef == null && aa.sequenceRef == null && av_renderProfile)
243       {
244         return AAFrequency.extractProfile(hconsensus[column],
245                 av_ignoreGapsConsensus);
246       }
247     }
248     else
249     {
250       if (aa.autoCalculated && aa.label.startsWith("StrucConsensus"))
251       {
252         // TODO implement group structure consensus
253         /*
254          * if (aa.groupRef != null && aa.groupRef.consensusData != null &&
255          * aa.groupRef.isShowSequenceLogo()) { //TODO check what happens for
256          * group selections return StructureFrequency.extractProfile(
257          * aa.groupRef.consensusData[column], aa.groupRef
258          * .getIgnoreGapsConsensus()); }
259          */
260         // TODO extend annotation row to enable dynamic and static profile data
261         // to
262         // be stored
263         if (aa.groupRef == null && aa.sequenceRef == null
264                 && av_renderProfile && hStrucConsensus != null
265                 && hStrucConsensus.length > column)
266         {
267           return StructureFrequency.extractProfile(hStrucConsensus[column],
268                   av_ignoreGapsConsensus);
269         }
270       }
271     }
272     return null;
273   }
274
275   /**
276    * Render the annotation rows associated with an alignment.
277    * @param annotPanel
278    *          container frame
279    * @param av
280    *          data and view settings to render
281    * @param g
282    *          destination for graphics
283    * @param activeRow
284    *          row where a mouse event occured (or -1)
285    * @param startRes
286    *          first column that will be drawn
287    * @param endRes
288    *          last column that will be drawn
289    * @return true if the fadedImage was used for any alignment annotation rows currently being calculated
290    */
291   public boolean drawComponent(AwtRenderPanelI annotPanel, AlignViewportI av,
292           Graphics g, int activeRow, int startRes, int endRes)
293   {
294     boolean usedFaded=false;
295     // NOTES:
296     // AnnotationPanel needs to implement: ImageObserver, access to
297     // AlignViewport
298     updateFromAwtRenderPanel(annotPanel, av);
299     fm = g.getFontMetrics();
300     AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
301
302     int x = 0, y = 0;
303     int column = 0;
304     char lastSS;
305     int lastSSX;
306     int iconOffset = 0;
307     boolean validRes = false;
308     boolean validEnd = false;
309     boolean labelAllCols = false;
310     boolean centreColLabels, centreColLabelsDef = av
311             .getCentreColumnLabels();
312     boolean scaleColLabel = false;
313     boolean[] graphGroupDrawn = new boolean[aa.length];
314     int charOffset = 0; // offset for a label
315     float fmWidth, fmScaling = 1f; // scaling for a label to fit it into a
316     // column.
317     Font ofont = g.getFont();
318     // \u03B2 \u03B1
319     for (int i = 0; i < aa.length; i++)
320     {
321       AlignmentAnnotation row = aa[i];
322       Annotation[] row_annotations=row.annotations;
323       if (!row.visible)
324       {
325         continue;
326       }
327       centreColLabels = row.centreColLabels || centreColLabelsDef;
328       labelAllCols = row.showAllColLabels;
329       scaleColLabel = row.scaleColLabel;
330       lastSS = ' ';
331       lastSSX = 0;
332       if (row.graph > 0)
333       {
334         if (row.graphGroup > -1 && graphGroupDrawn[row.graphGroup])
335         {
336           continue;
337         }
338
339         // this is so that we draw the characters below the graph
340         y += row.height;
341
342         if (row.hasText)
343         {
344           iconOffset = charHeight - fm.getDescent();
345           y -= charHeight;
346         }
347       }
348       else if (row.hasText)
349       {
350         iconOffset = charHeight - fm.getDescent();
351
352       }
353       else
354       {
355         iconOffset = 0;
356       }
357
358       if (row.autoCalculated && av.isCalculationInProgress(row))
359       {
360         y += charHeight;
361         usedFaded=true;
362         g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0, y
363                 - row.height, imgWidth, y, annotationPanel);
364         g.setColor(Color.black);
365         // g.drawString("Calculating "+aa[i].label+"....",20, y-row.height/2);
366
367         continue;
368       }
369
370       /*
371        * else if (annotationPanel.av.updatingConservation &&
372        * aa[i].label.equals("Conservation")) {
373        *
374        * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
375        * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
376        * annotationPanel.imgWidth, y, annotationPanel);
377        *
378        * g.setColor(Color.black); //
379        * g.drawString("Calculating Conservation.....",20, y-row.height/2);
380        *
381        * continue; } else if (annotationPanel.av.updatingConservation &&
382        * aa[i].label.equals("Quality")) {
383        *
384        * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
385        * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
386        * annotationPanel.imgWidth, y, annotationPanel); g.setColor(Color.black);
387        * // / g.drawString("Calculating Quality....",20, y-row.height/2);
388        *
389        * continue; }
390        */
391       // first pass sets up state for drawing continuation from left-hand column
392       // of startRes
393       x = (startRes == 0) ? 0 : -1;
394       while (x < endRes - startRes)
395       {
396         if (hasHiddenColumns)
397         {
398           column = columnSelection.adjustForHiddenColumns(startRes + x);
399           if (column > row_annotations.length - 1)
400           {
401             break;
402           }
403         }
404         else
405         {
406           column = startRes + x;
407         }
408
409         if ((row_annotations == null) || (row_annotations.length <= column)
410                 || (row_annotations[column] == null))
411         {
412           validRes = false;
413         }
414         else
415         {
416           validRes = true;
417         }
418         if (x > -1)
419         {
420           if (activeRow == i)
421           {
422             g.setColor(Color.red);
423
424             if (columnSelection != null)
425             {
426               for (int n = 0; n < columnSelection.size(); n++)
427               {
428                 int v = columnSelection.columnAt(n);
429
430                 if (v == column)
431                 {
432                   g.fillRect(x * charWidth, y, charWidth, charHeight);
433                 }
434               }
435             }
436           }
437           if (!row.isValidStruc())
438           {
439             g.setColor(Color.orange);
440             g.fillRect((int) row.getInvalidStrucPos() * charWidth, y,
441                     charWidth, charHeight);
442           }
443           if (validCharWidth
444                   && validRes
445                   && row_annotations[column].displayCharacter != null
446                   && (row_annotations[column].displayCharacter.length() > 0))
447           {
448
449             if (centreColLabels || scaleColLabel)
450             {
451               fmWidth = fm.charsWidth(
452                       row_annotations[column].displayCharacter
453                               .toCharArray(), 0,
454                       row_annotations[column].displayCharacter.length());
455
456               if (scaleColLabel)
457               {
458                 // justify the label and scale to fit in column
459                 if (fmWidth > charWidth)
460                 {
461                   // scale only if the current font isn't already small enough
462                   fmScaling = charWidth;
463                   fmScaling /= fmWidth;
464                   g.setFont(ofont.deriveFont(AffineTransform
465                           .getScaleInstance(fmScaling, 1.0)));
466                   // and update the label's width to reflect the scaling.
467                   fmWidth = charWidth;
468                 }
469               }
470             }
471             else
472             {
473               fmWidth = fm
474                       .charWidth(row_annotations[column].displayCharacter
475                               .charAt(0));
476             }
477             charOffset = (int) ((charWidth - fmWidth) / 2f);
478
479             if (row_annotations[column].colour == null)
480               g.setColor(Color.black);
481             else
482               g.setColor(row_annotations[column].colour);
483
484             if (column == 0 || row.graph > 0)
485             {
486               g.drawString(row_annotations[column].displayCharacter,
487                       (x * charWidth) + charOffset, y + iconOffset);
488             }
489             else if (row_annotations[column - 1] == null
490                     || (labelAllCols
491                             || !row_annotations[column].displayCharacter
492                                     .equals(row_annotations[column - 1].displayCharacter) || (row_annotations[column].displayCharacter
493                             .length() < 2 && row_annotations[column].secondaryStructure == ' ')))
494             {
495               g.drawString(row_annotations[column].displayCharacter, x
496                       * charWidth + charOffset, y + iconOffset);
497             }
498             g.setFont(ofont);
499           }
500         }
501         if (row.hasIcons)
502         {
503           char ss = validRes ? row_annotations[column].secondaryStructure
504                   : ' ';
505           //System.out.println(ss);
506           if (ss == 'S')
507           {
508             // distinguish between forward/backward base-pairing
509             if (row_annotations[column].displayCharacter.indexOf(')') > -1)
510             {
511               ss = 's';
512               System.out.println(ss);
513             }
514           }
515            if (ss == 'C')
516           {
517             if ((row_annotations[column].displayCharacter.indexOf(']') > -1) || (row_annotations[column].displayCharacter.indexOf('}') > -1))
518             {
519                 ss = 'c';
520                 System.out.println(ss);
521             }
522           }
523           if (!validRes || (ss != lastSS))
524           {
525             if (x > -1)
526             {
527               switch (lastSS)
528               {
529               case 'H':
530                 drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
531                         column, validRes, validEnd);
532                 break;
533
534               case 'E':
535                 drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
536                         column, validRes, validEnd);
537                 break;
538
539               case 'S': // Stem case for RNA secondary structure
540               case 's': // and opposite direction
541                 drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
542                         column, validRes, validEnd);
543                 break;
544               case 'C':
545               case 'c':
546                   drawNotCanonicalAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
547                           column, validRes, validEnd);
548                   break;
549               default:
550                 g.setColor(Color.gray);
551                 g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth)
552                         - lastSSX, 2);
553
554                 break;
555               }
556             }
557             if (validRes)
558             {
559               lastSS = ss;
560             }
561             else
562             {
563               lastSS = ' ';
564             }
565             if (x > -1)
566             {
567               lastSSX = (x * charWidth);
568             }
569           }
570         }
571         column++;
572         x++;
573       }
574       if (column >= row_annotations.length)
575       {
576         column = row_annotations.length - 1;
577         validEnd = false;
578       }
579       else
580       {
581         validEnd = true;
582       }
583       if ((row_annotations == null) || (row_annotations.length <= column)
584               || (row_annotations[column] == null))
585       {
586         validRes = false;
587       }
588       else
589       {
590         validRes = true;
591       }
592
593       // x ++;
594
595       if (row.hasIcons)
596       {
597         switch (lastSS)
598         {
599         case 'H':
600           drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
601                   column, validRes, validEnd);
602           break;
603
604         case 'E':
605           drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
606                   column, validRes, validEnd);
607           break;
608         case 's':
609         case 'S': // Stem case for RNA secondary structure
610           drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
611                   column, validRes, validEnd);
612           break;
613         case 'c':
614         case 'C': 
615           drawNotCanonicalAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
616                     column, validRes, validEnd);
617           break;
618         default:
619           drawGlyphLine(g, row_annotations, lastSSX, x, y, iconOffset, startRes,
620                   column, validRes, validEnd);
621           break;
622         }
623       }
624
625       if (row.graph > 0 && row.graphHeight > 0)
626       {
627         if (row.graph == AlignmentAnnotation.LINE_GRAPH)
628         {
629           if (row.graphGroup > -1 && !graphGroupDrawn[row.graphGroup])
630           {
631             float groupmax = -999999, groupmin = 9999999;
632             for (int gg = 0; gg < aa.length; gg++)
633             {
634               if (aa[gg].graphGroup != row.graphGroup)
635               {
636                 continue;
637               }
638
639               if (aa[gg] != row)
640               {
641                 aa[gg].visible = false;
642               }
643
644               if (aa[gg].graphMax > groupmax)
645               {
646                 groupmax = aa[gg].graphMax;
647               }
648               if (aa[gg].graphMin < groupmin)
649               {
650                 groupmin = aa[gg].graphMin;
651               }
652             }
653
654             for (int gg = 0; gg < aa.length; gg++)
655             {
656               if (aa[gg].graphGroup == row.graphGroup)
657               {
658                 drawLineGraph(g, aa[gg], aa[gg].annotations, startRes, endRes, y, groupmin,
659                         groupmax, row.graphHeight);
660               }
661             }
662
663             graphGroupDrawn[row.graphGroup] = true;
664           }
665           else
666           {
667             drawLineGraph(g, row, row_annotations, startRes, endRes, y, row.graphMin,
668                     row.graphMax, row.graphHeight);
669           }
670         }
671         else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
672         {
673           drawBarGraph(g, row, row_annotations, startRes, endRes, row.graphMin,
674                   row.graphMax, y);
675         }
676       }
677
678       if (row.graph > 0 && row.hasText)
679       {
680         y += charHeight;
681       }
682
683       if (row.graph == 0)
684       {
685         y += aa[i].height;
686       }
687     }
688     return !usedFaded;
689   }
690
691   private final Color GLYPHLINE_COLOR = Color.gray;
692
693   private final Color SHEET_COLOUR = Color.green;
694
695   private final Color HELIX_COLOUR = Color.red;
696
697   private final Color STEM_COLOUR = Color.blue;
698   
699   private final Color NOTCANONICAL_COLOUR = Color.red;
700
701   public void drawGlyphLine(Graphics g, Annotation[] row,
702           int lastSSX, int x, int y, int iconOffset, int startRes,
703           int column, boolean validRes, boolean validEnd)
704   {
705     g.setColor(GLYPHLINE_COLOR);
706     g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
707   }
708
709   public void drawSheetAnnot(Graphics g, Annotation[] row,
710
711           int lastSSX, int x, int y, int iconOffset, int startRes,
712           int column, boolean validRes, boolean validEnd)
713   {
714     g.setColor(SHEET_COLOUR);
715
716     if (!validEnd || !validRes || row==null || row[column] == null
717             || row[column].secondaryStructure != 'E')
718     {
719       g.fillRect(lastSSX, y + 4 + iconOffset,
720               (x * charWidth) - lastSSX - 4, 7);
721       g.fillPolygon(new int[]
722       { (x * charWidth) - 4, (x * charWidth) - 4, (x * charWidth) },
723               new int[]
724               { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset },
725               3);
726     }
727     else
728     {
729       g.fillRect(lastSSX, y + 4 + iconOffset,
730               (x + 1) * charWidth - lastSSX, 7);
731     }
732
733   }
734
735   public void drawHelixAnnot(Graphics g, Annotation[] row,
736           int lastSSX, int x, int y, int iconOffset, int startRes,
737           int column, boolean validRes, boolean validEnd)
738   {
739     g.setColor(HELIX_COLOUR);
740
741     int sCol = (lastSSX / charWidth) + startRes;
742     int x1 = lastSSX;
743     int x2 = (x * charWidth);
744
745     if (MAC)
746     {
747       int ofs = charWidth / 2;
748       // Off by 1 offset when drawing rects and ovals
749       // to offscreen image on the MAC
750       g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1, 8, 8, 8);
751       if (sCol == 0 || row[sCol - 1] == null
752               || row[sCol - 1].secondaryStructure != 'H')
753       {
754       }
755       else
756       {
757         // g.setColor(Color.orange);
758         g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1 - ofs + 1, 8,
759                 0, 0);
760       }
761       if (!validRes || row[column] == null
762               || row[column].secondaryStructure != 'H')
763       {
764
765       }
766       else
767       {
768         // g.setColor(Color.magenta);
769         g.fillRoundRect(lastSSX + ofs, y + 4 + iconOffset, x2 - x1 - ofs
770                 + 1, 8, 0, 0);
771
772       }
773
774       return;
775     }
776
777     if (sCol == 0 || row[sCol - 1] == null
778             || row[sCol - 1].secondaryStructure != 'H')
779     {
780       g.fillArc(lastSSX, y + 4 + iconOffset, charWidth, 8, 90, 180);
781       x1 += charWidth / 2;
782     }
783
784     if (!validRes || row[column] == null
785             || row[column].secondaryStructure != 'H')
786     {
787       g.fillArc((x * charWidth) - charWidth, y + 4 + iconOffset, charWidth,
788               8, 270, 180);
789       x2 -= charWidth / 2;
790     }
791
792     g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8);
793   }
794
795   public void drawLineGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int sRes,
796           int eRes, int y, float min, float max, int graphHeight)
797   {
798     if (sRes > aa_annotations.length)
799     {
800       return;
801     }
802
803     int x = 0;
804
805     // Adjustment for fastpaint to left
806     if (eRes < endRes)
807     {
808       eRes++;
809     }
810
811     eRes = Math.min(eRes, aa_annotations.length);
812
813     if (sRes == 0)
814     {
815       x++;
816     }
817
818     int y1 = y, y2 = y;
819     float range = max - min;
820
821     // //Draw origin
822     if (min < 0)
823     {
824       y2 = y - (int) ((0 - min / range) * graphHeight);
825     }
826
827     g.setColor(Color.gray);
828     g.drawLine(x - charWidth, y2, (eRes - sRes + 1) * charWidth, y2);
829
830     eRes = Math.min(eRes, aa_annotations.length);
831
832     int column;
833     int aaMax = aa_annotations.length - 1;
834
835     while (x < eRes - sRes)
836     {
837       column = sRes + x;
838       if (hasHiddenColumns)
839       {
840         column = columnSelection.adjustForHiddenColumns(column);
841       }
842
843       if (column > aaMax)
844       {
845         break;
846       }
847
848       if (aa_annotations[column] == null
849               || aa_annotations[column - 1] == null)
850       {
851         x++;
852         continue;
853       }
854
855       if (aa_annotations[column].colour == null)
856         g.setColor(Color.black);
857       else
858         g.setColor(aa_annotations[column].colour);
859
860       y1 = y
861               - (int) (((aa_annotations[column - 1].value - min) / range) * graphHeight);
862       y2 = y
863               - (int) (((aa_annotations[column].value - min) / range) * graphHeight);
864
865       g.drawLine(x * charWidth - charWidth / 2, y1, x * charWidth
866               + charWidth / 2, y2);
867       x++;
868     }
869
870     if (_aa.threshold != null)
871     {
872       g.setColor(_aa.threshold.colour);
873       Graphics2D g2 = (Graphics2D) g;
874       g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
875               BasicStroke.JOIN_ROUND, 3f, new float[]
876               { 5f, 3f }, 0f));
877
878       y2 = (int) (y - ((_aa.threshold.value - min) / range) * graphHeight);
879       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
880       g2.setStroke(new BasicStroke());
881     }
882   }
883
884   public void drawBarGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int sRes,
885           int eRes, float min, float max, int y)
886   {
887     if (sRes > aa_annotations.length)
888     {
889       return;
890     }
891     Font ofont = g.getFont();
892     eRes = Math.min(eRes, aa_annotations.length);
893
894     int x = 0, y1 = y, y2 = y;
895
896     float range = max - min;
897
898     if (min < 0)
899     {
900       y2 = y - (int) ((0 - min / (range)) * _aa.graphHeight);
901     }
902
903     g.setColor(Color.gray);
904
905     g.drawLine(x, y2, (eRes - sRes) * charWidth, y2);
906
907     int column;
908     int aaMax = aa_annotations.length - 1;
909     boolean renderHistogram = true, renderProfile = true, normaliseProfile = false;
910     // if (aa.autoCalculated && aa.label.startsWith("Consensus"))
911     {
912       // TODO: generalise this to have render styles for consensus/profile data
913       if (_aa.groupRef != null)
914       {
915         renderHistogram = _aa.groupRef.isShowConsensusHistogram();
916         renderProfile = _aa.groupRef.isShowSequenceLogo();
917         normaliseProfile = _aa.groupRef.isNormaliseSequenceLogo();
918       }
919       else
920       {
921         renderHistogram = av_renderHistogram;
922         renderProfile = av_renderProfile;
923         normaliseProfile = av_normaliseProfile;
924       }
925     }
926     while (x < eRes - sRes)
927     {
928       column = sRes + x;
929       if (hasHiddenColumns)
930       {
931         column = columnSelection.adjustForHiddenColumns(column);
932       }
933
934       if (column > aaMax)
935       {
936         break;
937       }
938
939       if (aa_annotations[column] == null)
940       {
941         x++;
942         continue;
943       }
944       if (aa_annotations[column].colour == null)
945         g.setColor(Color.black);
946       else
947         g.setColor(aa_annotations[column].colour);
948
949       y1 = y
950               - (int) (((aa_annotations[column].value - min) / (range)) * _aa.graphHeight);
951
952       if (renderHistogram)
953       {
954         if (y1 - y2 > 0)
955         {
956           g.fillRect(x * charWidth, y2, charWidth, y1 - y2);
957         }
958         else
959         {
960           g.fillRect(x * charWidth, y1, charWidth, y2 - y1);
961         }
962       }
963       // draw profile if available
964       if (renderProfile)
965       {
966
967         int profl[] = getProfileFor(_aa, column);
968         // just try to draw the logo if profl is not null
969         if (profl != null && profl[1] != 0)
970         {
971           float ht = normaliseProfile ? y - _aa.graphHeight : y1;
972           double htn = normaliseProfile ? _aa.graphHeight : (y2 - y1);// aa.graphHeight;
973           double hght;
974           float wdth;
975           double ht2 = 0;
976           char[] dc;
977
978           /**
979            * profl.length == 74 indicates that the profile of a secondary
980            * structure conservation row was accesed. Therefore dc gets length 2,
981            * to have space for a basepair instead of just a single nucleotide
982            */
983           if (profl.length == 74)
984           {
985             dc = new char[2];
986           }
987           else
988           {
989             dc = new char[1];
990           }
991           LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
992           double scale = 1f / (normaliseProfile ? profl[1] : 100f);
993           float ofontHeight = 1f / lm.getAscent();// magnify to fill box
994           double scl = 0.0;
995           for (int c = 2; c < profl[0];)
996           {
997             dc[0] = (char) profl[c++];
998
999             if (_aa.label.startsWith("StrucConsensus"))
1000             {
1001               dc[1] = (char) profl[c++];
1002             }
1003
1004             wdth = charWidth;
1005             wdth /= fm.charsWidth(dc, 0, dc.length);
1006
1007             ht += scl;
1008             {
1009               scl = htn * scale * profl[c++];
1010               lm = ofont.getLineMetrics(dc, 0, 1, g.getFontMetrics()
1011                       .getFontRenderContext());
1012               g.setFont(ofont.deriveFont(AffineTransform.getScaleInstance(
1013                       wdth, scl / lm.getAscent())));
1014               lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g);
1015
1016               // Debug - render boxes around characters
1017               // g.setColor(Color.red);
1018               // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
1019               // (int)(scl));
1020               // g.setColor(profcolour.findColour(dc[0]).darker());
1021               g.setColor(profcolour.findColour(dc[0], column,null));
1022
1023               hght = (ht + (scl - lm.getDescent() - lm.getBaselineOffsets()[lm
1024                       .getBaselineIndex()]));
1025
1026               g.drawChars(dc, 0, dc.length, x * charWidth, (int) hght);
1027             }
1028           }
1029           g.setFont(ofont);
1030         }
1031       }
1032       x++;
1033     }
1034     if (_aa.threshold != null)
1035     {
1036       g.setColor(_aa.threshold.colour);
1037       Graphics2D g2 = (Graphics2D) g;
1038       g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE,
1039               BasicStroke.JOIN_ROUND, 3f, new float[]
1040               { 5f, 3f }, 0f));
1041
1042       y2 = (int) (y - ((_aa.threshold.value - min) / range) * _aa.graphHeight);
1043       g.drawLine(0, y2, (eRes - sRes) * charWidth, y2);
1044       g2.setStroke(new BasicStroke());
1045     }
1046   }
1047
1048   // used by overview window
1049   public void drawGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int width,
1050           int y, int sRes, int eRes)
1051   {
1052     eRes = Math.min(eRes, aa_annotations.length);
1053     g.setColor(Color.white);
1054     g.fillRect(0, 0, width, y);
1055     g.setColor(new Color(0, 0, 180));
1056
1057     int x = 0, height;
1058
1059     for (int j = sRes; j < eRes; j++)
1060     {
1061       if (aa_annotations[j] != null)
1062       {
1063         if (aa_annotations[j].colour == null)
1064           g.setColor(Color.black);
1065         else
1066           g.setColor(aa_annotations[j].colour);
1067
1068         height = (int) ((aa_annotations[j].value / _aa.graphMax) * y);
1069         if (height > y)
1070         {
1071           height = y;
1072         }
1073
1074         g.fillRect(x, y - height, charWidth, height);
1075       }
1076       x += charWidth;
1077     }
1078   }
1079 }