X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Frenderer%2FAnnotationRenderer.java;h=70991c6c1e365f5e7a2e46cec342f0400cc2a5b8;hb=c794c5033adeee182b03a5ea92c0a7495a29661f;hp=40fb513b158e3a5a2ec77730f56463bef87f48f4;hpb=3609d4b908fa64cab35f2348401baab3347188fc;p=jalview.git diff --git a/src/jalview/renderer/AnnotationRenderer.java b/src/jalview/renderer/AnnotationRenderer.java index 40fb513..70991c6 100644 --- a/src/jalview/renderer/AnnotationRenderer.java +++ b/src/jalview/renderer/AnnotationRenderer.java @@ -45,7 +45,6 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; -import java.awt.font.LineMetrics; import java.awt.geom.AffineTransform; import java.awt.image.ImageObserver; import java.util.BitSet; @@ -70,7 +69,7 @@ public class AnnotationRenderer private FontMetrics fm; - private final boolean MAC = Platform.isAMac(); + private final boolean USE_FILL_ROUND_RECT = Platform.isAMacAndNotJS(); // todo remove these flags, read from group/viewport where needed boolean av_renderHistogram = true; @@ -89,9 +88,9 @@ public class AnnotationRenderer private ProfilesI hconsensus; - private Hashtable[] complementConsensus; + private Hashtable[] complementConsensus; - private Hashtable[] hStrucConsensus; + private Hashtable[] hStrucConsensus; private boolean av_ignoreGapsConsensus; @@ -439,8 +438,6 @@ public class AnnotationRenderer return null; } - boolean rna = false; - /** * Render the annotation rows associated with an alignment. * @@ -471,7 +468,7 @@ public class AnnotationRenderer updateFromAwtRenderPanel(annotPanel, av); fm = g.getFontMetrics(); AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation(); - int temp = 0; + // int temp = 0; if (aa == null) { return false; @@ -484,8 +481,8 @@ public class AnnotationRenderer boolean validRes = false; boolean validEnd = false; boolean labelAllCols = false; - boolean centreColLabels; - boolean centreColLabelsDef = av.isCentreColumnLabels(); +// boolean centreColLabels; +// boolean centreColLabelsDef = av.isCentreColumnLabels(); boolean scaleColLabel = false; final AlignmentAnnotation consensusAnnot = av .getAlignmentConsensusAnnotation(); @@ -493,14 +490,9 @@ public class AnnotationRenderer .getAlignmentStrucConsensusAnnotation(); final AlignmentAnnotation complementConsensusAnnot = av .getComplementConsensusAnnotation(); - boolean renderHistogram = true, renderProfile = true, - normaliseProfile = false, isRNA = rna; BitSet graphGroupDrawn = new BitSet(); int charOffset = 0; // offset for a label - float fmWidth, fmScaling = 1f; // scaling for a label to fit it into a - // column. - Font ofont = g.getFont(); // \u03B2 \u03B1 // debug ints int yfrom = 0, f_i = 0, yto = 0, f_to = 0; @@ -508,53 +500,57 @@ public class AnnotationRenderer for (int i = 0; i < aa.length; i++) { AlignmentAnnotation row = aa[i]; - isRNA = row.isRNA(); + boolean renderHistogram = true; + boolean renderProfile = false; + boolean normaliseProfile = false; + boolean isRNA = row.isRNA(); + + // check if this is a consensus annotation row and set the display + // settings appropriately + // TODO: generalise this to have render styles for consensus/profile + // data + if (row.groupRef != null && row == row.groupRef.getConsensus()) { - // check if this is a consensus annotation row and set the display - // settings appropriately - // TODO: generalise this to have render styles for consensus/profile - // data - if (row.groupRef != null && row == row.groupRef.getConsensus()) - { - renderHistogram = row.groupRef.isShowConsensusHistogram(); - renderProfile = row.groupRef.isShowSequenceLogo(); - normaliseProfile = row.groupRef.isNormaliseSequenceLogo(); - } - else if (row == consensusAnnot || row == structConsensusAnnot - || row == complementConsensusAnnot) - { - renderHistogram = av_renderHistogram; - renderProfile = av_renderProfile; - normaliseProfile = av_normaliseProfile; - } - else if (InformationThread.HMM_CALC_ID.equals(row.getCalcId())) + renderHistogram = row.groupRef.isShowConsensusHistogram(); + renderProfile = row.groupRef.isShowSequenceLogo(); + normaliseProfile = row.groupRef.isNormaliseSequenceLogo(); + } + else if (row == consensusAnnot || row == structConsensusAnnot + || row == complementConsensusAnnot) + { + renderHistogram = av_renderHistogram; + renderProfile = av_renderProfile; + normaliseProfile = av_normaliseProfile; + } + else if (InformationThread.HMM_CALC_ID.equals(row.getCalcId())) + { + if (row.groupRef != null) { - if (row.groupRef != null) - { - renderHistogram = row.groupRef.isShowInformationHistogram(); - renderProfile = row.groupRef.isShowHMMSequenceLogo(); - normaliseProfile = row.groupRef.isNormaliseHMMSequenceLogo(); - } - else - { - renderHistogram = av.isShowInformationHistogram(); - renderProfile = av.isShowHMMSequenceLogo(); - normaliseProfile = av.isNormaliseHMMSequenceLogo(); - } + renderHistogram = row.groupRef.isShowInformationHistogram(); + renderProfile = row.groupRef.isShowHMMSequenceLogo(); + normaliseProfile = row.groupRef.isNormaliseHMMSequenceLogo(); } else { - renderHistogram = true; - // don't need to set render/normaliseProfile since they are not - // currently used in any other annotation track renderer + renderHistogram = av.isShowInformationHistogram(); + renderProfile = av.isShowHMMSequenceLogo(); + normaliseProfile = av.isNormaliseHMMSequenceLogo(); } } + else if (row == consensusAnnot || row == structConsensusAnnot + || row == complementConsensusAnnot) + { + renderHistogram = av_renderHistogram; + renderProfile = av_renderProfile; + normaliseProfile = av_normaliseProfile; + } + Annotation[] row_annotations = row.annotations; if (!row.visible) { continue; } - centreColLabels = row.centreColLabels || centreColLabelsDef; +// centreColLabels = row.centreColLabels || centreColLabelsDef; labelAllCols = row.showAllColLabels; scaleColLabel = row.scaleColLabel; lastSS = ' '; @@ -690,49 +686,59 @@ public class AnnotationRenderer if (validCharWidth && validRes && displayChar != null && (displayChar.length() > 0)) { - - fmWidth = fm.charsWidth(displayChar.toCharArray(), 0, + Graphics2D gg = ((Graphics2D) g); + float fmWidth = fm.charsWidth(displayChar.toCharArray(), 0, displayChar.length()); - if (/* centreColLabels || */scaleColLabel) + + /* + * shrink label width to fit in column, if that is + * both configured and necessary + */ + boolean scaledToFit = false; + float fmScaling = 1f; + if (scaleColLabel && fmWidth > charWidth) { - // fmWidth = fm.charsWidth(displayChar.toCharArray(), 0, - // displayChar.length()); - // - // if (scaleColLabel) - // { - // justify the label and scale to fit in column - if (fmWidth > charWidth) - { - // scale only if the current font isn't already small enough - fmScaling = charWidth; - fmScaling /= fmWidth; - g.setFont(ofont.deriveFont(AffineTransform - .getScaleInstance(fmScaling, 1.0))); - // and update the label's width to reflect the scaling. - fmWidth = charWidth; - } - // } + scaledToFit = true; + fmScaling = charWidth; + fmScaling /= fmWidth; + // and update the label's width to reflect the scaling. + fmWidth = charWidth; } - // TODO is it ok to use width of / show all characters here? - // else - // { - // fmWidth = fm.charWidth(displayChar.charAt(0)); - // } + charOffset = (int) ((charWidth - fmWidth) / 2f); if (row_annotations[column].colour == null) { - g.setColor(Color.black); + gg.setColor(Color.black); } else { - g.setColor(row_annotations[column].colour); + gg.setColor(row_annotations[column].colour); } + /* + * draw the label, unless it is the same secondary structure + * symbol (excluding RNA Helix) as the previous column + */ + final int xPos = (x * charWidth) + charOffset; + final int yPos = y + iconOffset; + + /* + * translate to drawing position _before_ applying any scaling + */ + gg.translate(xPos, yPos); + if (scaledToFit) + { + /* + * use a scaling transform to make the label narrower + * (JalviewJS doesn't have Font.deriveFont(AffineTransform)) + */ + gg.transform( + AffineTransform.getScaleInstance(fmScaling, 1.0)); + } if (column == 0 || row.graph > 0) { - g.drawString(displayChar, (x * charWidth) + charOffset, - y + iconOffset); + gg.drawString(displayChar, 0, 0); } else if (row_annotations[column - 1] == null || (labelAllCols || !displayChar.equals( @@ -740,10 +746,18 @@ public class AnnotationRenderer || (displayChar.length() < 2 && row_annotations[column].secondaryStructure == ' '))) { - g.drawString(displayChar, x * charWidth + charOffset, - y + iconOffset); + gg.drawString(displayChar, 0, 0); + } + if (scaledToFit) + { + /* + * undo scaling before translating back + * (restoring saved transform does NOT work in JS PDFGraphics!) + */ + gg.transform(AffineTransform + .getScaleInstance(1D / fmScaling, 1.0)); } - g.setFont(ofont); + gg.translate(-xPos, -yPos); } } if (row.hasIcons) @@ -804,7 +818,7 @@ public class AnnotationRenderer if (x > -1) { - int nb_annot = x - temp; + // int nb_annot = x - temp; // System.out.println("\t type :"+lastSS+"\t x :"+x+"\t nbre // annot :"+nb_annot); switch (lastSS) @@ -813,7 +827,7 @@ public class AnnotationRenderer case ')': // and opposite direction drawStemAnnot(g, row_annotations, lastSSX, x, y, iconOffset, startRes, column, validRes, validEnd); - temp = x; + // temp = x; break; case 'H': @@ -896,13 +910,13 @@ public class AnnotationRenderer drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX, x, y, iconOffset, startRes, column, validRes, validEnd); - temp = x; + // temp = x; break; default: g.setColor(Color.gray); g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2); - temp = x; + // temp = x; break; } } @@ -1143,7 +1157,7 @@ public class AnnotationRenderer public static final Color STEM_COLOUR = Color.blue; - private Color sdNOTCANONICAL_COLOUR; + // private Color sdNOTCANONICAL_COLOUR; void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX, int x, int y, int iconOffset, int startRes, int column, boolean validRes, @@ -1191,7 +1205,7 @@ public class AnnotationRenderer int x1 = lastSSX; int x2 = (x * charWidth); - if (MAC) + if (USE_FILL_ROUND_RECT) { int ofs = charWidth / 2; // Off by 1 offset when drawing rects and ovals @@ -1335,6 +1349,7 @@ public class AnnotationRenderer } } + @SuppressWarnings("unused") void drawBarGraph(Graphics g, AlignmentAnnotation _aa, Annotation[] aa_annotations, int sRes, int eRes, float min, float max, int y, boolean renderHistogram, boolean renderProfile, @@ -1418,64 +1433,66 @@ public class AnnotationRenderer boolean isStructureProfile = profl[0] == AlignmentAnnotation.STRUCTURE_PROFILE; boolean isCdnaProfile = profl[0] == AlignmentAnnotation.CDNA_PROFILE; float ht = normaliseProfile ? y - _aa.graphHeight : y1; - double htn = normaliseProfile ? _aa.graphHeight : (y2 - y1);// aa.graphHeight; - double hght; - float wdth; - double ht2 = 0; - char[] dc; + final double normaliseFactor = normaliseProfile ? _aa.graphHeight + : (y2 - y1); /** * Render a single base for a sequence profile, a base pair for * structure profile, and a triplet for a cdna profile */ - dc = new char[isStructureProfile ? 2 : (isCdnaProfile ? 3 : 1)]; + char[] dc = new char[isStructureProfile ? 2 + : (isCdnaProfile ? 3 : 1)]; + + // lm is not necessary - we can just use fm - could be off by no more + // than 0.5 px + // LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g); + // System.out.println(asc + " " + dec + " " + (asc - lm.getAscent()) + // + " " + (dec - lm.getDescent())); + + double asc = fm.getAscent(); + double dec = fm.getDescent(); + double fht = fm.getHeight(); - LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g); double scale = 1f / (normaliseProfile ? profl[2] : 100f); - float ofontHeight = 1f / lm.getAscent();// magnify to fill box - double scl = 0.0; + // float ofontHeight = 1f / fm.getAscent();// magnify to fill box /* * Traverse the character(s)/percentage data in the array */ - int c = 3; - int valuesProcessed = 0; + + float ht2 = ht; + // profl[1] is the number of values in the profile - while (valuesProcessed < profl[1]) + for (int i = 0, c = 3, last = profl[1]; i < last; i++) { + + String s; if (isStructureProfile) { // todo can we encode a structure pair as an int, like codons? dc[0] = (char) profl[c++]; dc[1] = (char) profl[c++]; + s = new String(dc); } else if (isCdnaProfile) { - dc = CodingUtils.decodeCodon(profl[c++]); + CodingUtils.decodeCodon2(profl[c++], dc); + s = new String(dc); } else { dc[0] = (char) profl[c++]; + s = new String(dc); } - - wdth = charWidth; - wdth /= fm.charsWidth(dc, 0, dc.length); - - ht += scl; // next profl[] position is profile % for the character(s) - scl = htn * scale * profl[c++]; - lm = ofont.getLineMetrics(dc, 0, 1, - g.getFontMetrics().getFontRenderContext()); - Font font = ofont.deriveFont(AffineTransform - .getScaleInstance(wdth, scl / lm.getAscent())); - g.setFont(font); - lm = g.getFontMetrics().getLineMetrics(dc, 0, 1, g); - // Debug - render boxes around characters - // g.setColor(Color.red); - // g.drawRect(x*av.charWidth, (int)ht, av.charWidth, - // (int)(scl)); - // g.setColor(profcolour.findColour(dc[0]).darker()); + int percent = profl[c++]; + if (percent == 0) + { + // failsafe in case a count rounds down to 0% + continue; + } + double newHeight = normaliseFactor * scale * percent; /* * Set character colour as per alignment colour scheme; use the @@ -1485,7 +1502,7 @@ public class AnnotationRenderer if (isCdnaProfile) { final String codonTranslation = ResidueProperties - .codonTranslate(new String(dc)); + .codonTranslate(s); colour = profcolour.findColour(codonTranslation.charAt(0), column, null); } @@ -1495,13 +1512,56 @@ public class AnnotationRenderer } g.setColor(colour == Color.white ? Color.lightGray : colour); - hght = (ht + (scl - lm.getDescent() - - lm.getBaselineOffsets()[lm.getBaselineIndex()])); + // Debug - render boxes around characters + // g.setColor(Color.red); + // g.drawRect(x*av.charWidth, (int)ht, av.charWidth, + // (int)(scl)); + // g.setColor(profcolour.findColour(dc[0]).darker()); - g.drawChars(dc, 0, dc.length, x * charWidth, (int) hght); - valuesProcessed++; + double sx = 1f * charWidth / fm.charsWidth(dc, 0, dc.length); + double sy = newHeight / asc; + double newAsc = asc * sy; + double newDec = dec * sy; + // it is not necessary to recalculate lm for the new font. + // note: lm.getBaselineOffsets()[lm.getBaselineIndex()]) must be 0 + // by definition. Was: + // int hght = (int) (ht + (newAsc - newDec); + // - lm.getBaselineOffsets()[lm.getBaselineIndex()])); + + if (Platform.isJS()) + { + /* + * SwingJS does not implement font.deriveFont() + * so use a scaling transform to draw instead, + * this is off by a very small amount + */ + final int hght = (int) (ht2 + (newAsc - newDec)); + Graphics2D gg = (Graphics2D) g; + int xShift = (int) Math.round(x * charWidth / sx); + int yShift = (int) Math.round(hght / sy); + gg.transform(AffineTransform.getScaleInstance(sx, sy)); + gg.drawString(s, xShift, yShift); + gg.transform( + AffineTransform.getScaleInstance(1D / sx, 1D / sy)); + ht2 += newHeight; + } + else + /** + * Java only + * + * @j2sIgnore + */ + { + // Java ('normal') method is to scale the font to fit + + final int hght = (int) (ht + (newAsc - newDec)); + Font font = ofont + .deriveFont(AffineTransform.getScaleInstance(sx, sy)); + g.setFont(font); + g.drawChars(dc, 0, dc.length, x * charWidth, hght); + ht += newHeight; + } } - g.setFont(ofont); } } x++;