Clover icon

Coverage Report

  1. Project Clover database Thu Dec 4 2025 16:11:35 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.59613759.6%
 

Contributing tests

This file is covered by 208 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  528 toggle public AnnotationRenderer()
160    {
161  528 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  528 toggle public AnnotationRenderer(boolean debugRedraw)
172    {
173  528 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  68 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  68 int sCol = (lastSSX / charWidth)
273    + hiddenColumns.visibleToAbsoluteColumn(startRes);
274  68 int x1 = lastSSX;
275  68 int x2 = (x * charWidth);
276   
277  68 String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
278    : row_annotations[column - 1].displayCharacter;
279   
280  68 boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
281    || !dc.equals(row_annotations[sCol - 1].displayCharacter)
282    || !validEnd;
283  68 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  68 if (diffupstream || diffdownstream)
291    {
292    // draw glyphline under arrow
293  68 drawGlyphLine(g, lastSSX, x, y, iconOffset);
294    }
295  68 g.setColor(nonCanColor);
296  68 if (column > 0 && Rna.isClosingParenthesis(dc))
297    {
298   
299  28 if (diffupstream)
300    // if (validRes && column>1 && row_annotations[column-2]!=null &&
301    // dc.equals(row_annotations[column-2].displayCharacter))
302    {
303  28 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  28 x1 += 5;
309    }
310  28 if (diffdownstream)
311    {
312  20 x2 -= 1;
313    }
314    }
315    else
316    {
317   
318    // display a forward arrow
319  40 if (diffdownstream)
320    {
321  40 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  40 x2 -= 5;
327    }
328  40 if (diffupstream)
329    {
330  40 x1 += 1;
331    }
332    }
333    // draw arrow body
334  68 unsetAntialias(g);
335  68 fillRect(g, x1, y + 4 + iconOffset, x2 - x1, 6);
336    }
337   
338    // public void updateFromAnnotationPanel(FontMetrics annotFM, AlignViewportI
339    // av)
 
340  3189 toggle public void updateFromAwtRenderPanel(AwtRenderPanelI annotPanel,
341    AlignViewportI av)
342    {
343  3189 fm = annotPanel.getFontMetrics();
344  3189 annotationPanel = annotPanel;
345  3189 fadedImage = annotPanel.getFadedImage();
346  3189 imgWidth = annotPanel.getFadedImageWidth();
347    // visible area for rendering
348  3189 int[] bounds = annotPanel.getVisibleVRange();
349  3189 if (bounds != null)
350    {
351  3177 sOffset = bounds[0];
352  3177 visHeight = bounds[1];
353  3177 if (visHeight == 0)
354    {
355  0 useClip = false;
356    }
357    else
358    {
359  3177 useClip = canClip;
360    }
361    }
362    else
363    {
364  12 useClip = false;
365    }
366   
367  3189 rendererFactoryI = AnnotationRendererFactory.getRendererFactory();
368  3189 updateFromAlignViewport(av);
369    }
370   
 
371  3189 toggle public void updateFromAlignViewport(AlignViewportI av)
372    {
373  3189 charWidth = av.getCharWidth();
374  3189 endRes = av.getRanges().getEndRes();
375  3189 charHeight = av.getCharHeight();
376  3189 hasHiddenColumns = av.hasHiddenColumns();
377  3189 validCharWidth = av.isValidCharWidth();
378  3189 av_renderHistogram = av.isShowConsensusHistogram();
379  3189 av_renderProfile = av.isShowSequenceLogo();
380  3189 av_normaliseProfile = av.isNormaliseSequenceLogo();
381  3189 profcolour = av.getResidueShading();
382  3189 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  2771 ColourSchemeI col = av.getAlignment().isNucleotide()
390    ? new NucleotideColourScheme()
391    : new ZappoColourScheme();
392  2771 profcolour = new ResidueShader(col);
393    }
394  3189 columnSelection = av.getColumnSelection();
395  3189 hiddenColumns = av.getAlignment().getHiddenColumns();
396  3189 hconsensus = av.getSequenceConsensusHash();
397  3189 hSSconsensus = av.getSequenceSSConsensusHash();
398  3189 complementConsensus = av.getComplementConsensusHash();
399  3189 hStrucConsensus = av.getRnaStructureConsensusHash();
400  3189 av_ignoreGapsConsensus = av.isIgnoreGapsConsensus();
401  3189 av_ignoreBelowBackground = av.isIgnoreBelowBackground();
402  3189 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  36215 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  36215 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  36215 if (aa.autoCalculated
430    && (aa.label.startsWith("Consensus") || aa.label
431    .startsWith("cDNA Consensus")))
432    {
433  36215 boolean forComplement = aa.label.startsWith("cDNA Consensus");
434  36215 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  36215 if (aa.groupRef == null && aa.sequenceRef == null)
445    {
446  28223 if (forComplement)
447    {
448  0 return AAFrequency.extractCdnaProfile(complementConsensus[column],
449    av_ignoreGapsConsensus);
450    }
451    else
452    {
453  28223 return AAFrequency.extractProfile(hconsensus.get(column),
454    av_ignoreGapsConsensus);
455    }
456    }
457    }
458   
459  7992 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  7992 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  7992 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  3189 toggle public boolean drawComponent(AwtRenderPanelI annotPanel,
543    AlignViewportI av, Graphics g, int activeRow, int startRes,
544    int endRes)
545    {
546  3189 if (g instanceof EpsGraphics2D || g instanceof SVGGraphics2D)
547    {
548  14 this.setVectorRendering(true);
549    }
550  3189 Graphics2D g2d = (Graphics2D) g;
551   
552  3189 long stime = System.currentTimeMillis();
553  3189 boolean usedFaded = false;
554    // NOTES:
555    // AnnotationPanel needs to implement: ImageObserver, access to
556    // AlignViewport
557  3189 updateFromAwtRenderPanel(annotPanel, av);
558  3189 fm = g.getFontMetrics();
559  3189 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
560    // int temp = 0;
561  3189 if (aa == null)
562    {
563  0 return false;
564    }
565  3189 int x = 0, y = 0;
566  3189 int column = 0;
567  3189 char lastSS;
568  3189 int lastSSX;
569  3189 int iconOffset = 0;
570  3189 boolean validRes = false;
571  3189 boolean validEnd = false;
572  3189 boolean labelAllCols = false;
573    // boolean centreColLabels;
574    // boolean centreColLabelsDef = av.isCentreColumnLabels();
575  3189 boolean scaleColLabel = false;
576  3189 final AlignmentAnnotation consensusAnnot = av
577    .getAlignmentConsensusAnnotation();
578  3189 final AlignmentAnnotation structConsensusAnnot = av
579    .getAlignmentStrucConsensusAnnotation();
580  3189 final AlignmentAnnotation complementConsensusAnnot = av
581    .getComplementConsensusAnnotation();
582  3189 final List<AlignmentAnnotation> ssConsensusAnnot = av
583    .getAlignmentSecondaryStructureConsensusAnnotation();
584   
585  3189 BitSet graphGroupDrawn = new BitSet();
586  3189 int charOffset = 0; // offset for a label
587    // \u03B2 \u03B1
588    // debug ints
589  3189 int yfrom = 0, f_i = 0, yto = 0, f_to = 0;
590  3189 boolean clipst = false, clipend = false;
591  20593 for (int i = 0; i < aa.length; i++)
592    {
593  17405 AlignmentAnnotation row = aa[i];
594  17405 boolean renderHistogram = true;
595  17405 boolean renderProfile = false;
596  17405 boolean normaliseProfile = false;
597  17405 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  17405 if (row.groupRef != null && (row == row.groupRef.getConsensus()
604    || (row.groupRef.getSSConsensus(null) != null
605    && row.groupRef.getSSConsensus(null).contains(row))))
606    {
607  126 renderHistogram = row.groupRef.isShowConsensusHistogram();
608  126 renderProfile = row.groupRef.isShowSequenceLogo();
609  126 normaliseProfile = row.groupRef.isNormaliseSequenceLogo();
610    }
611  17279 else if (row == consensusAnnot || row == structConsensusAnnot
612    || row == complementConsensusAnnot
613    || (ssConsensusAnnot != null
614    && ssConsensusAnnot.contains(row)))
615    {
616  3199 renderHistogram = av_renderHistogram;
617  3199 renderProfile = av_renderProfile;
618  3199 normaliseProfile = av_normaliseProfile;
619    }
620  14080 else if (InformationThread.HMM_CALC_ID.equals(row.getCalcId()))
621    {
622  19 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  19 renderHistogram = av.isShowInformationHistogram();
631  19 renderProfile = av.isShowHMMSequenceLogo();
632  19 normaliseProfile = av.isNormaliseHMMSequenceLogo();
633    }
634    }
635   
636  17405 Annotation[] row_annotations = row.annotations;
637  17405 if (!row.isForDisplay())
638    {
639  2511 continue;
640    }
641    // centreColLabels = row.centreColLabels || centreColLabelsDef;
642  14894 labelAllCols = row.showAllColLabels;
643  14894 scaleColLabel = row.scaleColLabel;
644  14894 lastSS = ' ';
645  14894 lastSSX = 0;
646   
647  14894 if (!useClip || ((y - charHeight) < visHeight
648    && (y + row.height + charHeight * 2) >= sOffset))
649    {// if_in_visible_region
650  14894 if (!clipst)
651    {
652  3172 clipst = true;
653  3172 yfrom = y;
654  3172 f_i = i;
655    }
656  14894 yto = y;
657  14894 f_to = i;
658  14894 if (row.graph > 0)
659    {
660  13017 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  13017 y += row.height;
667   
668  13017 if (row.hasText)
669    {
670  11719 iconOffset = charHeight - fm.getDescent();
671  11719 y -= charHeight;
672    }
673    }
674  1877 else if (row.hasText)
675    {
676  378 iconOffset = charHeight - fm.getDescent();
677   
678    }
679    else
680    {
681  1499 iconOffset = 0;
682    }
683   
684  14894 if (row.autoCalculated && av.isCalculationInProgress(row))
685    {
686  408 y += charHeight;
687  408 usedFaded = true;
688  408 g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0,
689    y - row.height, imgWidth, y, annotationPanel);
690  408 g.setColor(Color.black);
691    // g.drawString("Calculating "+aa[i].label+"....",20, y-row.height/2);
692   
693  408 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  14486 this.glyphLineDrawn = false;
725  14486 x = (startRes == 0) ? 0 : -1;
726  738039 while (x < endRes - startRes)
727    {
728  723613 if (hasHiddenColumns)
729    {
730  36008 column = hiddenColumns.visibleToAbsoluteColumn(startRes + x);
731  36009 if (column > row_annotations.length - 1)
732    {
733  52 break;
734    }
735    }
736    else
737    {
738  687605 column = startRes + x;
739    }
740   
741  723563 if ((row_annotations == null)
742    || (row_annotations.length <= column)
743    || (row_annotations[column] == null))
744    {
745  145579 validRes = false;
746    }
747    else
748    {
749  577984 validRes = true;
750    }
751  723562 final String displayChar = validRes
752    ? row_annotations[column].displayCharacter
753    : null;
754  723560 if (x > -1)
755    {
756  722050 unsetAntialias(g);
757  722051 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  722047 if (row.getInvalidStrucPos() > x)
770    {
771  0 g.setColor(Color.orange);
772  0 fillRect(g, x * charWidth, y, charWidth, charHeight);
773    }
774  722052 else if (row.getInvalidStrucPos() == x)
775    {
776  0 g.setColor(Color.orange.darker());
777  0 fillRect(g, x * charWidth, y, charWidth, charHeight);
778    }
779  722055 if (validCharWidth && validRes && displayChar != null
780    && (displayChar.length() > 0))
781    {
782    // Graphics2D gg = (g);
783  384764 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  384764 boolean scaledToFit = false;
791  384764 float fmScaling = 1f;
792  384764 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  384764 charOffset = (int) ((charWidth - fmWidth) / 2f);
802   
803  384764 if (row_annotations[column].colour == null)
804    {
805  147834 g2d.setColor(Color.black);
806    }
807    else
808    {
809  236930 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  384764 final int xPos = (x * charWidth) + charOffset;
817  384764 final int yPos = y + iconOffset;
818   
819    /*
820    * translate to drawing position _before_ applying any scaling
821    */
822  384764 g2d.translate(xPos, yPos);
823  384764 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  384764 setAntialias(g);
833  384764 if (column == 0 || row.graph > 0)
834    {
835  381130 g2d.drawString(displayChar, 0, 0);
836    }
837  3634 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  3334 g2d.drawString(displayChar, 0, 0);
844    }
845  384764 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  384763 g2d.translate(-xPos, -yPos);
855    }
856    }
857  723560 if (row.hasIcons)
858    {
859  95777 char ss = validRes ? row_annotations[column].secondaryStructure
860    : '-';
861   
862  95777 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  95777 if (ss == '[')
873    {
874  0 if ((displayChar.indexOf(']') > -1))
875    {
876  0 ss = ']';
877   
878    }
879    }
880  95777 if (ss == '{')
881    {
882    // distinguish between forward/backward base-pairing
883  0 if (displayChar.indexOf('}') > -1)
884    {
885  0 ss = '}';
886   
887    }
888    }
889  95777 if (ss == '<')
890    {
891    // distinguish between forward/backward base-pairing
892  180 if (displayChar.indexOf('<') > -1)
893    {
894  180 ss = '>';
895   
896    }
897    }
898  95777 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  95777 if (!validRes || (ss != lastSS))
910    {
911   
912  78224 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  77826 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  739 case 'H':
929  739 if (!isRNA)
930    {
931  739 drawHelixAnnot(g, row_annotations, lastSSX, x, y,
932    iconOffset, startRes, column, validRes,
933    validEnd);
934  739 break;
935    }
936    // no break if isRNA - falls through to drawNotCanonicalAnnot!
937  4052 case 'E':
938  4052 if (!isRNA)
939    {
940  4052 drawSheetAnnot(g, row_annotations, lastSSX, x, y,
941    iconOffset, startRes, column, validRes,
942    validEnd);
943  4052 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  58 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  58 Color nonCanColor = getNotCanonicalColor(lastSS);
1005  58 drawNotCanonicalAnnot(g, nonCanColor, row_annotations,
1006    lastSSX, x, y, iconOffset, startRes, column,
1007    validRes, validEnd);
1008    // temp = x;
1009  58 break;
1010  72977 default:
1011  72977 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  72977 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1021    }
1022  72977 break;
1023    }
1024    }
1025  78224 if (validRes)
1026    {
1027  5797 lastSS = ss;
1028    }
1029    else
1030    {
1031  72427 lastSS = ' ';
1032    }
1033  78224 if (x > -1)
1034    {
1035  77826 lastSSX = (x * charWidth);
1036    }
1037    }
1038    }
1039  723555 column++;
1040  723554 x++;
1041    }
1042  14486 if (column >= row_annotations.length)
1043    {
1044  5250 column = row_annotations.length - 1;
1045  5250 validEnd = false;
1046    }
1047    else
1048    {
1049  9236 validEnd = true;
1050    }
1051  14486 if ((row_annotations == null) || (row_annotations.length <= column)
1052    || (row_annotations[column] == null))
1053    {
1054  2478 validRes = false;
1055    }
1056    else
1057    {
1058  12008 validRes = true;
1059    }
1060    // x ++;
1061   
1062  14486 if (row.hasIcons)
1063    {
1064  1428 switch (lastSS)
1065    {
1066   
1067  80 case 'H':
1068  80 if (!isRNA)
1069    {
1070  80 drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
1071    startRes, column, validRes, validEnd);
1072  80 break;
1073    }
1074    // no break if isRNA - fall through to drawNotCanonicalAnnot!
1075   
1076  76 case 'E':
1077  76 if (!isRNA)
1078    {
1079  76 drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
1080    startRes, column, validRes, validEnd);
1081  76 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  10 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  10 Color nonCanColor = getNotCanonicalColor(lastSS);
1148  10 drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX,
1149    x, y, iconOffset, startRes, column, validRes, validEnd);
1150  10 break;
1151  1262 default:
1152  1262 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  1262 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1162    }
1163  1262 break;
1164    }
1165    }
1166   
1167  14486 if (row.graph > 0 && row.graphHeight > 0)
1168    {
1169  12609 if (row.graph == AlignmentAnnotation.LINE_GRAPH)
1170    {
1171  937 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  99 float groupmax = -999999, groupmin = 9999999;
1176  3465 for (int gg = 0; gg < aa.length; gg++)
1177    {
1178  3366 if (aa[gg].graphGroup != row.graphGroup)
1179    {
1180  3168 continue;
1181    }
1182   
1183  198 if (aa[gg] != row)
1184    {
1185  99 aa[gg].visible = false;
1186    }
1187  198 if (aa[gg].graphMax > groupmax)
1188    {
1189  198 groupmax = aa[gg].graphMax;
1190    }
1191  198 if (aa[gg].graphMin < groupmin)
1192    {
1193  99 groupmin = aa[gg].graphMin;
1194    }
1195    }
1196   
1197  3465 for (int gg = 0; gg < aa.length; gg++)
1198    {
1199  3366 if (aa[gg].graphGroup == row.graphGroup)
1200    {
1201  198 drawLineGraph(g, aa[gg], aa[gg].annotations, startRes,
1202    endRes, y, groupmin, groupmax, row.graphHeight);
1203    }
1204    }
1205   
1206  99 graphGroupDrawn.set(row.graphGroup);
1207    }
1208    else
1209    {
1210  838 drawLineGraph(g, row, row_annotations, startRes, endRes, y,
1211    row.graphMin, row.graphMax, row.graphHeight);
1212    }
1213    }
1214  11672 else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
1215    {
1216  11267 drawBarGraph(g, row, row_annotations, startRes, endRes,
1217    row.graphMin, row.graphMax, y, renderHistogram,
1218    renderProfile, normaliseProfile);
1219    }
1220    else
1221    {
1222  405 AnnotationRowRendererI renderer = rendererFactoryI
1223    .getRendererFor(row);
1224  405 if (renderer != null)
1225    {
1226  405 renderer.renderRow(g, charWidth, charHeight, hasHiddenColumns,
1227    av, hiddenColumns, columnSelection, row,
1228    row_annotations, startRes, endRes, row.graphMin,
1229    row.graphMax, y);
1230    }
1231  405 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  14485 if (row.graph > 0 && row.hasText)
1256    {
1257  11310 y += charHeight;
1258    }
1259   
1260  14485 if (row.graph == 0)
1261    {
1262  1877 y += aa[i].height;
1263    }
1264    }
1265  3188 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  3188 ;
1283   
1284  3188 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  79193 toggle void drawGlyphLine(Graphics g, int lastSSX, int x, int y, int iconOffset)
1298    {
1299  79193 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  79193 unsetAntialias(g);
1306  79193 g.setColor(GLYPHLINE_COLOR);
1307  79193 g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
1308    }
1309   
 
