Clover icon

Coverage Report

  1. Project Clover database Mon Nov 11 2024 15:05:32 GMT
  2. Package jalview.renderer

File AnnotationRenderer.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
37% of files have more coverage

Code metrics

310
768
26
1
1,948
1,446
416
0.54
29.54
26
16

Classes

Class Line # Actions
AnnotationRenderer 62 768 416
0.517210151.7%
 

Contributing tests

This file is covered by 185 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.renderer;
22   
23    import java.awt.BasicStroke;
24    import java.awt.Color;
25    import java.awt.Font;
26    import java.awt.FontMetrics;
27    import java.awt.Graphics;
28    import java.awt.Graphics2D;
29    import java.awt.Image;
30    import java.awt.RenderingHints;
31    import java.awt.Stroke;
32    import java.awt.geom.AffineTransform;
33    import java.awt.image.ImageObserver;
34    import java.util.BitSet;
35    import java.util.Hashtable;
36   
37    import org.jfree.graphics2d.svg.SVGGraphics2D;
38    import org.jibble.epsgraphics.EpsGraphics2D;
39   
40    import jalview.analysis.AAFrequency;
41    import jalview.analysis.AlignmentUtils;
42    import jalview.analysis.CodingUtils;
43    import jalview.analysis.Rna;
44    import jalview.analysis.StructureFrequency;
45    import jalview.api.AlignViewportI;
46    import jalview.bin.Cache;
47    import jalview.bin.Console;
48    import jalview.datamodel.AlignmentAnnotation;
49    import jalview.datamodel.Annotation;
50    import jalview.datamodel.ColumnSelection;
51    import jalview.datamodel.HiddenColumns;
52    import jalview.datamodel.ProfilesI;
53    import jalview.renderer.api.AnnotationRendererFactoryI;
54    import jalview.renderer.api.AnnotationRowRendererI;
55    import jalview.schemes.ColourSchemeI;
56    import jalview.schemes.NucleotideColourScheme;
57    import jalview.schemes.ResidueProperties;
58    import jalview.schemes.ZappoColourScheme;
59    import jalview.util.MessageManager;
60    import jalview.util.Platform;
61   
 
