Clover icon

Coverage Report

  1. Project Clover database Thu May 28 2026 15:40:39 BST
  2. Package jalview.renderer

File AnnotationRenderer.java

 

Coverage histogram

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

Code metrics

328
794
26
1
2,035
1,512
431
0.54
30.54
26
16.58

Classes

Class Line # Actions
AnnotationRenderer 66 794 431
0.5984320659.8%
 

Contributing tests

This file is covered by 213 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  536 toggle public AnnotationRenderer()
160    {
161  536 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  536 toggle public AnnotationRenderer(boolean debugRedraw)
172    {
173  536 this.debugRedraw = debugRedraw;
174    }
175   
176    /**
177    * Remove any references and resources when this object is no longer required
178    */
 
179  323 toggle public void dispose()
180    {
181  323 hiddenColumns = null;
182  323 hconsensus = null;
183  323 hSSconsensus = null;
184  323 complementConsensus = null;
185  323 hStrucConsensus = null;
186  323 fadedImage = null;
187  323 annotationPanel = null;
188  323 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  56 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  56 int sCol = (lastSSX / charWidth)
273    + hiddenColumns.visibleToAbsoluteColumn(startRes);
274  56 int x1 = lastSSX;
275  56 int x2 = (x * charWidth);
276   
277  56 String dc = (column == 0 || row_annotations[column - 1] == null) ? ""
278    : row_annotations[column - 1].displayCharacter;
279   
280  56 boolean diffupstream = sCol == 0 || row_annotations[sCol - 1] == null
281    || !dc.equals(row_annotations[sCol - 1].displayCharacter)
282    || !validEnd;
283  56 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  56 if (diffupstream || diffdownstream)
291    {
292    // draw glyphline under arrow
293  56 drawGlyphLine(g, lastSSX, x, y, iconOffset);
294    }
295  56 g.setColor(nonCanColor);
296  56 if (column > 0 && Rna.isClosingParenthesis(dc))
297    {
298   
299  24 if (diffupstream)
300    // if (validRes && column>1 && row_annotations[column-2]!=null &&
301    // dc.equals(row_annotations[column-2].displayCharacter))
302    {
303  24 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  24 x1 += 5;
309    }
310  24 if (diffdownstream)
311    {
312  18 x2 -= 1;
313    }
314    }
315    else
316    {
317   
318    // display a forward arrow
319  32 if (diffdownstream)
320    {
321  32 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  32 x2 -= 5;
327    }
328  32 if (diffupstream)
329    {
330  32 x1 += 1;
331    }
332    }
333    // draw arrow body
334  56 unsetAntialias(g);
335  56 fillRect(g, x1, y + 4 + iconOffset, x2 - x1, 6);
336    }
337   
338    // public void updateFromAnnotationPanel(FontMetrics annotFM, AlignViewportI
339    // av)
 
340  3974 toggle public void updateFromAwtRenderPanel(AwtRenderPanelI annotPanel,
341    AlignViewportI av)
342    {
343  3974 fm = annotPanel.getFontMetrics();
344  3974 annotationPanel = annotPanel;
345  3974 fadedImage = annotPanel.getFadedImage();
346  3974 imgWidth = annotPanel.getFadedImageWidth();
347    // visible area for rendering
348  3974 int[] bounds = annotPanel.getVisibleVRange();
349  3974 if (bounds != null)
350    {
351  3947 sOffset = bounds[0];
352  3947 visHeight = bounds[1];
353  3947 if (visHeight == 0)
354    {
355  0 useClip = false;
356    }
357    else
358    {
359  3947 useClip = canClip;
360    }
361    }
362    else
363    {
364  27 useClip = false;
365    }
366   
367  3974 rendererFactoryI = AnnotationRendererFactory.getRendererFactory();
368  3974 updateFromAlignViewport(av);
369    }
370   
 
371  3974 toggle public void updateFromAlignViewport(AlignViewportI av)
372    {
373  3974 charWidth = av.getCharWidth();
374  3974 endRes = av.getRanges().getEndRes();
375  3974 charHeight = av.getCharHeight();
376  3974 hasHiddenColumns = av.hasHiddenColumns();
377  3974 validCharWidth = av.isValidCharWidth();
378  3974 av_renderHistogram = av.isShowConsensusHistogram();
379  3974 av_renderProfile = av.isShowSequenceLogo();
380  3974 av_normaliseProfile = av.isNormaliseSequenceLogo();
381  3974 profcolour = av.getResidueShading();
382  3974 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  3506 ColourSchemeI col = av.getAlignment().isNucleotide()
390    ? new NucleotideColourScheme()
391    : new ZappoColourScheme();
392  3506 profcolour = new ResidueShader(col);
393    }
394  3974 columnSelection = av.getColumnSelection();
395  3974 hiddenColumns = av.getAlignment().getHiddenColumns();
396  3974 hconsensus = av.getSequenceConsensusHash();
397  3974 hSSconsensus = av.getSequenceSSConsensusHash();
398  3974 complementConsensus = av.getComplementConsensusHash();
399  3974 hStrucConsensus = av.getRnaStructureConsensusHash();
400  3974 av_ignoreGapsConsensus = av.isIgnoreGapsConsensus();
401  3974 av_ignoreBelowBackground = av.isIgnoreBelowBackground();
402  3974 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  45452 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  45452 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  45452 if (aa.autoCalculated
430    && (aa.label.startsWith("Consensus") || aa.label
431    .startsWith("cDNA Consensus")))
432    {
433  45452 boolean forComplement = aa.label.startsWith("cDNA Consensus");
434  45452 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  45452 if (aa.groupRef == null && aa.sequenceRef == null)
445    {
446  33473 if (forComplement)
447    {
448  0 return AAFrequency.extractCdnaProfile(complementConsensus[column],
449    av_ignoreGapsConsensus);
450    }
451    else
452    {
453  33473 return AAFrequency.extractProfile(hconsensus.get(column),
454    av_ignoreGapsConsensus);
455    }
456    }
457    }
458   
459  11979 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  11979 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  11979 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  3974 toggle public boolean drawComponent(AwtRenderPanelI annotPanel,
543    AlignViewportI av, Graphics g, int activeRow, int startRes,
544    int endRes)
545    {
546  3974 if (g instanceof EpsGraphics2D || g instanceof SVGGraphics2D)
547    {
548  14 this.setVectorRendering(true);
549    }
550  3974 Graphics2D g2d = (Graphics2D) g;
551   
552  3974 long stime = System.currentTimeMillis();
553  3974 boolean usedFaded = false;
554    // NOTES:
555    // AnnotationPanel needs to implement: ImageObserver, access to
556    // AlignViewport
557  3974 updateFromAwtRenderPanel(annotPanel, av);
558  3974 fm = g.getFontMetrics();
559  3974 AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation();
560    // int temp = 0;
561  3974 if (aa == null)
562    {
563  0 return false;
564    }
565  3974 int x = 0, y = 0;
566  3974 int column = 0;
567  3974 char lastSS;
568  3974 int lastSSX;
569  3974 int iconOffset = 0;
570  3974 boolean validRes = false;
571  3974 boolean validEnd = false;
572  3974 boolean labelAllCols = false;
573    // boolean centreColLabels;
574    // boolean centreColLabelsDef = av.isCentreColumnLabels();
575  3974 boolean scaleColLabel = false;
576  3974 final AlignmentAnnotation consensusAnnot = av
577    .getAlignmentConsensusAnnotation();
578  3974 final AlignmentAnnotation structConsensusAnnot = av
579    .getAlignmentStrucConsensusAnnotation();
580  3974 final AlignmentAnnotation complementConsensusAnnot = av
581    .getComplementConsensusAnnotation();
582  3974 final List<AlignmentAnnotation> ssConsensusAnnot = av
583    .getAlignmentSecondaryStructureConsensusAnnotation();
584   
585  3974 BitSet graphGroupDrawn = new BitSet();
586  3974 int charOffset = 0; // offset for a label
587    // \u03B2 \u03B1
588    // debug ints
589  3974 int yfrom = 0, f_i = 0, yto = 0, f_to = 0;
590  3974 boolean clipst = false, clipend = false;
591  28099 for (int i = 0; i < aa.length; i++)
592    {
593  24129 AlignmentAnnotation row = aa[i];
594  24129 boolean renderHistogram = true;
595  24129 boolean renderProfile = false;
596  24129 boolean normaliseProfile = false;
597  24129 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  24129 if (row.groupRef != null && (row == row.groupRef.getConsensus()
604    || (row.groupRef.getSSConsensus(null) != null
605    && row.groupRef.getSSConsensus(null).contains(row))))
606    {
607  189 renderHistogram = row.groupRef.isShowConsensusHistogram();
608  189 renderProfile = row.groupRef.isShowSequenceLogo();
609  189 normaliseProfile = row.groupRef.isNormaliseSequenceLogo();
610    }
611  23940 else if (row == consensusAnnot || row == structConsensusAnnot
612    || row == complementConsensusAnnot
613    || (ssConsensusAnnot != null
614    && ssConsensusAnnot.contains(row)))
615    {
616  3980 renderHistogram = av_renderHistogram;
617  3980 renderProfile = av_renderProfile;
618  3980 normaliseProfile = av_normaliseProfile;
619    }
620  19960 else if (InformationThread.HMM_CALC_ID.equals(row.getCalcId()))
621    {
622  18 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  18 renderHistogram = av.isShowInformationHistogram();
631  18 renderProfile = av.isShowHMMSequenceLogo();
632  18 normaliseProfile = av.isNormaliseHMMSequenceLogo();
633    }
634    }
635   
636  24129 Annotation[] row_annotations = row.annotations;
637  24129 if (!row.isForDisplay())
638    {
639  5690 continue;
640    }
641    // centreColLabels = row.centreColLabels || centreColLabelsDef;
642  18439 labelAllCols = row.showAllColLabels;
643  18439 scaleColLabel = row.scaleColLabel;
644  18439 lastSS = ' ';
645  18439 lastSSX = 0;
646   
647  18439 if (!useClip || ((y - charHeight) < visHeight
648    && (y + row.height + charHeight * 2) >= sOffset))
649    {// if_in_visible_region
650  18439 if (!clipst)
651    {
652  3948 clipst = true;
653  3948 yfrom = y;
654  3948 f_i = i;
655    }
656  18439 yto = y;
657  18439 f_to = i;
658  18439 if (row.graph > 0)
659    {
660  15680 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  15680 y += row.height;
667   
668  15680 if (row.hasText)
669    {
670  14609 iconOffset = charHeight - fm.getDescent();
671  14609 y -= charHeight;
672    }
673    }
674  2759 else if (row.hasText)
675    {
676  433 iconOffset = charHeight - fm.getDescent();
677   
678    }
679    else
680    {
681  2326 iconOffset = 0;
682    }
683   
684  18439 if (row.autoCalculated && av.isCalculationInProgress(row))
685    {
686  386 y += charHeight;
687  386 usedFaded = true;
688  386 g.drawImage(fadedImage, 0, y - row.height, imgWidth, y, 0,
689    y - row.height, imgWidth, y, annotationPanel);
690  386 g.setColor(Color.black);
691    // g.drawString("Calculating "+aa[i].label+"....",20, y-row.height/2);
692   
693  386 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  18053 this.glyphLineDrawn = false;
725  18053 x = (startRes == 0) ? 0 : -1;
726  941941 while (x < endRes - startRes)
727    {
728  923953 if (hasHiddenColumns)
729    {
730  55119 column = hiddenColumns.visibleToAbsoluteColumn(startRes + x);
731  55119 if (column > row_annotations.length - 1)
732    {
733  64 break;
734    }
735    }
736    else
737    {
738  868829 column = startRes + x;
739    }
740   
741  923884 if ((row_annotations == null)
742    || (row_annotations.length <= column)
743    || (row_annotations[column] == null))
744    {
745  186969 validRes = false;
746    }
747    else
748    {
749  736914 validRes = true;
750    }
751  923893 final String displayChar = validRes
752    ? row_annotations[column].displayCharacter
753    : null;
754  923888 if (x > -1)
755    {
756  921966 unsetAntialias(g);
757  921967 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  921966 if (row.getInvalidStrucPos() > x)
770    {
771  0 g.setColor(Color.orange);
772  0 fillRect(g, x * charWidth, y, charWidth, charHeight);
773    }
774  921966 else if (row.getInvalidStrucPos() == x)
775    {
776  0 g.setColor(Color.orange.darker());
777  0 fillRect(g, x * charWidth, y, charWidth, charHeight);
778    }
779  921975 if (validCharWidth && validRes && displayChar != null
780    && (displayChar.length() > 0))
781    {
782    // Graphics2D gg = (g);
783  484036 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  484035 boolean scaledToFit = false;
791  484035 float fmScaling = 1f;
792  484035 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  484037 charOffset = (int) ((charWidth - fmWidth) / 2f);
802   
803  484037 if (row_annotations[column].colour == null)
804    {
805  181575 g2d.setColor(Color.black);
806    }
807    else
808    {
809  302461 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  484037 final int xPos = (x * charWidth) + charOffset;
817  484037 final int yPos = y + iconOffset;
818   
819    /*
820    * translate to drawing position _before_ applying any scaling
821    */
822  484036 g2d.translate(xPos, yPos);
823  484036 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  484036 setAntialias(g);
833  484035 if (column == 0 || row.graph > 0)
834    {
835  480298 g2d.drawString(displayChar, 0, 0);
836    }
837  3737 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  3444 g2d.drawString(displayChar, 0, 0);
844    }
845  484035 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  484035 g2d.translate(-xPos, -yPos);
855    }
856    }
857  923888 if (row.hasIcons)
858    {
859  177083 char ss = validRes ? row_annotations[column].secondaryStructure
860    : '-';
861   
862  177083 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  177083 if (ss == '[')
873    {
874  0 if ((displayChar.indexOf(']') > -1))
875    {
876  0 ss = ']';
877   
878    }
879    }
880  177083 if (ss == '{')
881    {
882    // distinguish between forward/backward base-pairing
883  0 if (displayChar.indexOf('}') > -1)
884    {
885  0 ss = '}';
886   
887    }
888    }
889  177083 if (ss == '<')
890    {
891    // distinguish between forward/backward base-pairing
892  144 if (displayChar.indexOf('<') > -1)
893    {
894  144 ss = '>';
895   
896    }
897    }
898  177083 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  177083 if (!validRes || (ss != lastSS))
910    {
911   
912  134908 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  134482 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  1363 case 'H':
929  1363 if (!isRNA)
930    {
931  1363 drawHelixAnnot(g, row_annotations, lastSSX, x, y,
932    iconOffset, startRes, column, validRes,
933    validEnd);
934  1363 break;
935    }
936    // no break if isRNA - falls through to drawNotCanonicalAnnot!
937  6352 case 'E':
938  6352 if (!isRNA)
939    {
940  6352 drawSheetAnnot(g, row_annotations, lastSSX, x, y,
941    iconOffset, startRes, column, validRes,
942    validEnd);
943  6352 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  48 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  48 Color nonCanColor = getNotCanonicalColor(lastSS);
1005  48 drawNotCanonicalAnnot(g, nonCanColor, row_annotations,
1006    lastSSX, x, y, iconOffset, startRes, column,
1007    validRes, validEnd);
1008    // temp = x;
1009  48 break;
1010  126719 default:
1011  126719 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  126719 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1021    }
1022  126719 break;
1023    }
1024    }
1025  134908 if (validRes)
1026    {
1027  11056 lastSS = ss;
1028    }
1029    else
1030    {
1031  123852 lastSS = ' ';
1032    }
1033  134908 if (x > -1)
1034    {
1035  134482 lastSSX = (x * charWidth);
1036    }
1037    }
1038    }
1039  923889 column++;
1040  923889 x++;
1041    }
1042  18052 if (column >= row_annotations.length)
1043    {
1044  6226 column = row_annotations.length - 1;
1045  6226 validEnd = false;
1046    }
1047    else
1048    {
1049  11826 validEnd = true;
1050    }
1051  18052 if ((row_annotations == null) || (row_annotations.length <= column)
1052    || (row_annotations[column] == null))
1053    {
1054  3034 validRes = false;
1055    }
1056    else
1057    {
1058  15018 validRes = true;
1059    }
1060    // x ++;
1061   
1062  18052 if (row.hasIcons)
1063    {
1064  2276 switch (lastSS)
1065    {
1066   
1067  49 case 'H':
1068  49 if (!isRNA)
1069    {
1070  49 drawHelixAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
1071    startRes, column, validRes, validEnd);
1072  49 break;
1073    }
1074    // no break if isRNA - fall through to drawNotCanonicalAnnot!
1075   
1076  444 case 'E':
1077  444 if (!isRNA)
1078    {
1079  444 drawSheetAnnot(g, row_annotations, lastSSX, x, y, iconOffset,
1080    startRes, column, validRes, validEnd);
1081  444 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  8 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  8 Color nonCanColor = getNotCanonicalColor(lastSS);
1148  8 drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX,
1149    x, y, iconOffset, startRes, column, validRes, validEnd);
1150  8 break;
1151  1775 default:
1152  1775 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  1775 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1162    }
1163  1775 break;
1164    }
1165    }
1166   
1167  18052 if (row.graph > 0 && row.graphHeight > 0)
1168    {
1169  15293 if (row.graph == AlignmentAnnotation.LINE_GRAPH)
1170    {
1171  829 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  129 float groupmax = -999999, groupmin = 9999999;
1176  4515 for (int gg = 0; gg < aa.length; gg++)
1177    {
1178  4386 if (aa[gg].graphGroup != row.graphGroup)
1179    {
1180  4128 continue;
1181    }
1182   
1183  258 if (aa[gg] != row)
1184    {
1185  129 aa[gg].visible = false;
1186    }
1187  258 if (aa[gg].graphMax > groupmax)
1188    {
1189  258 groupmax = aa[gg].graphMax;
1190    }
1191  258 if (aa[gg].graphMin < groupmin)
1192    {
1193  129 groupmin = aa[gg].graphMin;
1194    }
1195    }
1196   
1197  4515 for (int gg = 0; gg < aa.length; gg++)
1198    {
1199  4386 if (aa[gg].graphGroup == row.graphGroup)
1200    {
1201  258 drawLineGraph(g, aa[gg], aa[gg].annotations, startRes,
1202    endRes, y, groupmin, groupmax, row.graphHeight);
1203    }
1204    }
1205   
1206  129 graphGroupDrawn.set(row.graphGroup);
1207    }
1208    else
1209    {
1210  700 drawLineGraph(g, row, row_annotations, startRes, endRes, y,
1211    row.graphMin, row.graphMax, row.graphHeight);
1212    }
1213    }
1214  14464 else if (row.graph == AlignmentAnnotation.BAR_GRAPH)
1215    {
1216  14122 drawBarGraph(g, row, row_annotations, startRes, endRes,
1217    row.graphMin, row.graphMax, y, renderHistogram,
1218    renderProfile, normaliseProfile);
1219    }
1220    else
1221    {
1222  342 AnnotationRowRendererI renderer = rendererFactoryI
1223    .getRendererFor(row);
1224  342 if (renderer != null)
1225    {
1226  342 renderer.renderRow(g, charWidth, charHeight, hasHiddenColumns,
1227    av, hiddenColumns, columnSelection, row,
1228    row_annotations, startRes, endRes, row.graphMin,
1229    row.graphMax, y);
1230    }
1231  342 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  18049 if (row.graph > 0 && row.hasText)
1256    {
1257  14219 y += charHeight;
1258    }
1259   
1260  18049 if (row.graph == 0)
1261    {
1262  2759 y += aa[i].height;
1263    }
1264    }
1265  3970 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  3970 ;
1283   
1284  3970 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  136701 toggle void drawGlyphLine(Graphics g, int lastSSX, int x, int y, int iconOffset)
1298    {
1299  136701 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  136701 unsetAntialias(g);
1306  136701 g.setColor(GLYPHLINE_COLOR);
1307  136701 g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2);
1308    }
1309   
 
