Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 14:43:25 GMT
  2. Package jalview.renderer

File AnnotationRenderer.java

 

Coverage histogram

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

Code metrics

324
789
26
1
2,026
1,502
429
0.54
30.35
26
16.5

Classes

Class Line # Actions
AnnotationRenderer 66 789 429
0.597892959.8%
 

Contributing tests

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