62    public class AnnotationRenderer
63    {
64    private static final int UPPER_TO_LOWER = 'a' - 'A'; // 32
65   
66    private static final int CHAR_A = 'A'; // 65
67   
68    private static final int CHAR_Z = 'Z'; // 90
69   
70    /**
71    * flag indicating if timing and redraw parameter info should be output
72    */
73    private final boolean debugRedraw;
74   
75    private int charWidth, endRes, charHeight;
76   
77    private boolean validCharWidth, hasHiddenColumns;
78   
79    private FontMetrics fm;
80   
81    private final boolean USE_FILL_ROUND_RECT = Platform.isAMacAndNotJS();
82   
83    boolean av_renderHistogram = true, av_renderProfile = true,
84    av_normaliseProfile = false;
85   
86    ResidueShaderI profcolour = null;
87   
88    private ColumnSelection columnSelection;
89   
90    private HiddenColumns hiddenColumns;
91   
92    private ProfilesI hconsensus;
93   
94    private ProfilesI hSSconsensus;
95   
96    private Hashtable<String, Object>[] complementConsensus;
97   
98    private Hashtable<String, Object>[] hStrucConsensus;
99   
100    private boolean av_ignoreGapsConsensus;
101   
102    private boolean renderingVectors = false;
103   
104    private boolean glyphLineDrawn = false;
105   
106    /**
107    * attributes set from AwtRenderPanelI
108    */
109    /**
110    * old image used when data is currently being calculated and cannot be
111    * rendered
112    */
113    private Image fadedImage;
114   
115    /**
116    * panel being rendered into
117    */
118    private ImageObserver annotationPanel;
119   
120    /**
121    * width of image to render in panel
122    */
123    private int imgWidth;
124   
125    /**
126    * offset to beginning of visible area
127    */
128    private int sOffset;
129   
130    /**
131    * offset to end of visible area
132    */
133    private int visHeight;
134   
135    /**
136    * indicate if the renderer should only render the visible portion of the
137    * annotation given the current view settings
138    */
139    private boolean useClip = true;
140   
141    /**
142    * master flag indicating if renderer should ever try to clip. not enabled for
143    * jalview 2.8.1
144    */
145    private boolean canClip = false;
146   
 
147  491 toggle public AnnotationRenderer()
148    {
149  491 this(false);
150    }
151   
152    /**
153    * Create a new annotation Renderer
154    *
155    * @param debugRedraw
156    * flag indicating if timing and redraw parameter info should be
157    * output
158    */
 
159  491 toggle public AnnotationRenderer(boolean debugRedraw)
160    {
161  491 this.debugRedraw = debugRedraw;
162    }
163   
164    /**
165    * Remove any references and resources when this object is no longer required
166    */
 
167  253 toggle public void dispose()
168    {
169  253 hiddenColumns = null;
170  253 hconsensus = null;
171  253 hSSconsensus = null;
172  253 complementConsensus = null;
173  253 hStrucConsensus = null;
174  253 fadedImage = null;
175  253 annotationPanel = null;
176  253 rendererFactoryI = null;
177    }
178   
 
179  0 toggle void drawStemAnnot(Graphics g, Annotation[] row_annotations, int lastSSX,
180    int x, int y, int iconOffset, int startRes, int column,
181    boolean validRes, boolean validEnd)
182    {
183  0 int sCol = (lastSSX / charWidth)
184    + hiddenColumns.visibleToAbsoluteColumn(startRes);
185  0 int x1 = lastSSX;
186  0 int x2 = (x * charWidth);
187   
188  0 char dc = (column == 0 || row_annotations[column - 1] == null) ? ' '
189    : row_annotations[column - 1].secondaryStructure;
190   
191  0 boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
192    || dc != row_annotations[sCol - 1].secondaryStructure
193    || !validEnd;
194  0 boolean diffdownstream = !validRes || !validEnd
195    || row_annotations[column] == null
196    || dc != row_annotations[column].secondaryStructure;
197   
198  0 if (diffupstream || diffdownstream)
199    {
200    // draw glyphline under arrow
201  0 drawGlyphLine(g, lastSSX, x, y, iconOffset);
202    }
203  0 g.setColor(STEM_COLOUR);
204   
205  0 if (column > 0 && Rna.isClosingParenthesis(dc))
206    {
207  0 if (diffupstream)
208    // if (validRes && column>1 && row_annotations[column-2]!=null &&
209    // dc.equals(row_annotations[column-2].displayCharacter))
210    {
211    /*
212    * if new annotation with a closing base pair half of the stem,
213    * display a backward arrow
214    */
215  0 fillPolygon(g, new int[] { lastSSX + 5, lastSSX + 5, lastSSX },
216    new int[]
217    { y + iconOffset + 1, y + 13 + iconOffset,
218    y + 7 + iconOffset },
219    3);
220  0 x1 += 5;
221    }
222  0 if (diffdownstream)
223    {
224  0 x2 -= 1;
225    }
226    }
227    else
228    {
229    // display a forward arrow
230  0 if (diffdownstream)
231    {
232    /*
233    * if annotation ending with an opeing base pair half of the stem,
234    * display a forward arrow
235    */
236  0 fillPolygon(g, new int[] { x2 - 6, x2 - 6, x2 - 1 },
237    new int[]
238    { y + iconOffset + 1, y + 13 + iconOffset,
239    y + 7 + iconOffset },
240    3);
241  0 x2 -= 5;
242    }
243  0 if (diffupstream)
244    {
245  0 x1 += 1;
246    }
247    }
248    // draw arrow body
249  0 unsetAntialias(g);
250  0 fillRect(g, x1, y + 4 + iconOffset, x2 - x1, 6);
251    }
252   
 
253  0 toggle void drawNotCanonicalAnnot(Graphics g, Color nonCanColor,
254    Annotation[] row_annotations, int lastSSX, int x, int y,
255    int iconOffset, int startRes, int column, boolean validRes,
256    boolean validEnd)
257    {
258    // Console.info(nonCanColor);
259   
260  0 int sCol = (lastSSX / charWidth)
261    + hiddenColumns.visibleToAbsoluteColumn(startRes);
262  0 int x1 = lastSSX;
263  0 int x2 = (x * charWidth);
264   
265  0 String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
266    : row_annotations[column - 1].displayCharacter;
267   
268  0 boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
269    || !dc.equals(row_annotations[sCol - 1].displayCharacter)
270    || !validEnd;
271  0 boolean diffdownstream = !validRes || !validEnd
272    || row_annotations[column] == null
273    || !dc.equals(row_annotations[column].displayCharacter);
274    // Console.info("Column "+column+" diff up:
275    // "+diffupstream+"
276    // down:"+diffdownstream);
277    // If a closing base pair half of the stem, display a backward arrow
278  0 if (diffupstream || diffdownstream)
279    {
280    // draw glyphline under arrow
281  0 drawGlyphLine(g, lastSSX, x, y, iconOffset);
282    }
283  0 g.setColor(nonCanColor);
284  0 if (column > 0 && Rna.isClosingParenthesis(dc))
285    {
286   
287  0 if (diffupstream)
288    // if (validRes && column>1 && row_annotations[column-2]!=null &&
289    // dc.equals(row_annotations[column-2].displayCharacter))
290    {
291  0 fillPolygon(g, new int[] { lastSSX + 5, lastSSX + 5, lastSSX },
292    new int[]
293    { y + iconOffset + 1, y + 13 + iconOffset,
294    y + 7 + iconOffset },
295    3);
296  0 x1 += 5;
297    }
298  0 if (diffdownstream)
299    {
300  0 x2 -= 1;
301    }
302    }
303    else
304    {
305   
306    // display a forward arrow
307  0 if (diffdownstream)
308    {
309  0 fillPolygon(g, new int[] { x2 - 6, x2 - 6, x2 - 1 },
310    new int[]
311    { y + iconOffset + 1, y + 13 + iconOffset,
312    y + 7 + iconOffset },
313    3);
314  0 x2 -= 5;
315    }
316  0 if (diffupstream)
317    {
318  0 x1 += 1;
319    }
320    }
321    // draw arrow body
322  0 unsetAntialias(g);
323  0 fillRect(g, x1, y + 4 + iconOffset, x2 - x1, 6);
324    }
325   
326    // public void updateFromAnnotationPanel(FontMetrics annotFM, AlignViewportI
327    // av)
 
328  2756 toggle public void updateFromAwtRenderPanel(AwtRenderPanelI annotPanel,
329    AlignViewportI av)
330    {
331  2756 fm = annotPanel.getFontMetrics();
332  2756 annotationPanel = annotPanel;
333  2756 fadedImage = annotPanel.getFadedImage();
334  2756 imgWidth = annotPanel.getFadedImageWidth();
335    // visible area for rendering
336  2756 int[] bounds = annotPanel.getVisibleVRange();
337  2756 if (bounds != null)
338    {
339  2544 sOffset = bounds[0];
340  2544 visHeight = bounds[1];
341  2544 if (visHeight == 0)
342    {
343  0 useClip = false;
344    }
345    else
346    {
347  2544 useClip = canClip;
348    }
349    }
350    else
351    {
352  212 useClip = false;
353    }
354   
355  2756 rendererFactoryI = AnnotationRendererFactory.getRendererFactory();
356  2756 updateFromAlignViewport(av);
357    }
358   
 
359  2756 toggle public void updateFromAlignViewport(AlignViewportI av)
360    {
361  2756 charWidth = av.getCharWidth();
362  2756 endRes = av.getRanges().getEndRes();
363  2756 charHeight = av.getCharHeight();
364  2756 hasHiddenColumns = av.hasHiddenColumns();
365  2756 validCharWidth = av.isValidCharWidth();
366  2756 av_renderHistogram = av.isShowConsensusHistogram();
367  2756 av_renderProfile = av.isShowSequenceLogo();
368  2756 av_normaliseProfile = av.isNormaliseSequenceLogo();
369  2756 profcolour = av.getResidueShading();
370  2756 if (profcolour == null || profcolour.getColourScheme() == null)
371    {
372    /*
373    * Use default colour for sequence logo if
374    * the alignment has no colourscheme set
375    * (would like to use user preference but n/a for applet)
376    */
377  2082 ColourSchemeI col = av.getAlignment().isNucleotide()
378    ? new NucleotideColourScheme()
379    : new ZappoColourScheme();
380  2082 profcolour = new ResidueShader(col);
381    }
382  2756 columnSelection = av.getColumnSelection();
383  2756 hiddenColumns = av.getAlignment().getHiddenColumns();
384  2756 hconsensus = av.getSequenceConsensusHash();
385  2756 hSSconsensus = av.getSequenceSSConsensusHash();
386  2756 complementConsensus = av.getComplementConsensusHash();
387  2756 hStrucConsensus = av.getRnaStructureConsensusHash();
388  2756 av_ignoreGapsConsensus = av.isIgnoreGapsConsensus();
389    }
390   
391    /**
392    * Returns profile data; the first element is the profile type, the second is
393    * the number of distinct values, the third the total count, and the remainder
394    * depend on the profile type.
395    *
396    * @param aa
397    * @param column
398    * @return
399    */
 
400  102061 toggle int[] getProfileFor(AlignmentAnnotation aa, int column)
401    {
402    // TODO : consider refactoring the global alignment calculation
403    // properties/rendering attributes as a global 'alignment group' which holds
404    // all vis settings for the alignment as a whole rather than a subset
405    //
406  102061 if (aa.autoCalculated && (aa.label.startsWith("Consensus")
407    || aa.label.startsWith("cDNA Consensus")))
408    {
409  102061 boolean forComplement = aa.label.startsWith("cDNA Consensus");
410  102061 if (aa.groupRef != null && aa.groupRef.consensusData != null
411    && aa.groupRef.isShowSequenceLogo())
412    {
413    // TODO? group consensus for cDNA complement
414  30303 return AAFrequency.extractProfile(
415    aa.groupRef.consensusData.get(column),
416    aa.groupRef.getIgnoreGapsConsensus());
417    }
418    // TODO extend annotation row to enable dynamic and static profile data to
419    // be stored
420  71758 if (aa.groupRef == null && aa.sequenceRef == null)
421    {
422  71758 if (forComplement)
423    {
424  0 return AAFrequency.extractCdnaProfile(complementConsensus[column],
425    av_ignoreGapsConsensus);
426    }
427    else
428    {
429  71758 return AAFrequency.extractProfile(hconsensus.get(column),
430    av_ignoreGapsConsensus);
431    }
432    }
433    }
434   
435  0 else if (aa.autoCalculated && aa.label.startsWith(
436    MessageManager.getString("label.ssconsensus_label")))
437    {
438  0 return AAFrequency.extractProfile(hSSconsensus.get(column),
439    av_ignoreGapsConsensus);
440   
441    }
442   
443    else
444    {
445  0 if (aa.autoCalculated && aa.label.startsWith("StrucConsensus"))
446    {
447    // TODO implement group structure consensus
448    /*
449    * if (aa.groupRef != null && aa.groupRef.consensusData != null &&
450    * aa.groupRef.isShowSequenceLogo()) { //TODO check what happens for
451    * group selections return StructureFrequency.extractProfile(
452    * aa.groupRef.consensusData[column], aa.groupRef
453    * .getIgnoreGapsConsensus()); }
454    */
455    // TODO extend annotation row to enable dynamic and static profile data
456    // to
457    // be stored
458  0 if (aa.groupRef == null && aa.sequenceRef == null
459    && hStrucConsensus != null
460    && hStrucConsensus.length > column)
461    {
462  0 return StructureFrequency.extractProfile(hStrucConsensus[column],
463    av_ignoreGapsConsensus);
464    }
465    }
466    }
467  0 return null;
468    }
469   
470    boolean rna = false;
471   
472    private AnnotationRendererFactoryI rendererFactoryI;
473   
474    /**
475    * Render the annotation rows associated with an alignment.
476    *
477    * @param annotPanel
478    * container frame
479    * @param av
480    * data and view settings to render
481    * @param g
482    * destination for graphics
483    * @param activeRow
484    * row where a mouse event occured (or -1)
485    * @param startRes
486    * first column that will be drawn
487    * @param endRes
488    * last column that will be drawn
489    * @return true if the fadedImage was used for any alignment annotation rows
490    * currently being calculated
491    */
 
492  2756 toggle public boolean drawComponent(AwtRenderPanelI annotPanel,
493    AlignViewportI av, Graphics g, int activeRow, int startRes,
494    int endRes)
495    {
496  2756 if (g instanceof EpsGraphics2D || g instanceof SVGGraphics2D)
497    {
498  14 this.setVectorRendering(true);
499    }
500  2756 Graphics2D g2d = (Graphics2D) g;
501   
502  2756 long stime = System.currentTimeMillis();
503  2756 boolean usedFaded = false;
504    // NOTES:
505    // AnnotationPanel needs to implement: ImageObserver, access to
506    // AlignViewport
507  2756 updateFromAwtRenderPanel(annotPanel, av);
508  2756 fm = g.getFontMetrics();
509  2756 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
510    // int temp = 0;
511  2756 if (aa == null)
512    {
513  0 return false;
514    }
515  2756 int x = 0, y = 0;
516  2756 int column = 0;
517  2756 char lastSS;
518  2756 int lastSSX;
519  2756 int iconOffset = 0;
520  2756 boolean validRes = false;
521  2756 boolean validEnd = false;
522  2756 boolean labelAllCols = false;
523    // boolean centreColLabels;
524    // boolean centreColLabelsDef = av.isCentreColumnLabels();
525  2756 boolean scaleColLabel = false;
526  2756 final AlignmentAnnotation consensusAnnot = av
527    .getAlignmentConsensusAnnotation();
528  2756 final AlignmentAnnotation structConsensusAnnot = av
529    .getAlignmentStrucConsensusAnnotation();
530  2756 final AlignmentAnnotation complementConsensusAnnot = av
531    .getComplementConsensusAnnotation();
532  2756 final AlignmentAnnotation ssConsensusAnnot = av
533    .getAlignmentSecondaryStructureConsensusAnnotation();
534   
535  2756 BitSet graphGroupDrawn = new BitSet();
536  2756 int charOffset = 0; // offset for a label
537    // \u03B2 \u03B1
538    // debug ints
539  2756 int yfrom = 0, f_i = 0, yto = 0, f_to = 0;
540  2756 boolean clipst = false, clipend = false;
541  18055 for (int i = 0; i < aa.length; i++)
542    {
543  15301 AlignmentAnnotation row = aa[i];
544  15301 boolean renderHistogram = true;
545  15301 boolean renderProfile = false;
546  15301 boolean normaliseProfile = false;
547  15301 boolean isRNA = row.isRNA();
548   
549    // check if this is a consensus annotation row and set the display
550    // settings appropriately
551    // TODO: generalise this to have render styles for consensus/profile
552    // data
553  15301 if (row.groupRef != null && row == row.groupRef.getConsensus())
554    {
555  483 renderHistogram = row.groupRef.isShowConsensusHistogram();
556  483 renderProfile = row.groupRef.isShowSequenceLogo();
557  483 normaliseProfile = row.groupRef.isNormaliseSequenceLogo();
558    }
559  14818 else if (row == consensusAnnot || row == structConsensusAnnot
560    || row == complementConsensusAnnot || row == ssConsensusAnnot)
561    {
562  2755 renderHistogram = av_renderHistogram;
563  2755 renderProfile = av_renderProfile;
564  2755 normaliseProfile = av_normaliseProfile;
565    }
566   
567  15301 Annotation[] row_annotations = row.annotations;
568  15301 if (!row.isForDisplay())
569    {
570  1187 continue;
571    }
572    // centreColLabels = row.centreColLabels || centreColLabelsDef;
573  14114 labelAllCols = row.showAllColLabels;
574  14114 scaleColLabel = row.scaleColLabel;
575  14114 lastSS = ' ';
576  14114 lastSSX = 0;
577   
578  14114 if (!useClip || ((y - charHeight) < visHeight
579    && (y + row.height + charHeight * 2) >= sOffset))
580    {// if_in_visible_region
581  14114 if (!clipst)
582    {
583  2743 clipst = true;
584  2743 yfrom = y;
585  2743 f_i = i;
586    }
587  14114 yto = y;
588  14114 f_to = i;
589  14114 if (row.graph > 0)
590    {
591  11289 if (row.graphGroup > -1 && graphGroupDrawn.get(row.graphGroup))
592    {
593  0 continue;
594    }
595   
596    // this is so that we draw the characters below the graph
597  11289 y += row.height;
598   
599  11289 if (row.hasText)
600    {
601  10704 iconOffset = charHeight - fm.getDescent();
602  10704 y -= charHeight;
603    }
604    }
605  2825 else if (row.hasText)
606    {
607  2588 iconOffset = charHeight - fm.getDescent();
608   
609    }
610    else
611    {
612  237 iconOffset = 0;
613    }
614   
615  14114 if (row.autoCalculated && av.isCalculationInProgress(row))
616    {
617  297 y += charHeight;
618  297 usedFaded = true;
619  297 g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0,
620    y - row.height, imgWidth, y, annotationPanel);
621  297 g.setColor(Color.black);
622    // g.drawString("Calculating "+aa[i].label+"....",20, y-row.height/2);
623   
624  297 continue;
625    }
626   
627    /*
628    * else if (annotationPanel.av.updatingConservation &&
629    * aa[i].label.equals("Conservation")) {
630    *
631    * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
632    * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
633    * annotationPanel.imgWidth, y, annotationPanel);
634    *
635    * g.setColor(Color.black); //
636    * g.drawString("Calculating Conservation.....",20, y-row.height/2);
637    *
638    * continue; } else if (annotationPanel.av.updatingConservation &&
639    * aa[i].label.equals("Quality")) {
640    *
641    * y += charHeight; g.drawImage(annotationPanel.fadedImage, 0, y -
642    * row.height, annotationPanel.imgWidth, y, 0, y - row.height,
643    * annotationPanel.imgWidth, y, annotationPanel);
644    * g.setColor(Color.black); // /
645    * g.drawString("Calculating Quality....",20, y-row.height/2);
646    *
647    * continue; }
648    */
649   
650    // first pass sets up state for drawing continuation from left-hand
651    // column
652    // of startRes
653   
654    // flag used for vector rendition
655  13817 this.glyphLineDrawn = false;
656  13817 x = (startRes == 0) ? 0 : -1;
657  727414 while (x < endRes - startRes)
658    {
659  713605 if (hasHiddenColumns)
660    {
661  102578 column = hiddenColumns.visibleToAbsoluteColumn(startRes + x);
662  102577 if (column > row_annotations.length - 1)
663    {
664  4 break;
665    }
666    }
667    else
668    {
669  611026 column = startRes + x;
670    }
671   
672  713600 if ((row_annotations == null)
673    || (row_annotations.length <= column)
674    || (row_annotations[column] == null))
675    {
676  144192 validRes = false;
677    }
678    else
679    {
680  569408 validRes = true;
681    }
682  713600 final String displayChar = validRes
683    ? row_annotations[column].displayCharacter
684    : null;
685  713599 if (x > -1)
686    {
687  705513 unsetAntialias(g);
688  705513 if (activeRow == i)
689    {
690  0 g.setColor(Color.red);
691   
692  0 if (columnSelection != null)
693    {
694  0 if (columnSelection.contains(column))
695    {
696  0 fillRect(g, x * charWidth, y, charWidth, charHeight);
697    }
698    }
699    }
700  705512 if (row.getInvalidStrucPos() > x)
701    {
702  0 g.setColor(Color.orange);
703  0 fillRect(g, x * charWidth, y, charWidth, charHeight);
704    }
705  705512 else if (row.getInvalidStrucPos() == x)
706    {
707  0 g.setColor(Color.orange.darker());
708  0 fillRect(g, x * charWidth, y, charWidth, charHeight);
709    }
710  705512 if (validCharWidth && validRes && displayChar != null
711    && (displayChar.length() > 0))
712    {
713    // Graphics2D gg = (g);
714  404066 float fmWidth = fm.charsWidth(displayChar.toCharArray(), 0,
715    displayChar.length());
716   
717    /*
718    * shrink label width to fit in column, if that is
719    * both configured and necessary
720    */
721  404068 boolean scaledToFit = false;
722  404068 float fmScaling = 1f;
723  404068 if (scaleColLabel && fmWidth > charWidth)
724    {
725  0 scaledToFit = true;
726  0 fmScaling = charWidth;
727  0 fmScaling /= fmWidth;
728    // and update the label's width to reflect the scaling.
729  0 fmWidth = charWidth;
730    }
731   
732  404068 charOffset = (int) ((charWidth - fmWidth) / 2f);
733   
734  404066 if (row_annotations[column].colour == null)
735    {
736  156219 g2d.setColor(Color.black);
737    }
738    else
739    {
740  247847 g2d.setColor(row_annotations[column].colour);
741    }
742   
743    /*
744    * draw the label, unless it is the same secondary structure
745    * symbol (excluding RNA Helix) as the previous column
746    */
747  404067 final int xPos = (x * charWidth) + charOffset;
748  404068 final int yPos = y + iconOffset;
749   
750    /*
751    * translate to drawing position _before_ applying any scaling
752    */
753  404068 g2d.translate(xPos, yPos);
754  404067 if (scaledToFit)
755    {
756    /*
757    * use a scaling transform to make the label narrower
758    * (JalviewJS doesn't have Font.deriveFont(AffineTransform))
759    */
760  0 g2d.transform(
761    AffineTransform.getScaleInstance(fmScaling, 1.0));
762    }
763  404067 setAntialias(g);
764  404066 if (column == 0 || row.graph > 0)
765    {
766  390336 g2d.drawString(displayChar, 0, 0);
767    }
768  13730 else if (row_annotations[column - 1] == null || (labelAllCols
769    || !displayChar.equals(
770    row_annotations[column - 1].displayCharacter)
771    || (displayChar.length() < 2
772    && row_annotations[column].secondaryStructure == ' ')))
773    {
774  13730 g2d.drawString(displayChar, 0, 0);
775    }
776  404068 if (scaledToFit)
777    {
778    /*
779    * undo scaling before translating back
780    * (restoring saved transform does NOT work in JS PDFGraphics!)
781    */
782  0 g2d.transform(AffineTransform
783    .getScaleInstance(1D / fmScaling, 1.0));
784    }
785  404068 g2d.translate(-xPos, -yPos);
786    }
787    }
788  713601 if (row.hasIcons)
789    {
790  88327 char ss = validRes ? row_annotations[column].secondaryStructure
791    : '-';
792   
793  88327 if (ss == '(')
794    {
795    // distinguish between forward/backward base-pairing
796  0 if (displayChar.indexOf(')') > -1)
797    {
798   
799  0 ss = ')';
800   
801    }
802    }
803  88327 if (ss == '[')
804    {
805  0 if ((displayChar.indexOf(']') > -1))
806    {
807  0 ss = ']';
808   
809    }
810    }
811  88327 if (ss == '{')
812    {
813    // distinguish between forward/backward base-pairing
814  0 if (displayChar.indexOf('}') > -1)
815    {
816  0 ss = '}';
817   
818    }
819    }
820  88327 if (ss == '<')
821    {
822    // distinguish between forward/backward base-pairing
823  0 if (displayChar.indexOf('<') > -1)
824    {
825  0 ss = '>';
826   
827    }
828    }
829  88327 if (isRNA && (ss >= CHAR_A) && (ss <= CHAR_Z))
830    {
831    // distinguish between forward/backward base-pairing
832  0 int ssLowerCase = ss + UPPER_TO_LOWER;
833    // TODO would .equals() be safer here? or charAt(0)?
834  0 if (displayChar.indexOf(ssLowerCase) > -1)
835    {
836  0 ss = (char) ssLowerCase;
837    }
838    }
839   
840  88327 if (!validRes || (ss != lastSS))
841    {
842   
843  65195 if (x > -1)
844    {
845   
846    // int nb_annot = x - temp;
847    // Console.info("\t type :"+lastSS+"\t x
848    // :"+x+"\t nbre
849    // annot :"+nb_annot);
850  63898 switch (lastSS)
851    {
852  0 case '(': // Stem case for RNA secondary structure
853  0 case ')': // and opposite direction
854  0 drawStemAnnot(g, row_annotations, lastSSX, x, y,
855    iconOffset, startRes, column, validRes, validEnd);
856    // temp = x;
857  0 break;
858   
859  1322 case 'H':
860  1322 if (!isRNA)
861    {
862  1322 drawHelixAnnot(g, row_annotations, lastSSX, x, y,
863    iconOffset, startRes, column, validRes,
864    validEnd);
865  1322 break;
866    }
867    // no break if isRNA - falls through to drawNotCanonicalAnnot!
868  5520 case 'E':
869  5520 if (!isRNA)
870    {
871  5520 drawSheetAnnot(g, row_annotations, lastSSX, x, y,
872    iconOffset, startRes, column, validRes,
873    validEnd);
874  5520 break;
875    }
876    // no break if isRNA - fall through to drawNotCanonicalAnnot!
877   
878  0 case '{':
879  0 case '}':
880  0 case '[':
881  0 case ']':
882  0 case '>':
883  0 case '<':
884  0 case 'A':
885  0 case 'a':
886  0 case 'B':
887  0 case 'b':
888  0 case 'C':
889  0 case 'c':
890  0 case 'D':
891  0 case 'd':
892  0 case 'e':
893  0 case 'F':
894  0 case 'f':
895  0 case 'G':
896  0 case 'g':
897  0 case 'h':
898  0 case 'I':
899  0 case 'i':
900  0 case 'J':
901  0 case 'j':
902  0 case 'K':
903  0 case 'k':
904  0 case 'L':
905  0 case 'l':
906  0 case 'M':
907  0 case 'm':
908  0 case 'N':
909  0 case 'n':
910  0 case 'O':
911  0 case 'o':
912  0 case 'P':
913  0 case 'p':
914  0 case 'Q':
915  0 case 'q':
916  0 case 'R':
917  0 case 'r':
918  0 case 'S':
919  0 case 's':
920  0 case 'T':
921  0 case 't':
922  0 case 'U':
923  0 case 'u':
924  0 case 'V':
925  0 case 'v':
926  0 case 'W':
927  0 case 'w':
928  0 case 'X':
929  0 case 'x':
930  0 case 'Y':
931  0 case 'y':
932  0 case 'Z':
933  0 case 'z':
934   
935  0 Color nonCanColor = getNotCanonicalColor(lastSS);
936  0 drawNotCanonicalAnnot(g, nonCanColor, row_annotations,
937    lastSSX, x, y, iconOffset, startRes, column,
938    validRes, validEnd);
939    // temp = x;
940  0 break;
941  57055 default:
942  57056 if (isVectorRendering())
943    {
944    // draw single full width glyphline
945  0 drawGlyphLine(g, lastSSX, endRes - x, y, iconOffset);
946    // disable more glyph lines
947  0 this.glyphLineDrawn = true;
948    }
949    else
950    {
951  57056 drawGlyphLine(g, lastSSX, x, y, iconOffset);
952    }
953  57056 break;
954    }
955    }
956  65195 if (validRes)
957    {
958  7339 lastSS = ss;
959    }
960    else
961    {
962  57856 lastSS = ' ';
963    }
964  65194 if (x > -1)
965    {
966  63897 lastSSX = (x * charWidth);
967    }
968    }
969    }
970  713600 column++;
971  713600 x++;
972    }
973  13816 if (column >= row_annotations.length)
974    {
975  1608 column = row_annotations.length - 1;
976  1608 validEnd = false;
977    }
978    else
979    {
980  12208 validEnd = true;
981    }
982  13816 if ((row_annotations == null) || (row_annotations.length <= column)
983    || (row_annotations[column] == null))
984    {
985  3165 validRes = false;
986    }
987    else
988    {
989  10651 validRes = true;
990    }
991    // x ++;
992   
993  13816 if (row.hasIcons)
994    {
995  1531 switch (lastSS)
996    {
997   
998  0 case 'H':
999  0 if (!isRNA)
1000    {
1001  0 drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
1002    startRes, column, validRes, validEnd);
1003  0 break;
1004    }
1005    // no break if isRNA - fall through to drawNotCanonicalAnnot!
1006   
1007  497 case 'E':
1008  497 if (!isRNA)
1009    {
1010  497 drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
1011    startRes, column, validRes, validEnd);
1012  497 break;
1013    }
1014    // no break if isRNA - fall through to drawNotCanonicalAnnot!
1015   
1016  0 case '(':
1017  0 case ')': // Stem case for RNA secondary structure
1018   
1019  0 drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
1020    startRes, column, validRes, validEnd);
1021   
1022  0 break;
1023  0 case '{':
1024  0 case '}':
1025  0 case '[':
1026  0 case ']':
1027  0 case '>':
1028  0 case '<':
1029  0 case 'A':
1030  0 case 'a':
1031  0 case 'B':
1032  0 case 'b':
1033  0 case 'C':
1034  0 case 'c':
1035  0 case 'D':
1036  0 case 'd':
1037  0 case 'e':
1038  0 case 'F':
1039  0 case 'f':
1040  0 case 'G':
1041  0 case 'g':
1042  0 case 'h':
1043  0 case 'I':
1044  0 case 'i':
1045  0 case 'J':
1046  0 case 'j':
1047  0 case 'K':
1048  0 case 'k':
1049  0 case 'L':
1050  0 case 'l':
1051  0 case 'M':
1052  0 case 'm':
1053  0 case 'N':
1054  0 case 'n':
1055  0 case 'O':
1056  0 case 'o':
1057  0 case 'P':
1058  0 case 'p':
1059  0 case 'Q':
1060  0 case 'q':
1061  0 case 'R':
1062  0 case 'r':
1063  0 case 'T':
1064  0 case 't':
1065  0 case 'U':
1066  0 case 'u':
1067  0 case 'V':
1068  0 case 'v':
1069  0 case 'W':
1070  0 case 'w':
1071  0 case 'X':
1072  0 case 'x':
1073  0 case 'Y':
1074  0 case 'y':
1075  0 case 'Z':
1076  0 case 'z':
1077    // Console.info(lastSS);
1078  0 Color nonCanColor = getNotCanonicalColor(lastSS);
1079  0 drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX,
1080    x, y, iconOffset, startRes, column, validRes, validEnd);
1081  0 break;
1082  1034 default:
1083  1034 if (isVectorRendering())
1084    {
1085    // draw single full width glyphline
1086  0 drawGlyphLine(g, lastSSX, endRes - x, y, iconOffset);
1087    // disable more glyph lines
1088  0 this.glyphLineDrawn = true;
1089    }
1090    else
1091    {
1092  1034 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1093    }
1094  1034 break;
1095    }
1096    }
1097   
1098  13816 if (row.graph > 0 && row.graphHeight > 0)
1099    {
1100  10991 if (row.graph == AlignmentAnnotation.LINE_GRAPH)
1101    {
1102  447 if (row.graphGroup > -1 && !graphGroupDrawn.get(row.graphGroup))
1103    {
1104    // TODO: JAL-1291 revise rendering model so the graphGroup map is
1105    // computed efficiently for all visible labels
1106  0 float groupmax = -999999, groupmin = 9999999;
1107  0 for (int gg = 0; gg < aa.length; gg++)
1108    {
1109  0 if (aa[gg].graphGroup != row.graphGroup)
1110    {
1111  0 continue;
1112    }
1113   
1114  0 if (aa[gg] != row)
1115    {
1116  0 aa[gg].visible = false;
1117    }
1118  0 if (aa[gg].graphMax > groupmax)
1119    {
1120  0 groupmax = aa[gg].graphMax;
1121    }
1122  0 if (aa[gg].graphMin < groupmin)
1123    {
1124  0 groupmin = aa[gg].graphMin;
1125    }
1126    }
1127   
1128  0 for (int gg = 0; gg < aa.length; gg++)
1129    {
1130  0 if (aa[gg].graphGroup == row.graphGroup)
1131    {
1132  0 drawLineGraph(g, aa[gg], aa[gg].annotations, startRes,
1133    endRes, y, groupmin, groupmax, row.graphHeight);
1134    }
1135    }
1136   
1137  0 graphGroupDrawn.set(row.graphGroup);
1138    }
1139    else
1140    {
1141  447 drawLineGraph(g, row, row_annotations, startRes, endRes, y,
1142    row.graphMin, row.graphMax, row.graphHeight);
1143    }
1144    }
1145  10544 else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
1146    {
1147  10406 drawBarGraph(g, row, row_annotations, startRes, endRes,
1148    row.graphMin, row.graphMax, y, renderHistogram,
1149    renderProfile, normaliseProfile);
1150    }
1151    else
1152    {
1153  138 AnnotationRowRendererI renderer = rendererFactoryI
1154    .getRendererFor(row);
1155  138 if (renderer != null)
1156    {
1157  138 renderer.renderRow(g, charWidth, charHeight, hasHiddenColumns,
1158    av, hiddenColumns, columnSelection, row,
1159    row_annotations, startRes, endRes, row.graphMin,
1160    row.graphMax, y);
1161    }
1162  138 if (debugRedraw)
1163    {
1164  0 if (renderer == null)
1165    {
1166  0 System.err
1167    .println("No renderer found for " + row.toString());
1168    }
1169    else
1170    {
1171  0 Console.warn(
1172    "rendered with " + renderer.getClass().toString());
1173    }
1174    }
1175   
1176    }
1177    }
1178    }
1179    else
1180    {
1181  0 if (clipst && !clipend)
1182    {
1183  0 clipend = true;
1184    }
1185    } // end if_in_visible_region
1186  13815 if (row.graph > 0 && row.hasText)
1187    {
1188  10406 y += charHeight;
1189    }
1190   
1191  13815 if (row.graph == 0)
1192    {
1193  2825 y += aa[i].height;
1194    }
1195    }
1196  2754 if (debugRedraw)
1197    {
1198  0 if (canClip)
1199    {
1200  0 if (clipst)
1201    {
1202  0 Console.warn("Start clip at : " + yfrom + " (index " + f_i + ")");
1203    }
1204  0 if (clipend)
1205    {
1206  0 Console.warn("End clip at : " + yto + " (index " + f_to + ")");
1207    }
1208    }
1209  0 ;
1210  0 Console.warn("Annotation Rendering time:"
1211    + (System.currentTimeMillis() - stime));
1212    }
1213  2754 ;
1214   
1215  2754 return !usedFaded;
1216    }
1217   
1218    public static final Color GLYPHLINE_COLOR = Color.gray;
1219   
1220    public static final Color SHEET_COLOUR = Color.green;
1221   
1222    public static final Color HELIX_COLOUR = Color.red;
1223   
1224    public static final Color STEM_COLOUR = Color.blue;
1225   
1226    // private Color sdNOTCANONICAL_COLOUR;
1227   
 