1310  6796 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  6796 if (!validEnd || !validRes || row == null || row[column] == null
1316    || row[column].secondaryStructure != 'E')
1317    {
1318    // draw the glyphline underneath
1319  6739 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1320   
1321  6739 g.setColor(SHEET_COLOUR);
1322  6739 fillRect(g, lastSSX, y + 4 + iconOffset,
1323    (x * charWidth) - lastSSX - 4, 6);
1324  6739 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  57 g.setColor(SHEET_COLOUR);
1336  57 fillRect(g, lastSSX, y + 4 + iconOffset, (x * charWidth) - lastSSX,
1337    6);
1338    }
1339    }
1340   
 
1341  1412 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  1412 int sCol = (lastSSX / charWidth)
1346    + hiddenColumns.visibleToAbsoluteColumn(startRes);
1347  1412 int x1 = lastSSX;
1348  1412 int x2 = (x * charWidth);
1349   
1350  1412 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  1412 boolean leftEnd = sCol == 0 || row[sCol - 1] == null
1385    || row[sCol - 1].secondaryStructure != 'H';
1386  1412 boolean rightEnd = !validRes || row[column] == null
1387    || row[column].secondaryStructure != 'H';
1388   
1389  1412 if (leftEnd || rightEnd)
1390    {
1391  1412 drawGlyphLine(g, lastSSX, x, y, iconOffset);
1392    }
1393  1412 g.setColor(HELIX_COLOUR);
1394   
1395  1412 if (leftEnd)
1396    {
1397  1412 fillArc(g, lastSSX, y + 3 + iconOffset, charWidth, 8, 90, 180);
1398  1412 x1 += charWidth / 2;
1399    }
1400   
1401  1412 if (rightEnd)
1402    {
1403  1363 fillArc(g, ((x - 1) * charWidth), y + 3 + iconOffset, charWidth, 8,
1404    270, 180);
1405  1363 x2 -= charWidth / 2;
1406    }
1407   
1408  1412 fillRect(g, x1, y + 3 + iconOffset, x2 - x1, 8);
1409    }
1410   
 
