Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.renderer

File AnnotationRenderer.java

 

Coverage histogram

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

Code metrics

274
689
16
1
1,699
1,260
384
0.56
43.06
16
24

Classes

Class Line # Actions
AnnotationRenderer 52 689 384 514
0.4749744547.5%
 

Contributing tests

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