1310  4128 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  4128 if (!validEnd || !validRes || row == null || row[column] == null
1316    || row[column].secondaryStructure != 'E')
1317    {
1318    // draw the glyphline underneath
1319  4067 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1320   
1321  4067 g.setColor(SHEET_COLOUR);
1322  4067 fillRect(g, lastSSX, y + 4 + iconOffset,
1323    (x * charWidth) - lastSSX - 4, 6);
1324  4067 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  61 g.setColor(SHEET_COLOUR);
1336  61 fillRect(g, lastSSX, y + 4 + iconOffset, (x * charWidth) - lastSSX,
1337    6);
1338    }
1339    }
1340   
 
1341  819 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  819 int sCol = (lastSSX / charWidth)
1346    + hiddenColumns.visibleToAbsoluteColumn(startRes);
1347  819 int x1 = lastSSX;
1348  819 int x2 = (x * charWidth);
1349   
1350  819 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  819 boolean leftEnd = sCol == 0 || row[sCol - 1] == null
1385    || row[sCol - 1].secondaryStructure != 'H';
1386  819 boolean rightEnd = !validRes || row[column] == null
1387    || row[column].secondaryStructure != 'H';
1388   
1389  819 if (leftEnd || rightEnd)
1390    {
1391  819 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1392    }
1393  819 g.setColor(HELIX_COLOUR);
1394   
1395  819 if (leftEnd)
1396    {
1397  819 fillArc(g, lastSSX, y + 3 + iconOffset, charWidth, 8, 90, 180);
1398  819 x1 += charWidth / 2;
1399    }
1400   
1401  819 if (rightEnd)
1402    {
1403  739 fillArc(g, ((x - 1) * charWidth), y + 3 + iconOffset, charWidth, 8,
1404    270, 180);
1405  739 x2 -= charWidth / 2;
1406    }
1407   
1408  819 fillRect(g, x1, y + 3 + iconOffset, x2 - x1, 8);
1409    }
1410   
 