1411  958 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  958 if (sRes > aa_annotations.length)
1416    {
1417  9 return;
1418    }
1419  949 Stroke roundStroke = new BasicStroke(1, BasicStroke.CAP_ROUND,
1420    BasicStroke.JOIN_ROUND);
1421  949 Stroke squareStroke = new BasicStroke(1, BasicStroke.CAP_SQUARE,
1422    BasicStroke.JOIN_MITER);
1423  949 Graphics2D g2d = (Graphics2D) g;
1424  949 Stroke prevStroke = g2d.getStroke();
1425  949 g2d.setStroke(roundStroke);
1426   
1427  949 int x = 0;
1428   
1429    // Adjustment for fastpaint to left
1430  949 if (eRes < endRes)
1431    {
1432  0 eRes++;
1433    }
1434   
1435  949 eRes = Math.min(eRes, aa_annotations.length);
1436   
1437  949 if (sRes == 0)
1438    {
1439  931 x++;
1440    }
1441   
1442  949 int y1 = y, y2 = y;
1443  949 float range = max - min;
1444   
1445    // //Draw origin
1446  949 if (min < 0)
1447    {
1448  0 y2 = y - (int) ((0 - min / range) * graphHeight);
1449    }
1450   
1451  949 g.setColor(Color.gray);
1452  949 drawLine(g, squareStroke, x * charWidth - charWidth, y2,
1453    (eRes - sRes) * charWidth, y2);
1454   
1455  949 eRes = Math.min(eRes, aa_annotations.length);
1456   
1457  949 int column;
1458  949 int aaMax = aa_annotations.length - 1;
1459   
1460  63213 while (x < eRes - sRes)
1461    {
1462  62264 column = sRes + x;
1463  62264 if (hasHiddenColumns)
1464    {
1465  576 column = hiddenColumns.visibleToAbsoluteColumn(column);
1466    }
1467   
1468  62264 if (column > aaMax)
1469    {
1470  0 break;
1471    }
1472   
1473  62264 if (aa_annotations[column] == null)
1474    {
1475  24364 x++;
1476  24364 continue;
1477    }
1478   
1479  37900 if (aa_annotations[column].colour == null)
1480    {
1481  12212 g.setColor(Color.black);
1482    }
1483    else
1484    {
1485  25688 g.setColor(aa_annotations[column].colour);
1486    }
1487   
1488  37900 if (aa_annotations[column - 1] == null
1489    && aa_annotations.length > column + 1
1490    && aa_annotations[column + 1] == null)
1491    {
1492    // standalone value
1493  1710 y1 = y - (int) (((aa_annotations[column].value - min) / range)
1494    * graphHeight);
1495  1710 drawLine(g, x * charWidth + charWidth / 4, y1,
1496    x * charWidth + 3 * charWidth / 4, y1);
1497  1710 x++;
1498  1710 continue;
1499    }
1500   
1501  36190 if (aa_annotations[column - 1] == null)
1502    {
1503  1606 x++;
1504  1606 continue;
1505    }
1506   
1507  34584 y1 = y - (int) (((aa_annotations[column - 1].value - min) / range)
1508    * graphHeight);
1509  34584 y2 = y - (int) (((aa_annotations[column].value - min) / range)
1510    * graphHeight);
1511   
1512  34584 drawLine(g, (x - 1) * charWidth + charWidth / 2, y1,
1513    x * charWidth + charWidth / 2, y2);
1514  34584 x++;
1515    }
1516   
1517  949 if (_aa.threshold != null)
1518    {
1519  4 g.setColor(_aa.threshold.colour);
1520  4 Graphics2D g2 = (Graphics2D) g;
1521  4 Stroke s = new BasicStroke(1, BasicStroke.CAP_SQUARE,
1522    BasicStroke.JOIN_ROUND, 3f, new float[]
1523    { 5f, 3f }, 0f);
1524   
1525  4 y2 = (int) (y - ((_aa.threshold.value - min) / range) * graphHeight);
1526  4 drawLine(g, s, 0, y2, (eRes - sRes) * charWidth, y2);
1527    }
1528  949 g2d.setStroke(prevStroke);
1529    }
1530   
 