1228  65428 toggle void drawGlyphLine(Graphics g, int lastSSX, int x, int y, int iconOffset)
1229    {
1230  65428 if (glyphLineDrawn)
1231    {
1232    // if we've drawn a single long glyphline for an export, don't draw the
1233    // bits
1234  0 return;
1235    }
1236  65428 unsetAntialias(g);
1237  65427 g.setColor(GLYPHLINE_COLOR);
1238  65427 g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
1239    }
1240   
 
1241  6017 toggle void drawSheetAnnot(Graphics g, Annotation[] row,
1242   
1243    int lastSSX, int x, int y, int iconOffset, int startRes,
1244    int column, boolean validRes, boolean validEnd)
1245    {
1246  6017 if (!validEnd || !validRes || row == null || row[column] == null
1247    || row[column].secondaryStructure != 'E')
1248    {
1249    // draw the glyphline underneath
1250  6016 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1251   
1252  6016 g.setColor(SHEET_COLOUR);
1253  6016 fillRect(g, lastSSX, y + 4 + iconOffset,
1254    (x * charWidth) - lastSSX - 4, 6);
1255  6016 fillPolygon(g,
1256    new int[]
1257    { (x * charWidth) - 6, (x * charWidth) - 6,
1258    (x * charWidth - 1) },
1259    new int[]
1260    { y + iconOffset + 1, y + 13 + iconOffset,
1261    y + 7 + iconOffset },
1262    3);
1263    }
1264    else
1265    {
1266  1 g.setColor(SHEET_COLOUR);
1267  1 fillRect(g, lastSSX, y + 4 + iconOffset, (x * charWidth) - lastSSX,
1268    6);
1269    }
1270    }
1271   
 
