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