1531  14122 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  14122 if (sRes > aa_annotations.length)
1538    {
1539  6 return;
1540    }
1541  14116 Font ofont = g.getFont();
1542  14116 eRes = Math.min(eRes, aa_annotations.length);
1543   
1544  14116 boolean logIt = true;
1545  14116 int x = 0, y1 = y, y2 = y;
1546   
1547  14116 float range = max - min;
1548  14116 if (logIt)
1549    {
1550  14116 range = (float) Math.log1p(range);
1551    }
1552   
1553  14116 if (min < 0)
1554    {
1555  0 y2 = y - (int) ((0 - min / (range)) * _aa.graphHeight);
1556    }
1557   
1558  14116 g.setColor(Color.gray);
1559   
1560  14116 drawLine(g, x, y2, (eRes - sRes) * charWidth, y2);
1561   
1562  14116 int column;
1563  14116 int aaMax = aa_annotations.length - 1;
1564   
1565    /**
1566    * TODO Should really set this using a constant local to AlignmentAnnotation
1567    * in the same way as the cDNA and rna structure profiles are typed
1568    */
1569  14116 boolean _aa_is_dssp3 = _aa.autoCalculated && _aa.label
1570    .startsWith(Constants.SECONDARY_STRUCTURE_CONSENSUS_LABEL);
1571   
1572  659588 while (x < eRes - sRes)
1573    {
1574  645534 column = sRes + x;
1575  645532 if (hasHiddenColumns)
1576    {
1577  54169 column = hiddenColumns.visibleToAbsoluteColumn(column);
1578    }
1579   
1580  645538 if (column > aaMax)
1581    {
1582  60 break;
1583    }
1584   
1585  645479 if (aa_annotations[column] == null)
1586    {
1587  3853 x++;
1588  3853 continue;
1589    }
1590  641626 if (aa_annotations[column].colour == null)
1591    {
1592  180745 g.setColor(Color.black);
1593    }
1594    else
1595    {
1596  460882 g.setColor(aa_annotations[column].colour);
1597    }
1598   
1599  641624 if (logIt) {
1600  641627 y1 = y - (int) ((Math.log1p(aa_annotations[column].value - min) / (range))
1601    * _aa.graphHeight);
1602    } else {
1603  0 y1 = y - (int) (((aa_annotations[column].value - min) / (range))
1604    * _aa.graphHeight);
1605    }
1606  641626 if (renderHistogram)
1607    {
1608  640873 if (y1 - y2 > 0)
1609    {
1610  0 fillRect(g, x * charWidth, y2, charWidth, y1 - y2);
1611    }
1612    else
1613    {
1614  640873 fillRect(g, x * charWidth, y1, charWidth, y2 - y1);
1615    }
1616    }
1617    // draw profile if available
1618  641621 if (renderProfile)
1619    {
1620   
1621    /*
1622    * {profile type, #values, total count, char1, pct1, char2, pct2...}
1623    */
1624  45452 int profl[] = getProfileFor(_aa, column);
1625   
1626    // just try to draw the logo if profl is not null
1627  45451 if (profl != null && profl[2] != 0)
1628    {
1629    // TODO do we ever have different profiles for different columns ?
1630    // Move up to where we set _aa_is_dssp3
1631  33472 boolean isStructureProfile = profl[0] == AlignmentAnnotation.STRUCTURE_PROFILE;
1632  33472 boolean isCdnaProfile = profl[0] == AlignmentAnnotation.CDNA_PROFILE;
1633  33472 float ht = normaliseProfile ? y - _aa.graphHeight : y1;
1634  33472 final double normaliseFactor = normaliseProfile ? _aa.graphHeight
1635    : (y2 - y1);
1636   
1637    /**
1638    * Render a single base for a sequence profile, a base pair for
1639    * structure profile, and a triplet for a cdna profile
1640    */
1641  33472 char[] dc = new char[isStructureProfile ? 2
1642  33472 : (isCdnaProfile ? 3 : 1)];
1643   
1644    // lm is not necessary - we can just use fm - could be off by no more
1645    // than 0.5 px
1646    // LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g);
1647    // Console.info(asc + " " + dec + " " + (asc -
1648    // lm.getAscent())
1649    // + " " + (dec - lm.getDescent()));
1650   
1651  33472 double asc = fm.getAscent();
1652  33472 double dec = fm.getDescent();
1653  33472 double fht = fm.getHeight();
1654   
1655  33472 double scale = 1f / (normaliseProfile ? profl[2] : 100f);
1656    // float ofontHeight = 1f / fm.getAscent();// magnify to fill box
1657   
1658    /*
1659    * Traverse the character(s)/percentage data in the array
1660    */
1661   
1662  33472 float ht2 = ht;
1663   
1664    // profl[1] is the number of values in the profile
1665  118916 for (int i = 0, c = 3, last = profl[1]; i < last; i++)
1666    {
1667   
1668  85445 String s;
1669  85445 if (isStructureProfile)
1670    {
1671    // todo can we encode a structure pair as an int, like codons?
1672  0 dc[0] = (char) profl[c++];
1673  0 dc[1] = (char) profl[c++];
1674  0 s = new String(dc);
1675    }
1676  85445 else if (isCdnaProfile)
1677    {
1678  0 CodingUtils.decodeCodon2(profl[c++], dc);
1679  0 s = new String(dc);
1680    }
1681    else
1682    {
1683  85445 dc[0] = (char) profl[c++];
1684  85445 s = new String(dc);
1685    }
1686    // next profl[] position is profile % for the character(s)
1687   
1688  85445 int percent = profl[c++];
1689  85445 if (percent == 0)
1690    {
1691    // failsafe in case a count rounds down to 0%
1692  0 continue;
1693    }
1694  85445 double newHeight = normaliseFactor * scale * percent;
1695   
1696    /*
1697    * Set character colour as per alignment colour scheme; use the
1698    * codon translation if a cDNA profile
1699    */
1700  85445 Color colour = null;
1701  85445 if (isCdnaProfile)
1702    {
1703  0 final String codonTranslation = ResidueProperties
1704    .codonTranslate(s);
1705  0 colour = profcolour.findColour(codonTranslation.charAt(0),
1706    column, null);
1707    }
1708  85445 else if (_aa_is_dssp3)
1709    {
1710    // TODO - would be nice to get rid of the special 'findSSColour'
1711    // here ?
1712  0 colour = profcolour.findSSColour(dc[0], column);
1713    }
1714    else
1715    {
1716   
1717  85445 colour = profcolour.findColour(dc[0], column, null);
1718    }
1719  85445 g.setColor(colour == Color.white ? Color.lightGray : colour);
1720   
1721    // Debug - render boxes around characters
1722    // g.setColor(Color.red);
1723    // g.drawRect(x*av.charWidth, (int)ht, av.charWidth,
1724    // (int)(scl));
1725    // g.setColor(profcolour.findColour(dc[0]).darker());
1726   
1727  85445 double sx = 1f * charWidth / fm.charsWidth(dc, 0, dc.length);
1728  85445 double sy = newHeight / asc;
1729  85445 double newAsc = asc * sy;
1730  85445 double newDec = dec * sy;
1731    // it is not necessary to recalculate lm for the new font.
1732    // note: lm.getBaselineOffsets()[lm.getBaselineIndex()]) must be 0
1733    // by definition. Was:
1734    // int hght = (int) (ht + (newAsc - newDec);
1735    // - lm.getBaselineOffsets()[lm.getBaselineIndex()]));
1736   
1737  85445 if (Platform.isJS())
1738    {
1739    /*
1740    * SwingJS does not implement font.deriveFont()
1741    * so use a scaling transform to draw instead,
1742    * this is off by a very small amount
1743    */
1744  0 final int hght = (int) (ht2 + (newAsc - newDec));
1745  0 Graphics2D gg = (Graphics2D) g;
1746  0 int xShift = (int) Math.round(x * charWidth / sx);
1747  0 int yShift = (int) Math.round(hght / sy);
1748  0 gg.transform(AffineTransform.getScaleInstance(sx, sy));
1749  0 gg.drawString(s, xShift, yShift);
1750  0 gg.transform(
1751    AffineTransform.getScaleInstance(1D / sx, 1D / sy));
1752  0 ht2 += newHeight;
1753    }
1754    else
1755    /**
1756    * Java only
1757    *
1758    * @j2sIgnore
1759    */
1760    {
1761    // Java ('normal') method is to scale the font to fit
1762   
1763  85445 final int hght = (int) (ht + (newAsc - newDec));
1764  85445 Font font = ofont
1765    .deriveFont(AffineTransform.getScaleInstance(sx, sy));
1766  85445 g.setFont(font);
1767  85445 g.drawChars(dc, 0, dc.length, x * charWidth, hght);
1768  85444 g.setFont(ofont);
1769   
1770  85444 ht += newHeight;
1771    }
1772    }
1773    }
1774    }
1775  641617 x++;
1776    }
1777  14114 if (_aa.threshold != null)
1778    {
1779  0 g.setColor(_aa.threshold.colour);
1780  0 Stroke s = new BasicStroke(1, BasicStroke.CAP_SQUARE,
1781    BasicStroke.JOIN_ROUND, 3f, new float[]
1782    { 5f, 3f }, 0f);
1783   
1784  0 y2 = (int) (y
1785    - ((_aa.threshold.value - min) / range) * _aa.graphHeight);
1786  0 drawLine(g, s, 0, y2, (eRes - sRes) * charWidth, y2);
1787    }
1788    }
1789   
1790    // used by overview window
 