1272  1322 toggle void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX, int x,
1273    int y, int iconOffset, int startRes, int column, boolean validRes,
1274    boolean validEnd)
1275    {
1276  1322 int sCol = (lastSSX / charWidth)
1277    + hiddenColumns.visibleToAbsoluteColumn(startRes);
1278  1322 int x1 = lastSSX;
1279  1322 int x2 = (x * charWidth);
1280   
1281  1322 if (USE_FILL_ROUND_RECT || isVectorRendering())
1282    {
1283    // draw glyph line behind helix (visible in EPS or SVG output)
1284  0 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1285   
1286  0 g.setColor(HELIX_COLOUR);
1287  0 setAntialias(g);
1288  0 int ofs = charWidth / 2;
1289    // Off by 1 offset when drawing rects and ovals
1290    // to offscreen image on the MAC
1291  0 fillRoundRect(g, lastSSX, y + 3 + iconOffset, x2 - x1 - 1, 8, 8, 8);
1292  0 if (sCol == 0 || row[sCol - 1] == null
1293    || row[sCol - 1].secondaryStructure != 'H')
1294    {
1295    }
1296    else
1297    {
1298  0 fillRoundRect(g, lastSSX, y + 3 + iconOffset, x2 - x1 - ofs, 8, 0,
1299    0);
1300    }
1301  0 if (!validRes || row[column] == null
1302    || row[column].secondaryStructure != 'H')
1303    {
1304   
1305    }
1306    else
1307    {
1308  0 fillRoundRect(g, lastSSX + ofs, y + 3 + iconOffset, x2 - x1 - ofs,
1309    8, 0, 0);
1310    }
1311   
1312  0 return;
1313    }
1314   
1315  1322 boolean leftEnd = sCol == 0 || row[sCol - 1] == null
1316    || row[sCol - 1].secondaryStructure != 'H';
1317  1322 boolean rightEnd = !validRes || row[column] == null
1318    || row[column].secondaryStructure != 'H';
1319   
1320  1322 if (leftEnd || rightEnd)
1321    {
1322  1322 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1323    }
1324  1322 g.setColor(HELIX_COLOUR);
1325   
1326  1322 if (leftEnd)
1327    {
1328  1322 fillArc(g, lastSSX, y + 3 + iconOffset, charWidth, 8, 90, 180);
1329  1322 x1 += charWidth / 2;
1330    }
1331   
1332  1322 if (rightEnd)
1333    {
1334  1322 fillArc(g, ((x - 1) * charWidth), y + 3 + iconOffset, charWidth, 8,
1335    270, 180);
1336  1322 x2 -= charWidth / 2;
1337    }
1338   
1339  1322 fillRect(g, x1, y + 3 + iconOffset, x2 - x1, 8);
1340    }
1341   
 