1411  1036 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  1036 if (sRes > aa_annotations.length)
1416    {
1417  14 return;
1418    }
1419  1022 Stroke roundStroke = new BasicStroke(1, BasicStroke.CAP_ROUND,
1420    BasicStroke.JOIN_ROUND);
1421  1022 Stroke squareStroke = new BasicStroke(1, BasicStroke.CAP_SQUARE,
1422    BasicStroke.JOIN_MITER);
1423  1022 Graphics2D g2d = (Graphics2D) g;
1424  1022 Stroke prevStroke = g2d.getStroke();
1425  1022 g2d.setStroke(roundStroke);
1426   
1427  1022 int x = 0;
1428   
1429    // Adjustment for fastpaint to left
1430  1022 if (eRes < endRes)
1431    {
1432  0 eRes++;
1433    }
1434   
1435  1022 eRes = Math.min(eRes, aa_annotations.length);
1436   
1437  1022 if (sRes == 0)
1438    {
1439  994 x++;
1440    }
1441   
1442  1022 int y1 = y, y2 = y;
1443  1022 float range = max - min;
1444   
1445    // //Draw origin
1446  1022 if (min < 0)
1447    {
1448  0 y2 = y - (int) ((0 - min / range) * graphHeight);
1449    }
1450   
1451  1022 g.setColor(Color.gray);
1452  1022 drawLine(g, squareStroke, x * charWidth - charWidth, y2,
1453    (eRes - sRes) * charWidth, y2);
1454   
1455  1022 eRes = Math.min(eRes, aa_annotations.length);
1456   
1457  1022 int column;
1458  1022 int aaMax = aa_annotations.length - 1;
1459   
1460  69234 while (x < eRes - sRes)
1461    {
1462  68212 column = sRes + x;
1463  68212 if (hasHiddenColumns)
1464    {
1465  160 column = hiddenColumns.visibleToAbsoluteColumn(column);
1466    }
1467   
1468  68212 if (column > aaMax)
1469    {
1470  0 break;
1471    }
1472   
1473  68212 if (aa_annotations[column] == null)
1474    {
1475  28344 x++;
1476  28344 continue;
1477    }
1478   
1479  39868 if (aa_annotations[column].colour == null)
1480    {
1481  14577 g.setColor(Color.black);
1482    }
1483    else
1484    {
1485  25291 g.setColor(aa_annotations[column].colour);
1486    }
1487   
1488  39868 if (aa_annotations[column - 1] == null
1489    && aa_annotations.length > column + 1
1490    && aa_annotations[column + 1] == null)
1491    {
1492    // standalone value
1493  2206 y1 = y - (int) (((aa_annotations[column].value - min) / range)
1494    * graphHeight);
1495  2206 drawLine(g, x * charWidth + charWidth / 4, y1,
1496    x * charWidth + 3 * charWidth / 4, y1);
1497  2206 x++;
1498  2206 continue;
1499    }
1500   
1501  37662 if (aa_annotations[column - 1] == null)
1502    {
1503  1490 x++;
1504  1490 continue;
1505    }
1506   
1507  36172 y1 = y - (int) (((aa_annotations[column - 1].value - min) / range)
1508    * graphHeight);
1509  36172 y2 = y - (int) (((aa_annotations[column].value - min) / range)
1510    * graphHeight);
1511   
1512  36172 drawLine(g, (x - 1) * charWidth + charWidth / 2, y1,
1513    x * charWidth + charWidth / 2, y2);
1514  36172 x++;
1515    }
1516   
1517  1022 if (_aa.threshold != null)
1518    {
1519  5 g.setColor(_aa.threshold.colour);
1520  5 Graphics2D g2 = (Graphics2D) g;
1521  5 Stroke s = new BasicStroke(1, BasicStroke.CAP_SQUARE,
1522    BasicStroke.JOIN_ROUND, 3f, new float[]
1523    { 5f, 3f }, 0f);
1524   
1525  5 y2 = (int) (y - ((_aa.threshold.value - min) / range) * graphHeight);
1526  5 drawLine(g, s, 0, y2, (eRes - sRes) * charWidth, y2);
1527    }
1528  1022 g2d.setStroke(prevStroke);
1529    }
1530   
 