1791  0 toggle public void drawGraph(Graphics g, AlignmentAnnotation _aa,
1792    Annotation[] aa_annotations, int width, int y, int sRes, int eRes)
1793    {
1794  0 eRes = Math.min(eRes, aa_annotations.length);
1795  0 g.setColor(Color.white);
1796  0 fillRect(g, 0, 0, width, y);
1797  0 g.setColor(new Color(0, 0, 180));
1798   
1799  0 int x = 0, height;
1800   
1801  0 for (int j = sRes; j < eRes; j++)
1802    {
1803  0 if (aa_annotations[j] != null)
1804    {
1805  0 if (aa_annotations[j].colour == null)
1806    {
1807  0 g.setColor(Color.black);
1808    }
1809    else
1810    {
1811  0 g.setColor(aa_annotations[j].colour);
1812    }
1813   
1814  0 height = (int) ((aa_annotations[j].value / _aa.graphMax) * y);
1815  0 if (height > y)
1816    {
1817  0 height = y;
1818    }
1819   
1820  0 fillRect(g, x, y - height, charWidth, height);
1821    }
1822  0 x += charWidth;
1823    }
1824    }
1825   
 
1826  56 toggle Color getNotCanonicalColor(char lastss)
1827    {
1828  56 switch (lastss)
1829    {
1830  0 case '{':
1831  0 case '}':
1832  0 return new Color(255, 125, 5);
1833   
1834  0 case '[':
1835  0 case ']':
1836  0 return new Color(245, 115, 10);
1837   
1838  56 case '>':
1839  0 case '<':
1840  56 return new Color(235, 135, 15);
1841   
1842  0 case 'A':
1843  0 case 'a':
1844  0 return new Color(225, 105, 20);
1845   
1846  0 case 'B':
1847  0 case 'b':
1848  0 return new Color(215, 145, 30);
1849   
1850  0 case 'C':
1851  0 case 'c':
1852  0 return new Color(205, 95, 35);
1853   
1854  0 case 'D':
1855  0 case 'd':
1856  0 return new Color(195, 155, 45);
1857   
1858  0 case 'E':
1859  0 case 'e':
1860  0 return new Color(185, 85, 55);
1861   
1862  0 case 'F':
1863  0 case 'f':
1864  0 return new Color(175, 165, 65);
1865   
1866  0 case 'G':
1867  0 case 'g':
1868  0 return new Color(170, 75, 75);
1869   
1870  0 case 'H':
1871  0 case 'h':
1872  0 return new Color(160, 175, 85);
1873   
1874  0 case 'I':
1875  0 case 'i':
1876  0 return new Color(150, 65, 95);
1877   
1878  0 case 'J':
1879  0 case 'j':
1880  0 return new Color(140, 185, 105);
1881   
1882  0 case 'K':
1883  0 case 'k':
1884  0 return new Color(130, 55, 110);
1885   
1886  0 case 'L':
1887  0 case 'l':
1888  0 return new Color(120, 195, 120);
1889   
1890  0 case 'M':
1891  0 case 'm':
1892  0 return new Color(110, 45, 130);
1893   
1894  0 case 'N':
1895  0 case 'n':
1896  0 return new Color(100, 205, 140);
1897   
1898  0 case 'O':
1899  0 case 'o':
1900  0 return new Color(90, 35, 150);
1901   
1902  0 case 'P':
1903  0 case 'p':
1904  0 return new Color(85, 215, 160);
1905   
1906  0 case 'Q':
1907  0 case 'q':
1908  0 return new Color(75, 25, 170);
1909   
1910  0 case 'R':
1911  0 case 'r':
1912  0 return new Color(65, 225, 180);
1913   
1914  0 case 'S':
1915  0 case 's':
1916  0 return new Color(55, 15, 185);
1917   
1918  0 case 'T':
1919  0 case 't':
1920  0 return new Color(45, 235, 195);
1921   
1922  0 case 'U':
1923  0 case 'u':
1924  0 return new Color(35, 5, 205);
1925   
1926  0 case 'V':
1927  0 case 'v':
1928  0 return new Color(25, 245, 215);
1929   
1930  0 case 'W':
1931  0 case 'w':
1932  0 return new Color(15, 0, 225);
1933   
1934  0 case 'X':
1935  0 case 'x':
1936  0 return new Color(10, 255, 235);
1937   
1938  0 case 'Y':
1939  0 case 'y':
1940  0 return new Color(5, 150, 245);
1941   
1942  0 case 'Z':
1943  0 case 'z':
1944  0 return new Color(0, 80, 255);
1945   
1946  0 default:
1947  0 Console.info("This is not a interaction : " + lastss);
1948  0 return null;
1949   
1950    }
1951    }
1952   
 