1342  447 toggle void drawLineGraph(Graphics g, AlignmentAnnotation _aa,
1343    Annotation[] aa_annotations, int sRes, int eRes, int y, float min,
1344    float max, int graphHeight)
1345    {
1346  447 if (sRes > aa_annotations.length)
1347    {
1348  1 return;
1349    }
1350  446 Stroke roundStroke = new BasicStroke(1, BasicStroke.CAP_ROUND,
1351    BasicStroke.JOIN_ROUND);
1352  446 Stroke squareStroke = new BasicStroke(1, BasicStroke.CAP_SQUARE,
1353    BasicStroke.JOIN_MITER);
1354  446 Graphics2D g2d = (Graphics2D) g;
1355  446 Stroke prevStroke = g2d.getStroke();
1356  446 g2d.setStroke(roundStroke);
1357   
1358  446 int x = 0;
1359   
1360    // Adjustment for fastpaint to left
1361  446 if (eRes < endRes)
1362    {
1363  0 eRes++;
1364    }
1365   
1366  446 eRes = Math.min(eRes, aa_annotations.length);
1367   
1368  446 if (sRes == 0)
1369    {
1370  444 x++;
1371    }
1372   
1373  446 int y1 = y, y2 = y;
1374  446 float range = max - min;
1375   
1376    // //Draw origin
1377  446 if (min < 0)
1378    {
1379  0 y2 = y - (int) ((0 - min / range) * graphHeight);
1380    }
1381   
1382  446 g.setColor(Color.gray);
1383  446 drawLine(g, squareStroke, x * charWidth - charWidth, y2,
1384    (eRes - sRes) * charWidth, y2);
1385   
1386  446 eRes = Math.min(eRes, aa_annotations.length);
1387   
1388  446 int column;
1389  446 int aaMax = aa_annotations.length - 1;
1390   
1391  20260 while (x < eRes - sRes)
1392    {
1393  19815 column = sRes + x;
1394  19815 if (hasHiddenColumns)
1395    {
1396  6627 column = hiddenColumns.visibleToAbsoluteColumn(column);
1397    }
1398   
1399  19815 if (column > aaMax)
1400    {
1401  0 break;
1402    }
1403   
1404  19815 if (aa_annotations[column] == null)
1405    {
1406  8308 x++;
1407  8308 continue;
1408    }
1409   
1410  11507 if (aa_annotations[column].colour == null)
1411    {
1412  2010 g.setColor(Color.black);
1413    }
1414    else
1415    {
1416  9497 g.setColor(aa_annotations[column].colour);
1417    }
1418   
1419  11507 if (aa_annotations[column - 1] == null
1420    && aa_annotations.length > column + 1
1421    && aa_annotations[column + 1] == null)
1422    {
1423    // standalone value
1424  1692 y1 = y - (int) (((aa_annotations[column].value - min) / range)
1425    * graphHeight);
1426  1692 drawLine(g, x * charWidth + charWidth / 4, y1,
1427    x * charWidth + 3 * charWidth / 4, y1);
1428  1692 x++;
1429  1692 continue;
1430    }
1431   
1432  9815 if (aa_annotations[column - 1] == null)
1433    {
1434  204 x++;
1435  204 continue;
1436    }
1437   
1438  9611 y1 = y - (int) (((aa_annotations[column - 1].value - min) / range)
1439    * graphHeight);
1440  9611 y2 = y - (int) (((aa_annotations[column].value - min) / range)
1441    * graphHeight);
1442   
1443  9611 drawLine(g, (x - 1) * charWidth + charWidth / 2, y1,
1444    x * charWidth + charWidth / 2, y2);
1445  9610 x++;
1446    }
1447   
1448  445 if (_aa.threshold != null)
1449    {
1450  0 g.setColor(_aa.threshold.colour);
1451  0 Graphics2D g2 = (Graphics2D) g;
1452  0 Stroke s = new BasicStroke(1, BasicStroke.CAP_SQUARE,
1453    BasicStroke.JOIN_ROUND, 3f, new float[]
1454    { 5f, 3f }, 0f);
1455   
1456  0 y2 = (int) (y - ((_aa.threshold.value - min) / range) * graphHeight);
1457  0 drawLine(g, s, 0, y2, (eRes - sRes) * charWidth, y2);
1458    }
1459  445 g2d.setStroke(prevStroke);
1460    }
1461   
 
