From: gmungoc Date: Tue, 22 Aug 2017 10:19:21 +0000 (+0100) Subject: JAL-2609 tweaks to, and tests for, calculateWrappedGeometry X-Git-Tag: Release_2_10_3b1~91^2~6^2~3 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=89dff5f24a9584bc3f01cc9057cfbc723a31b145;p=jalview.git JAL-2609 tweaks to, and tests for, calculateWrappedGeometry --- diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 46f2d84..a4c7da5 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -77,10 +77,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI int cursorY = 0; - int charHeight = 0; - - int charWidth = 0; - private AnnotationPanel annotations; /* @@ -105,7 +101,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI public SeqCanvas(AlignmentPanel ap) { this.av = ap.av; - updateViewport(); fr = new FeatureRenderer(ap); sr = new SequenceRenderer(av); setLayout(new BorderLayout()); @@ -125,12 +120,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return fr; } - private void updateViewport() - { - charHeight = av.getCharHeight(); - charWidth = av.getCharWidth(); - } - /** * Draws the scale above a region of a wrapped alignment, consisting of a * column number every major interval (10 columns). @@ -147,7 +136,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ private void drawNorthScale(Graphics g, int startx, int endx, int ypos) { - updateViewport(); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); /* * white fill the scale space (for the fastPaint case) @@ -201,6 +191,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI void drawVerticalScale(Graphics g, int startx, int endx, int ypos, boolean left) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + ypos += charHeight; if (av.hasHiddenColumns()) @@ -297,7 +290,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI try { - updateViewport(); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); ViewportRanges ranges = av.getRanges(); int startRes = ranges.getStartRes(); @@ -356,7 +350,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI @Override public void paintComponent(Graphics g) { - updateViewport(); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); BufferedImage lcimg = img; // take reference since other threads may null // img and call later. super.paintComponent(g); @@ -436,6 +431,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ public int getWrappedCanvasWidth(int canvasWidth) { + int charWidth = av.getCharWidth(); + FontMetrics fm = getFontMetrics(av.getFont()); labelWidthEast = 0; @@ -500,8 +497,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI public void drawWrappedPanel(Graphics g, int canvasWidth, int canvasHeight, final int startColumn) { - updateViewport(); - int wrappedWidthInResidues = calculateWrappedGeometry(canvasWidth, canvasHeight); @@ -514,20 +509,19 @@ public class SeqCanvas extends JComponent implements ViewportListenerI * draw one width at a time (including any scales or annotation shown), * until we have run out of either alignment or vertical space available */ - int yposMax = canvasHeight; - // ensure room for at least one sequence - yposMax -= wrappedSpaceAboveAlignment - charHeight; int ypos = wrappedSpaceAboveAlignment; int maxWidth = ranges.getVisibleAlignmentWidth(); int start = startColumn; - while ((ypos <= yposMax) && (start < maxWidth)) + int currentWidth = 0; + while ((currentWidth < wrappedVisibleWidths) && (start < maxWidth)) { int endColumn = Math .min(maxWidth, start + wrappedWidthInResidues - 1); drawWrappedWidth(g, ypos, start, endColumn, canvasHeight); ypos += wrappedRepeatHeightPx; start += wrappedWidthInResidues; + currentWidth++; } drawWrappedDecorators(g, startColumn); @@ -549,6 +543,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ protected int calculateWrappedGeometry(int canvasWidth, int canvasHeight) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + /* * width of labels in pixels left and right (if shown) */ @@ -563,6 +560,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI /* * vertical space in pixels between wrapped widths of alignment + * - one character height, or two if scale above is drawn */ wrappedSpaceAboveAlignment = charHeight * (av.getScaleAboveWrapped() ? 2 : 1); @@ -577,7 +575,16 @@ public class SeqCanvas extends JComponent implements ViewportListenerI wrappedRepeatHeightPx += getAnnotationHeight(); /* - * number of visible widths (the last one may be part height) + * number of residue columns we can show in each row; + * this is just canvas width less scale left and right (if shown), + * as a whole multiple of character widths + */ + int wrappedWidthInResidues = (canvasWidth - labelWidthEast - labelWidthWest) + / charWidth; + + /* + * number of visible widths (the last one may be part height), + * ensuring a part height includes at least one sequence */ ViewportRanges ranges = av.getRanges(); int xMax = ranges.getVisibleAlignmentWidth(); @@ -591,19 +598,13 @@ public class SeqCanvas extends JComponent implements ViewportListenerI /* * limit visibleWidths to not exceed width of alignment */ - int viewportWidth = ranges.getViewportWidth(); - int maxWidths = (xMax - ranges.getStartRes()) / viewportWidth; - if (xMax % viewportWidth > 0) + int maxWidths = (xMax - ranges.getStartRes()) / wrappedWidthInResidues; + if (xMax % wrappedWidthInResidues > 0) { maxWidths++; } wrappedVisibleWidths = Math.min(wrappedVisibleWidths, maxWidths); - /* - * number of whole width residue columns we can show in each row - */ - int wrappedWidthInResidues = (canvasWidth - labelWidthEast - labelWidthWest) - / charWidth; return wrappedWidthInResidues; } @@ -620,6 +621,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI protected void drawWrappedWidth(Graphics g, int ypos, int startColumn, int endColumn, int canvasHeight) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + ViewportRanges ranges = av.getRanges(); int viewportWidth = ranges.getViewportWidth(); @@ -687,6 +691,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI */ protected void drawWrappedDecorators(Graphics g, int startColumn) { + int charWidth = av.getCharWidth(); + g.setFont(av.getFont()); g.setColor(Color.black); @@ -747,6 +753,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI protected void drawHiddenColumnMarkers(Graphics g, int ypos, int startColumn, int endColumn) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + g.setColor(Color.blue); HiddenColumns hidden = av.getAlignment().getHiddenColumns(); List positions = hidden.findHiddenRegionPositions(); @@ -809,7 +818,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI public void drawPanel(Graphics g1, final int startRes, final int endRes, final int startSeq, final int endSeq, final int yOffset) { - updateViewport(); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + if (!av.hasHiddenColumns()) { draw(g1, startRes, endRes, startSeq, endSeq, yOffset); @@ -899,6 +910,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI private void draw(Graphics g, int startRes, int endRes, int startSeq, int endSeq, int offset) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + g.setFont(av.getFont()); sr.prepare(g, av.isRenderGaps()); @@ -963,6 +977,9 @@ public class SeqCanvas extends JComponent implements ViewportListenerI void drawGroupsBoundaries(Graphics g1, int startRes, int endRes, int startSeq, int endSeq, int offset) { + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + Graphics2D g = (Graphics2D) g1; // // /////////////////////////////////// @@ -1170,14 +1187,11 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return false; } boolean wrapped = av.getWrapAlignment(); - try { fastPaint = !noFastPaint; fastpainting = fastPaint; - updateViewport(); - /* * to avoid redrawing the whole visible region, we instead * redraw just the minimal regions to remove previous highlights @@ -1445,6 +1459,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI ViewportRanges ranges = av.getRanges(); int viewportWidth = ranges.getViewportWidth(); + int charWidth = av.getCharWidth(); /** * draw full height alignment in the second last row, last columns, if the @@ -1478,6 +1493,8 @@ public class SeqCanvas extends JComponent implements ViewportListenerI } /* + * draw newly visible columns in last wrapped width (none if we + * have reached the end of the alignment) * y-offset for drawing last width is height of widths above, * plus one gap row */ @@ -1486,7 +1503,6 @@ public class SeqCanvas extends JComponent implements ViewportListenerI + wrappedSpaceAboveAlignment; int endRes = ranges.getEndRes(); endRes += widthsAbove * viewportWidth; - endRes = Math.min(endRes, ranges.getVisibleAlignmentWidth()); int startRes = endRes - columns + 1; /* @@ -1503,7 +1519,10 @@ public class SeqCanvas extends JComponent implements ViewportListenerI gg.setFont(av.getFont()); gg.setColor(Color.black); - drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); + if (startRes < ranges.getVisibleAlignmentWidth()) + { + drawWrappedWidth(gg, ypos, startRes, endRes, canvasHeight); + } /* * and finally, white fill any space below the visible alignment @@ -1530,6 +1549,7 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { return; } + int charWidth = av.getCharWidth(); int canvasHeight = getHeight(); ViewportRanges ranges = av.getRanges(); @@ -1618,11 +1638,13 @@ public class SeqCanvas extends JComponent implements ViewportListenerI { return false; } - + int charHeight = av.getCharHeight(); + boolean matchFound = false; + calculateWrappedGeometry(getWidth(), getHeight()); int wrappedWidth = av.getWrappedWidth(); - int wrappedHeight = getRepeatHeightWrapped(); + int wrappedHeight = wrappedRepeatHeightPx; ViewportRanges ranges = av.getRanges(); int canvasHeight = getHeight(); @@ -1719,25 +1741,4 @@ public class SeqCanvas extends JComponent implements ViewportListenerI return matchFound; } - - /** - * Answers the height in pixels of a repeating section of the wrapped - * alignment, including space above, scale above if shown, sequences, and - * annotation panel if shown - * - * @return - */ - protected int getRepeatHeightWrapped() - { - // gap (and maybe scale) above - int repeatHeight = charHeight * (av.getScaleAboveWrapped() ? 2 : 1); - - // add sequences - repeatHeight += av.getRanges().getViewportHeight() * charHeight; - - // add annotations panel height if shown - repeatHeight += getAnnotationHeight(); - - return repeatHeight; - } } diff --git a/test/jalview/gui/SeqCanvasTest.java b/test/jalview/gui/SeqCanvasTest.java new file mode 100644 index 0000000..e387322 --- /dev/null +++ b/test/jalview/gui/SeqCanvasTest.java @@ -0,0 +1,184 @@ +package jalview.gui; + +import static org.testng.Assert.assertEquals; + +import jalview.datamodel.AlignmentI; +import jalview.io.DataSourceType; +import jalview.io.FileLoader; + +import java.awt.Font; +import java.awt.FontMetrics; + +import junit.extensions.PA; + +import org.testng.annotations.Test; + +import sun.swing.SwingUtilities2; + +public class SeqCanvasTest +{ + /** + * Test the method that computes wrapped width in residues, height of wrapped + * widths in pixels, and the number of widths visible + */ + @Test(groups = "Functional") + public void testCalculateWrappedGeometry_noAnnotations() + { + AlignFrame af = new FileLoader().LoadFileWaitTillLoaded( + "examples/uniref50.fa", DataSourceType.FILE); + AlignViewport av = af.getViewport(); + AlignmentI al = av.getAlignment(); + assertEquals(al.getWidth(), 157); + assertEquals(al.getHeight(), 15); + + av.setWrapAlignment(true); + av.setFont(new Font("SansSerif", Font.PLAIN, 14), true); + int charHeight = av.getCharHeight(); + int charWidth = av.getCharWidth(); + assertEquals(charHeight, 17); + assertEquals(charWidth, 12); + + SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas; + + /* + * first with scales above, left, right + */ + av.setShowAnnotation(false); + av.setScaleAboveWrapped(true); + av.setScaleLeftWrapped(true); + av.setScaleRightWrapped(true); + FontMetrics fm = SwingUtilities2.getFontMetrics(testee, av.getFont()); + int labelWidth = fm.stringWidth("00000"); // width of 3 digits and 2 spaces + assertEquals(labelWidth, 45); // note this is not 5 * charWidth + + /* + * width 400 pixels leaves (400 - 2*labelWidth) for residue columns + * take the whole multiple of character widths + */ + int canvasWidth = 400; + int canvasHeight = 300; + int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth; + int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(wrappedWidth, residueColumns); + assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth); + assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth); + assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"), + 2 * charHeight); + int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx"); + assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1); + + /* + * repeat height is 17 * (2 + 15) = 289 + * make canvas height 2 * 289 + 3 * charHeight so just enough to + * draw 2 widths and the first sequence of a third + */ + canvasHeight = charHeight * (17 * 2 + 3); + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + + /* + * reduce canvas height by 1 pixel - should not be enough height + * to draw 3 widths + */ + canvasHeight -= 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2); + + /* + * turn off scale above - can now fit in 2 and a bit widths + */ + av.setScaleAboveWrapped(false); + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3); + + /* + * reduce height to enough for 2 widths and not quite a third + * i.e. two repeating heights + spacer + sequence - 1 pixel + */ + canvasHeight = charHeight * (16 * 2 + 2) - 1; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2); + + /* + * make canvas width enough for scales and 20 residues + */ + canvasWidth = 2 * labelWidth + 20 * charWidth; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 20); + + /* + * reduce width by 1 pixel - rounds down to 19 residues + */ + canvasWidth -= 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 19); + + /* + * turn off West scale - adds labelWidth (45) to available for residues + * which with the 11 remainder makes 56 which is 4 more charWidths rem 8 + */ + av.setScaleLeftWrapped(false); + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 23); + + /* + * add 4 pixels to width to fit in another whole residue column + */ + canvasWidth += 3; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 23); + canvasWidth += 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 24); + + /* + * turn off East scale to gain 45 more pixels (3 columns remainder 9) + */ + av.setScaleRightWrapped(false); + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 27); + + /* + * add 3 pixels to width to gain a residue column + */ + canvasWidth += 3; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 28); + + /* + * now West but not East scale - lose 45 pixels or 4 columns + */ + av.setScaleLeftWrapped(true); + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 24); + + /* + * adding 9 pixels to width regains one column + */ + canvasWidth += 8; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 24); + canvasWidth += 1; + wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, + canvasHeight); + assertEquals(wrappedWidth, 25); + + /* + * turn off scales left and right, make width exactly 157 columns + */ + av.setScaleLeftWrapped(false); + canvasWidth = al.getWidth() * charWidth; + testee.calculateWrappedGeometry(canvasWidth, canvasHeight); + assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1); + } +}