1953  6795 toggle private void fillPolygon(Graphics g, int[] xpoints, int[] ypoints, int n)
1954    {
1955  6795 setAntialias(g);
1956  6795 g.fillPolygon(xpoints, ypoints, n);
1957    }
1958   
1959    /*
1960    private void fillRect(Graphics g, int a, int b, int c, int d)
1961    {
1962    fillRect(g, false, a, b, c, d);
1963    }*/
1964   
 
1965  649136 toggle private void fillRect(Graphics g, int a, int b, int c, int d)
1966    {
1967  649136 unsetAntialias(g);
1968  649136 g.fillRect(a, b, c, d);
1969    }
1970   
 
1971  0 toggle private void fillRoundRect(Graphics g, int a, int b, int c, int d, int e,
1972    int f)
1973    {
1974  0 setAntialias(g);
1975  0 g.fillRoundRect(a, b, c, d, e, f);
1976    }
1977   
 
1978  2775 toggle private void fillArc(Graphics g, int a, int b, int c, int d, int e, int f)
1979    {
1980  2775 setAntialias(g);
1981  2775 g.fillArc(a, b, c, d, e, f);
1982    }
1983   
 
1984  953 toggle private void drawLine(Graphics g, Stroke s, int a, int b, int c, int d)
1985    {
1986  953 Graphics2D g2d = (Graphics2D) g;
1987  953 Stroke p = g2d.getStroke();
1988  953 g2d.setStroke(s);
1989  953 drawLine(g, a, b, c, d);
1990  953 g2d.setStroke(p);
1991    }
1992   
 