1462  10406 toggle @SuppressWarnings("unused")
1463    void drawBarGraph(Graphics g, AlignmentAnnotation _aa,
1464    Annotation[] aa_annotations, int sRes, int eRes, float min,
1465    float max, int y, boolean renderHistogram, boolean renderProfile,
1466    boolean normaliseProfile)
1467    {
1468  10406 if (sRes > aa_annotations.length)
1469    {
1470  0 return;
1471    }
1472  10406 Font ofont = g.getFont();
1473  10406 eRes = Math.min(eRes, aa_annotations.length);
1474   
1475  10406 int x = 0, y1 = y, y2 = y;
1476   
1477  10406 float range = max - min;
1478   
1479  10406 if (min < 0)
1480    {
1481  0 y2 = y - (int) ((0 - min / (range)) * _aa.graphHeight);
1482    }
1483   
1484  10406 g.setColor(Color.gray);
1485   
1486  10406 drawLine(g, x, y2, (eRes - sRes) * charWidth, y2);
1487   
1488  10406 int column;
1489  10406 int aaMax = aa_annotations.length - 1;
1490  526481 while (x < eRes - sRes)
1491    {
1492  516078 column = sRes + x;
1493  516078 if (hasHiddenColumns)
1494    {
1495  95743 column = hiddenColumns.visibleToAbsoluteColumn(column);
1496    }
1497   
1498  516078 if (column > aaMax)
1499    {
1500  4 break;
1501    }
1502   
1503  516074 if (aa_annotations[column] == null)
1504    {
1505  1631 x++;
1506  1631 continue;
1507    }
1508  514443 if (aa_annotations[column].colour == null)
1509    {
1510  156474 g.setColor(Color.black);
1511    }
1512    else
1513    {
1514  357969 g.setColor(aa_annotations[column].colour);
1515    }
1516   
1517  514441 y1 = y - (int) (((aa_annotations[column].value - min) / (range))
1518    * _aa.graphHeight);
1519   
1520  514441 if (renderHistogram)
1521    {
1522  514441 if (y1 - y2 > 0)
1523    {
1524  0 fillRect(g, x * charWidth, y2, charWidth, y1 - y2);
1525    }
1526    else
1527    {
1528  514441 fillRect(g, x * charWidth, y1, charWidth, y2 - y1);
1529    }
1530    }
1531    // draw profile if available
1532  514445 if (renderProfile)
1533    {
1534   
1535    /*
1536    * {profile type, #values, total count, char1, pct1, char2, pct2...}
1537    */
1538  102061 int profl[] = getProfileFor(_aa, column);
1539   
1540    // just try to draw the logo if profl is not null
1541  102061 if (profl != null && profl[2] != 0)
1542    {
1543  102061 boolean isStructureProfile = profl[0] == AlignmentAnnotation.STRUCTURE_PROFILE;
1544  102061 boolean isCdnaProfile = profl[0] == AlignmentAnnotation.CDNA_PROFILE;
1545  102061 float ht = normaliseProfile ? y - _aa.graphHeight : y1;
1546  102061 final double normaliseFactor = normaliseProfile ? _aa.graphHeight
1547    : (y2 - y1);
1548   
1549    /**
1550    * Render a single base for a sequence profile, a base pair for
1551    * structure profile, and a triplet for a cdna profile
1552    */
1553  102061 char[] dc = new char[isStructureProfile ? 2
1554  102061 : (isCdnaProfile ? 3 : 1)];
1555   
1556    // lm is not necessary - we can just use fm - could be off by no more
1557    // than 0.5 px
1558    // LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
1559    // Console.info(asc + " " + dec + " " + (asc -
1560    // lm.getAscent())
1561    // + " " + (dec - lm.getDescent()));
1562   
1563  102061 double asc = fm.getAscent();
1564  102061 double dec = fm.getDescent();
1565  102061 double fht = fm.getHeight();
1566   
1567  102061 double scale = 1f / (normaliseProfile ? profl[2] : 100f);
1568    // float ofontHeight = 1f / fm.getAscent();// magnify to fill box
1569   
1570    /*
1571    * Traverse the character(s)/percentage data in the array
1572    */
1573   
1574  102061 float ht2 = ht;
1575   
1576    // profl[1] is the number of values in the profile
1577  296442 for (int i = 0, c = 3, last = profl[1]; i < last; i++)
1578    {
1579   
1580  194381 String s;
1581  194381 if (isStructureProfile)
1582    {
1583    // todo can we encode a structure pair as an int, like codons?
1584  0 dc[0] = (char) profl[c++];
1585  0 dc[1] = (char) profl[c++];
1586  0 s = new String(dc);
1587    }
1588  194381 else if (isCdnaProfile)
1589    {
1590  0 CodingUtils.decodeCodon2(profl[c++], dc);
1591  0 s = new String(dc);
1592    }
1593    else
1594    {
1595  194381 dc[0] = (char) profl[c++];
1596  194381 s = new String(dc);
1597    }
1598    // next profl[] position is profile % for the character(s)
1599   
1600  194382 int percent = profl[c++];
1601  194382 if (percent == 0)
1602    {
1603    // failsafe in case a count rounds down to 0%
1604  0 continue;
1605    }
1606  194382 double newHeight = normaliseFactor * scale * percent;
1607   
1608    /*
1609    * Set character colour as per alignment colour scheme; use the
1610    * codon translation if a cDNA profile
1611    */
1612  194382 Color colour = null;
1613  194381 if (isCdnaProfile)
1614    {
1615  0 final String codonTranslation = ResidueProperties
1616    .codonTranslate(s);
1617  0 colour = profcolour.findColour(codonTranslation.charAt(0),
1618    column, null);
1619    }
1620  194381 if (_aa.label == MessageManager
1621    .getString("label.ssconsensus_label"))
1622    {
1623  0 colour = AlignmentUtils
1624    .getSecondaryStructureAnnotationColour(dc[0]);
1625    }
1626    else
1627    {
1628   
1629  194380 colour = profcolour.findColour(dc[0], column, null);
1630   
1631    }
1632  194383 g.setColor(colour == Color.white ? Color.lightGray : colour);
1633   
1634    // Debug - render boxes around characters
1635    // g.setColor(Color.red);
1636    // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
1637    // (int)(scl));
1638    // g.setColor(profcolour.findColour(dc[0]).darker());
1639   
1640  194383 double sx = 1f * charWidth / fm.charsWidth(dc, 0, dc.length);
1641  194383 double sy = newHeight / asc;
1642  194383 double newAsc = asc * sy;
1643  194383 double newDec = dec * sy;
1644    // it is not necessary to recalculate lm for the new font.
1645    // note: lm.getBaselineOffsets()[lm.getBaselineIndex()]) must be 0
1646    // by definition. Was:
1647    // int hght = (int) (ht + (newAsc - newDec);
1648    // - lm.getBaselineOffsets()[lm.getBaselineIndex()]));
1649   
1650  194383 if (Platform.isJS())
1651    {
1652    /*
1653    * SwingJS does not implement font.deriveFont()
1654    * so use a scaling transform to draw instead,
1655    * this is off by a very small amount
1656    */
1657  0 final int hght = (int) (ht2 + (newAsc - newDec));
1658  0 Graphics2D gg = (Graphics2D) g;
1659  0 int xShift = (int) Math.round(x * charWidth / sx);
1660  0 int yShift = (int) Math.round(hght / sy);
1661  0 gg.transform(AffineTransform.getScaleInstance(sx, sy));
1662  0 gg.drawString(s, xShift, yShift);
1663  0 gg.transform(
1664    AffineTransform.getScaleInstance(1D / sx, 1D / sy));
1665  0 ht2 += newHeight;
1666    }
1667    else
1668    /**
1669    * Java only
1670    *
1671    * @j2sIgnore
1672    */
1673    {
1674    // Java ('normal') method is to scale the font to fit
1675   
1676  194383 final int hght = (int) (ht + (newAsc - newDec));
1677  194383 Font font = ofont
1678    .deriveFont(AffineTransform.getScaleInstance(sx, sy));
1679  194383 g.setFont(font);
1680  194383 g.drawChars(dc, 0, dc.length, x * charWidth, hght);
1681  194383 g.setFont(ofont);
1682   
1683  194383 ht += newHeight;
1684    }
1685    }
1686    }
1687    }
1688  514445 x++;
1689    }
1690  10406 if (_aa.threshold != null)
1691    {
1692  0 g.setColor(_aa.threshold.colour);
1693  0 Stroke s = new BasicStroke(1, BasicStroke.CAP_SQUARE,
1694    BasicStroke.JOIN_ROUND, 3f, new float[]
1695    { 5f, 3f }, 0f);
1696   
1697  0 y2 = (int) (y
1698    - ((_aa.threshold.value - min) / range) * _aa.graphHeight);
1699  0 drawLine(g, s, 0, y2, (eRes - sRes) * charWidth, y2);
1700    }
1701    }
1702   
1703    // used by overview window
 