1531  11267 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  11267 if (sRes > aa_annotations.length)
1538    {
1539  0 return;
1540    }
1541  11267 Font ofont = g.getFont();
1542  11267 eRes = Math.min(eRes, aa_annotations.length);
1543   
1544  11267 int x = 0, y1 = y, y2 = y;
1545   
1546  11267 float range = max - min;
1547   
1548  11267 if (min < 0)
1549    {
1550  0 y2 = y - (int) ((0 - min / (range)) * _aa.graphHeight);
1551    }
1552   
1553  11267 g.setColor(Color.gray);
1554   
1555  11267 drawLine(g, x, y2, (eRes - sRes) * charWidth, y2);
1556   
1557  11267 int column;
1558  11267 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  11267 boolean _aa_is_dssp3 = _aa.autoCalculated && _aa.label
1565    .startsWith(Constants.SECONDARY_STRUCTURE_CONSENSUS_LABEL);
1566   
1567  524801 while (x < eRes - sRes)
1568    {
1569  513577 column = sRes + x;
1570  513577 if (hasHiddenColumns)
1571    {
1572  35610 column = hiddenColumns.visibleToAbsoluteColumn(column);
1573    }
1574   
1575  513577 if (column > aaMax)
1576    {
1577  42 break;
1578    }
1579   
1580  513535 if (aa_annotations[column] == null)
1581    {
1582  3009 x++;
1583  3009 continue;
1584    }
1585  510526 if (aa_annotations[column].colour == null)
1586    {
1587  146796 g.setColor(Color.black);
1588    }
1589    else
1590    {
1591  363730 g.setColor(aa_annotations[column].colour);
1592    }
1593   
1594  510526 y1 = y - (int) (((aa_annotations[column].value - min) / (range))
1595    * _aa.graphHeight);
1596   
1597  510526 if (renderHistogram)
1598    {
1599  509766 if (y1 - y2 > 0)
1600    {
1601  0 fillRect(g, x * charWidth, y2, charWidth, y1 - y2);
1602    }
1603    else
1604    {
1605  509766 fillRect(g, x * charWidth, y1, charWidth, y2 - y1);
1606    }
1607    }
1608    // draw profile if available
1609  510526 if (renderProfile)
1610    {
1611   
1612    /*
1613    * {profile type, #values, total count, char1, pct1, char2, pct2...}
1614    */
1615  36215 int profl[] = getProfileFor(_aa, column);
1616   
1617    // just try to draw the logo if profl is not null
1618  36214 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  28222 boolean isStructureProfile = profl[0] == AlignmentAnnotation.STRUCTURE_PROFILE;
1623  28222 boolean isCdnaProfile = profl[0] == AlignmentAnnotation.CDNA_PROFILE;
1624  28222 float ht = normaliseProfile ? y - _aa.graphHeight : y1;
1625  28222 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  28222 char[] dc = new char[isStructureProfile ? 2
1633  28222 : (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  28222 double asc = fm.getAscent();
1643  28222 double dec = fm.getDescent();
1644  28222 double fht = fm.getHeight();
1645   
1646  28222 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  28222 float ht2 = ht;
1654   
1655    // profl[1] is the number of values in the profile
1656  101123 for (int i = 0, c = 3, last = profl[1]; i < last; i++)
1657    {
1658   
1659  72901 String s;
1660  72901 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  72901 else if (isCdnaProfile)
1668    {
1669  0 CodingUtils.decodeCodon2(profl[c++], dc);
1670  0 s = new String(dc);
1671    }
1672    else
1673    {
1674  72901 dc[0] = (char) profl[c++];
1675  72901 s = new String(dc);
1676    }
1677    // next profl[] position is profile % for the character(s)
1678   
1679  72901 int percent = profl[c++];
1680  72901 if (percent == 0)
1681    {
1682    // failsafe in case a count rounds down to 0%
1683  0 continue;
1684    }
1685  72901 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  72901 Color colour = null;
1692  72901 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  72901 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  72901 colour = profcolour.findColour(dc[0], column, null);
1709    }
1710  72901 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  72901 double sx = 1f * charWidth / fm.charsWidth(dc, 0, dc.length);
1719  72901 double sy = newHeight / asc;
1720  72901 double newAsc = asc * sy;
1721  72901 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  72901 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  72901 final int hght = (int) (ht + (newAsc - newDec));
1755  72901 Font font = ofont
1756    .deriveFont(AffineTransform.getScaleInstance(sx, sy));
1757  72901 g.setFont(font);
1758  72901 g.drawChars(dc, 0, dc.length, x * charWidth, hght);
1759  72901 g.setFont(ofont);
1760   
1761  72901 ht += newHeight;
1762    }
1763    }
1764    }
1765    }
1766  510525 x++;
1767    }
1768  11266 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  68 toggle Color getNotCanonicalColor(char lastss)
1818    {
1819  68 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  68 case '>':
1830  0 case '<':
1831  68 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  4135 toggle private void fillPolygon(Graphics g, int[] xpoints, int[] ypoints, int n)
1945    {
1946  4135 setAntialias(g);
1947  4135 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  514781 toggle private void fillRect(Graphics g, int a, int b, int c, int d)
1957    {
1958  514781 unsetAntialias(g);
1959  514781 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  1558 toggle private void fillArc(Graphics g, int a, int b, int c, int d, int e, int f)
1970    {
1971  1558 setAntialias(g);
1972  1558 g.fillArc(a, b, c, d, e, f);
1973    }
1974   
 
1975  1027 toggle private void drawLine(Graphics g, Stroke s, int a, int b, int c, int d)
1976    {
1977  1027 Graphics2D g2d = (Graphics2D) g;
1978  1027 Stroke p = g2d.getStroke();
1979  1027 g2d.setStroke(s);
1980  1027 drawLine(g, a, b, c, d);
1981  1027 g2d.setStroke(p);
1982    }
1983   
 
1984  50672 toggle private void drawLine(Graphics g, int a, int b, int c, int d)
1985    {
1986  50672 setAntialias(g);
1987  50672 g.drawLine(a, b, c, d);
1988    }
1989   
 
1990  441129 toggle private void setAntialias(Graphics g)
1991    {
1992  441129 if (isVectorRendering())
1993    {
1994    // no need to antialias vector drawings
1995  6650 return;
1996    }
1997  434479 if (Cache.getDefault("ANTI_ALIAS", true))
1998    {
1999  57628 Graphics2D g2d = (Graphics2D) g;
2000  57628 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
2001    RenderingHints.VALUE_ANTIALIAS_ON);
2002    }
2003    }
2004   
 
2005  1316090 toggle private void unsetAntialias(Graphics g)
2006    {
2007  1316089 if (isVectorRendering())
2008    {
2009    // no need to antialias vector drawings
2010  17584 return;
2011    }
2012  1298505 Graphics2D g2d = (Graphics2D) g;
2013  1298505 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  1832265 toggle public boolean isVectorRendering()
2023    {
2024  1832265 return renderingVectors;
2025    }
2026    }