1993  51363 toggle private void drawLine(Graphics g, int a, int b, int c, int d)
1994    {
1995  51363 setAntialias(g);
1996  51363 g.drawLine(a, b, c, d);
1997    }
1998   
 
1999  545000 toggle private void setAntialias(Graphics g)
2000    {
2001  545001 if (isVectorRendering())
2002    {
2003    // no need to antialias vector drawings
2004  6650 return;
2005    }
2006  538350 if (Cache.getDefault("ANTI_ALIAS", true))
2007    {
2008  58771 Graphics2D g2d = (Graphics2D) g;
2009  58771 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
2010    RenderingHints.VALUE_ANTIALIAS_ON);
2011    }
2012    }
2013   
 
2014  1707880 toggle private void unsetAntialias(Graphics g)
2015    {
2016  1707880 if (isVectorRendering())
2017    {
2018    // no need to antialias vector drawings
2019  17584 return;
2020    }
2021  1690301 Graphics2D g2d = (Graphics2D) g;
2022  1690301 g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
2023    RenderingHints.VALUE_ANTIALIAS_OFF);
2024    }
2025   
 
2026  14 toggle public void setVectorRendering(boolean b)
2027    {
2028  14 renderingVectors = b;
2029    }
2030   
 
2031  2382768 toggle public boolean isVectorRendering()
2032    {
2033  2382768 return renderingVectors;
2034    }
2035    }