1704  0 toggle public void drawGraph(Graphics g, AlignmentAnnotation _aa,
1705    Annotation[] aa_annotations, int width, int y, int sRes, int eRes)
1706    {
1707  0 eRes = Math.min(eRes, aa_annotations.length);
1708  0 g.setColor(Color.white);
1709  0 fillRect(g, 0, 0, width, y);
1710  0 g.setColor(new Color(0, 0, 180));
1711   
1712  0 int x = 0, height;
1713   
1714  0 for (int j = sRes; j < eRes; j++)
1715    {
1716  0 if (aa_annotations[j] != null)
1717    {
1718  0 if (aa_annotations[j].colour == null)
1719    {
1720  0 g.setColor(Color.black);
1721    }
1722    else
1723    {
1724  0 g.setColor(aa_annotations[j].colour);
1725    }
1726   
1727  0 height = (int) ((aa_annotations[j].value / _aa.graphMax) * y);
1728  0 if (height > y)
1729    {
1730  0 height = y;
1731    }
1732   
1733  0 fillRect(g, x, y - height, charWidth, height);
1734    }
1735  0 x += charWidth;
1736    }
1737    }
1738   
 
1739  0 toggle Color getNotCanonicalColor(char lastss)
1740    {
1741  0 switch (lastss)
1742    {
1743  0 case '{':
1744  0 case '}':
1745  0 return new Color(255, 125, 5);
1746   
1747  0 case '[':
1748  0 case ']':
1749  0 return new Color(245, 115, 10);
1750   
1751  0 case '>':
1752  0 case '<':
1753  0 return new Color(235, 135, 15);
1754   
1755  0 case 'A':
1756  0 case 'a':
1757  0 return new Color(225, 105, 20);
1758   
1759  0 case 'B':
1760  0 case 'b':
1761  0 return new Color(215, 145, 30);
1762   
1763  0 case 'C':
1764  0 case 'c':
1765  0 return new Color(205, 95, 35);
1766   
1767  0 case 'D':
1768  0 case 'd':
1769  0 return new Color(195, 155, 45);
1770   
1771  0 case 'E':
1772  0 case 'e':
1773  0 return new Color(185, 85, 55);
1774   
1775  0 case 'F':
1776  0 case 'f':
1777  0 return new Color(175, 165, 65);
1778   
1779  0 case 'G':
1780  0 case 'g':
1781  0 return new Color(170, 75, 75);
1782   
1783  0 case 'H':
1784  0 case 'h':
1785  0 return new Color(160, 175, 85);
1786   
1787  0 case 'I':
1788  0 case 'i':
1789  0 return new Color(150, 65, 95);
1790   
1791  0 case 'J':
1792  0 case 'j':
1793  0 return new Color(140, 185, 105);
1794   
1795  0 case 'K':
1796  0 case 'k':
1797  0 return new Color(130, 55, 110);
1798   
1799  0 case 'L':
1800  0 case 'l':
1801  0 return new Color(120, 195, 120);
1802   
1803  0 case 'M':
1804  0 case 'm':
1805  0 return new Color(110, 45, 130);
1806   
1807  0 case 'N':
1808  0 case 'n':
1809  0 return new Color(100, 205, 140);
1810   
1811  0 case 'O':
1812  0 case 'o':
1813  0 return new Color(90, 35, 150);
1814   
1815  0 case 'P':
1816  0 case 'p':
1817  0 return new Color(85, 215, 160);
1818   
1819  0 case 'Q':
1820  0 case 'q':
1821  0 return new Color(75, 25, 170);
1822   
1823  0 case 'R':
1824  0 case 'r':
1825  0 return new Color(65, 225, 180);
1826   
1827  0 case 'S':
1828  0 case 's':
1829  0 return new Color(55, 15, 185);
1830   
1831  0 case 'T':
1832  0 case 't':
1833  0 return new Color(45, 235, 195);
1834   
1835  0 case 'U':
1836  0 case 'u':
1837  0 return new Color(35, 5, 205);
1838   
1839  0 case 'V':
1840  0 case 'v':
1841  0 return new Color(25, 245, 215);
1842   
1843  0 case 'W':
1844  0 case 'w':
1845  0 return new Color(15, 0, 225);
1846   
1847  0 case 'X':
1848  0 case 'x':
1849  0 return new Color(10, 255, 235);
1850   
1851  0 case 'Y':
1852  0 case 'y':
1853  0 return new Color(5, 150, 245);
1854   
1855  0 case 'Z':
1856  0 case 'z':
1857  0 return new Color(0, 80, 255);
1858   
1859  0 default:
1860  0 Console.info("This is not a interaction : " + lastss);
1861  0 return null;
1862   
1863    }
1864    }
1865   
 
