Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.renderer

File AnnotationRenderer.java

 

Coverage histogram

../../img/srcFileCovDistChart5.png
39% of files have more coverage

Code metrics

276
685
16
1
1,702
1,250
385
0.56
42.81
16
24.06

Classes

Class Line # Actions
AnnotationRenderer 51 685 385
0.4779938547.8%
 

Contributing tests

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