2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
23 import static org.testng.Assert.assertEquals;
25 import jalview.bin.Cache;
26 import jalview.bin.Jalview;
27 import jalview.datamodel.AlignmentI;
28 import jalview.io.DataSourceType;
29 import jalview.io.FileLoader;
30 import jalview.viewmodel.ViewportRanges;
33 import java.awt.FontMetrics;
35 import org.testng.Assert;
36 import org.testng.annotations.BeforeClass;
37 import org.testng.annotations.Test;
39 import junit.extensions.PA;
41 @Test(singleThreaded = true)
42 public class SeqCanvasTest
44 @BeforeClass(alwaysRun = true)
47 Thread.currentThread().setName("SeqCanvasTest Setup " + ++nTest);
50 Jalview.setSynchronous(true);
54 * Test the method that computes wrapped width in residues, height of wrapped
55 * widths in pixels, and the number of widths visible
57 @Test(groups = "Functional")
58 public void testCalculateWrappedGeometry_noAnnotations()
60 Thread.currentThread().setName("SeqCanvasTest noAnn " + ++nTest);
61 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
62 "examples/uniref50.fa", DataSourceType.FILE);
63 AlignViewport av = af.getViewport();
64 AlignmentI al = av.getAlignment();
65 assertEquals(al.getWidth(), 157);
66 assertEquals(al.getHeight(), 15);
67 av.getRanges().setStartEndSeq(0, 14);
69 SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
71 av.setWrapAlignment(true);
72 av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
73 int charHeight = av.getCharHeight();
74 int charWidth = av.getCharWidth();
75 Assert.assertTrue(charHeight == 17 && charWidth == 12
76 || charHeight == 19 && charWidth == 11,
77 "char height/width " + charHeight + "/" + charWidth);
79 // assertEquals(charHeight, Platform.isMac() ? 17 : 19);
80 // assertEquals(charWidth, Platform.isMac() ? 12 : 11);
83 * first with scales above, left, right
85 av.setShowAnnotation(false);
86 av.setScaleAboveWrapped(true);
87 av.setScaleLeftWrapped(true);
88 av.setScaleRightWrapped(true);
89 FontMetrics fm = testee.getFontMetrics(av.getFont());
90 int labelWidth = fm.stringWidth("000") + charWidth;
91 // BH 2020.03.22 It is not really necessary to be this detailed. Different
93 // always have slightly different parameters. StringgWidths are not
94 // necessarily linear sums of the letters involved.
95 // for example, the calculation for JavaScript is a float that has to be
97 // ..............................mac................PC................linux?
99 labelWidth == 39 || labelWidth == 35 || labelWidth == 36);// 3 * 9 +
111 * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
112 * take the whole multiple of character widths
114 int canvasWidth = 400;
115 int canvasHeight = 300;
116 int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
117 int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
118 assertEquals(wrappedWidth, residueColumns);
119 assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
120 assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
121 assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
123 int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
124 assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
125 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
128 * repeat height is 17 * (2 + 15) = 289
129 * make canvas height 2 * 289 + 3 * charHeight so just enough to
130 * draw 2 widths and the first sequence of a third
132 canvasHeight = charHeight * (17 * 2 + 3);
133 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
134 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
137 * reduce canvas height by 1 pixel
138 * - should not be enough height to draw 3 widths
141 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
142 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
145 * turn off scale above - can now fit in 2 and a bit widths
147 av.setScaleAboveWrapped(false);
148 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
149 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
152 * reduce height to enough for 2 widths and not quite a third
153 * i.e. two repeating heights + spacer + sequence - 1 pixel
155 canvasHeight = charHeight * (16 * 2 + 2) - 1;
156 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
157 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
160 * make canvas width enough for scales and 20 residues
162 canvasWidth = 2 * labelWidth + 20 * charWidth;
163 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
165 assertEquals(wrappedWidth, 20);
168 * reduce width by 1 pixel - rounds down to 19 residues
171 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
173 assertEquals(wrappedWidth, 19);
176 * turn off West scale - adds labelWidth (39) to available for residues
177 * which with the 11 remainder makes 50 which is 4 more charWidths rem 2
179 av.setScaleLeftWrapped(false);
180 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
182 assertEquals(wrappedWidth, 23);
185 * add 10 pixels to width to fit in another whole residue column
188 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
190 assertEquals(wrappedWidth, 23);
192 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
194 assertEquals(wrappedWidth, 24);
197 * turn off East scale to gain 39 more pixels (3 columns remainder 3)
199 av.setScaleRightWrapped(false);
200 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
202 assertEquals(wrappedWidth, 27);
205 * add 9 pixels to width to gain a residue column
208 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
210 assertEquals(wrappedWidth, 27); // 8px not enough
212 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
214 assertEquals(wrappedWidth, 28); // 9px is enough
217 * now West but not East scale - lose 39 pixels or 4 columns
219 av.setScaleLeftWrapped(true);
220 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
222 assertEquals(wrappedWidth, 24);
225 * adding 3 pixels to width regains one column
228 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
230 Assert.assertTrue(wrappedWidth == 24 || wrappedWidth == 25,
231 "WrappedWidth [" + wrappedWidth + "] should be 24 or 25"); // 2px
235 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
237 assertEquals(wrappedWidth, 25); // 3px is enough
240 * turn off scales left and right, make width exactly 157 columns
242 av.setScaleLeftWrapped(false);
243 canvasWidth = al.getWidth() * charWidth;
244 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
245 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
249 * Test the method that computes wrapped width in residues, height of wrapped
250 * widths in pixels, and the number of widths visible
252 @Test(groups = "Functional")
253 public void testCalculateWrappedGeometry_withAnnotations()
255 Thread.currentThread().setName("SeqCanvasTest wAnn " + ++nTest);
256 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
257 "examples/uniref50.fa", DataSourceType.FILE);
258 AlignViewport av = af.getViewport();
259 AlignmentI al = av.getAlignment();
260 assertEquals(al.getWidth(), 157);
261 assertEquals(al.getHeight(), 15);
263 av.setWrapAlignment(true);
264 av.getRanges().setStartEndSeq(0, 14);
265 av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
266 int charHeight = av.getCharHeight();
267 int charWidth = av.getCharWidth();
270 charHeight == 17 && charWidth == 12
271 || charHeight == 19 && charWidth == 11,
272 "char height/width " + charHeight + "/" + charWidth);
273 // assertEquals(charHeight, Platform.isMac() ? 17 : 19);
274 // assertEquals(charWidth, Platform.isMac() ? 12 : 11);
276 SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
279 * first with scales above, left, right
281 av.setShowAnnotation(true);
282 av.setScaleAboveWrapped(true);
283 av.setScaleLeftWrapped(true);
284 av.setScaleRightWrapped(true);
286 FontMetrics fm = testee.getFontMetrics(av.getFont());
287 int labelWidth = fm.stringWidth("000") + charWidth;
288 // BH 2020.03.22 It is not really necessary to be this detailed. Different
290 // always have slightly different parameters. StringgWidths are not
291 // necessarily linear sums of the letters involved.
292 // for example, the calculation for JavaScript is a float that has to be
294 // ..............................mac................PC................linux?
296 labelWidth == 39 || labelWidth == 35 || labelWidth == 36);// 3 * 9 +
306 // int labelWidth = fm.stringWidth("000") + charWidth;
307 // assertEquals(labelWidth,
308 // Platform.isMac() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
310 int annotationHeight = testee.getAnnotationHeight();
313 * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
314 * take the whole multiple of character widths
316 int canvasWidth = 400;
317 int canvasHeight = 300;
318 int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
319 int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
320 assertEquals(wrappedWidth, residueColumns);
321 assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
322 assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
323 assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
325 int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
326 assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())
327 + SeqCanvas.SEQS_ANNOTATION_GAP + annotationHeight);
328 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
331 * repeat height is 17 * (2 + 15) = 289 + 3 + annotationHeight = 510
332 * make canvas height 2 of these plus 3 charHeights
333 * so just enough to draw 2 widths, gap + scale + the first sequence of a third
335 canvasHeight = charHeight * (17 * 2 + 3)
336 + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP);
337 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
338 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
341 * reduce canvas height by 1 pixel - should not be enough height
345 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
346 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
349 * turn off scale above - can now fit in 2 and a bit widths
351 av.setScaleAboveWrapped(false);
352 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
353 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
356 * reduce height to enough for 2 widths and not quite a third
357 * i.e. two repeating heights + spacer + sequence - 1 pixel
359 canvasHeight = charHeight * (16 * 2 + 2)
360 + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP) - 1;
361 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
362 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
365 * add 1 pixel to height - should now get 3 widths drawn
368 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
369 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
372 private static int nTest = 0;
374 * Test simulates loading an unwrapped alignment, shrinking it vertically so
375 * not all sequences are visible, then changing to wrapped mode. The ranges
376 * endSeq should be unchanged, but the vertical repeat height should include
379 @Test(groups = "Functional")
380 public void testCalculateWrappedGeometry_fromScrolled()
382 Thread.currentThread().setName("SeqCanvasTest fromScrolled " + ++nTest);
383 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
384 "examples/uniref50.fa", DataSourceType.FILE);
385 AlignViewport av = af.getViewport();
386 AlignmentI al = av.getAlignment();
387 assertEquals(al.getWidth(), 157);
388 assertEquals(al.getHeight(), 15);
389 av.getRanges().setStartEndSeq(0, 3);
391 s += " SC1 " + av.getRanges() + " " + ViewportRanges.sTest;
392 av.setShowAnnotation(false);
393 av.setScaleAboveWrapped(true);
394 SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
395 av.setWrapAlignment(true);
396 av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
397 int charHeight = av.getCharHeight();
398 int charWidth = av.getCharWidth();
399 // Windows h=19, w=11; Mac (and Linux?) 17,11
400 Assert.assertTrue(charHeight == 17 && charWidth == 12
401 || charHeight == 19 && charWidth == 11,
402 "char height/width " + charHeight + "/" + charWidth);
403 int canvasWidth = 400;
404 int canvasHeight = 300;
405 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
406 int repeatingHeight = (int) PA.getValue(testee,
407 "wrappedRepeatHeightPx");
408 assertEquals(av.getRanges().getEndSeq(), 0, "endSeq should be 3 " + s); // unchanged
409 assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));