1866  6016 toggle private void fillPolygon(Graphics g, int[] xpoints, int[] ypoints, int n)
1867    {
1868  6016 setAntialias(g);
1869  6016 g.fillPolygon(xpoints, ypoints, n);
1870    }
1871   
1872    /*
1873    private void fillRect(Graphics g, int a, int b, int c, int d)
1874    {
1875    fillRect(g, false, a, b, c, d);
1876    }*/
1877   
 
1878  521780 toggle private void fillRect(Graphics g, int a, int b, int c, int d)
1879    {
1880  521780 unsetAntialias(g);
1881  521780 g.fillRect(a, b, c, d);
1882    }
1883   
 
1884  0 toggle private void fillRoundRect(Graphics g, int a, int b, int c, int d, int e,
1885    int f)
1886    {
1887  0 setAntialias(g);
1888  0 g.fillRoundRect(a, b, c, d, e, f);
1889    }
1890   
 
1891  2644 toggle private void fillArc(Graphics g, int a, int b, int c, int d, int e, int f)
1892    {
1893  2644 setAntialias(g);
1894  2644 g.fillArc(a, b, c, d, e, f);
1895    }
1896   
 
1897  446 toggle private void drawLine(Graphics g, Stroke s, int a, int b, int c, int d)
1898    {
1899  446 Graphics2D g2d = (Graphics2D) g;
1900  446 Stroke p = g2d.getStroke();
1901  446 g2d.setStroke(s);
1902  446 drawLine(g, a, b, c, d);
1903  446 g2d.setStroke(p);
1904    }
1905   
 
1906  22155 toggle private void drawLine(Graphics g, int a, int b, int c, int d)
1907    {
1908  22155 setAntialias(g);
1909  22155 g.drawLine(a, b, c, d);
1910    }
1911   
 
1912  434880 toggle private void setAntialias(Graphics g)
1913    {
1914  434880 if (isVectorRendering())
1915    {
1916    // no need to antialias vector drawings
1917  6650 return;
1918    }
1919  428232 if (Cache.getDefault("ANTI_ALIAS", true))
1920    {
1921  47802 Graphics2D g2d = (Graphics2D) g;
1922  47802 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1923    RenderingHints.VALUE_ANTIALIAS_ON);
1924    }
1925    }
1926   
 
1927  1292718 toggle private void unsetAntialias(Graphics g)
1928    {
1929  1292717 if (isVectorRendering())
1930    {
1931    // no need to antialias vector drawings
1932  17584 return;
1933    }
1934  1275133 Graphics2D g2d = (Graphics2D) g;
1935  1275134 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1936    RenderingHints.VALUE_ANTIALIAS_OFF);
1937    }
1938   
 
1939  14 toggle public void setVectorRendering(boolean b)
1940    {
1941  14 renderingVectors = b;
1942    }
1943   
 
1944  1786998 toggle public boolean isVectorRendering()
1945    {
1946  1786996 return renderingVectors;
1947    }
1948    }