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;
34 import java.awt.Toolkit;
36 import org.testng.Assert;
37 import org.testng.annotations.BeforeClass;
38 import org.testng.annotations.Test;
40 import junit.extensions.PA;
42 @Test(singleThreaded = true)
43 public class SeqCanvasTest
45 @BeforeClass(alwaysRun = true)
48 Thread.currentThread().setName("SeqCanvasTest Setup " + ++nTest);
51 Jalview.setSynchronous(true);
55 * Test the method that computes wrapped width in residues, height of wrapped
56 * widths in pixels, and the number of widths visible
58 @Test(groups = "Functional")
59 public void testCalculateWrappedGeometry_noAnnotations()
61 Thread.currentThread().setName("SeqCanvasTest noAnn " + ++nTest);
62 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
63 "examples/uniref50.fa", DataSourceType.FILE);
64 AlignViewport av = af.getViewport();
65 AlignmentI al = av.getAlignment();
66 assertEquals(al.getWidth(), 157);
67 assertEquals(al.getHeight(), 15);
68 av.getRanges().setStartEndSeq(0, 14);
70 SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
72 av.setWrapAlignment(true);
73 av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
74 int charHeight = av.getCharHeight();
75 int charWidth = av.getCharWidth();
76 Assert.assertTrue(charHeight == 17 && charWidth == 12
77 || charHeight == 19 && charWidth == 11,
78 "char height/width " + charHeight + "/" + charWidth);
80 // assertEquals(charHeight, Platform.isMac() ? 17 : 19);
81 // assertEquals(charWidth, Platform.isMac() ? 12 : 11);
84 * first with scales above, left, right
86 av.setShowAnnotation(false);
87 av.setScaleAboveWrapped(true);
88 av.setScaleLeftWrapped(true);
89 av.setScaleRightWrapped(true);
90 FontMetrics fm = testee.getFontMetrics(av.getFont());
91 int labelWidth = fm.stringWidth("000") + charWidth;
92 // BH 2020.03.22 It is not really necessary to be this detailed. Different
94 // always have slightly different parameters. StringgWidths are not
95 // necessarily linear sums of the letters involved.
96 // for example, the calculation for JavaScript is a float that has to be
98 // ..............................mac................PC................linux?
100 labelWidth == 39 || labelWidth == 35 || labelWidth == 36);// 3 * 9 +
112 * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
113 * take the whole multiple of character widths
115 int canvasWidth = 400;
116 int canvasHeight = 300;
117 int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
118 int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
119 assertEquals(wrappedWidth, residueColumns);
120 assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
121 assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
122 assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
124 int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
125 assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
126 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
129 * repeat height is 17 * (2 + 15) = 289
130 * make canvas height 2 * 289 + 3 * charHeight so just enough to
131 * draw 2 widths and the first sequence of a third
133 canvasHeight = charHeight * (17 * 2 + 3);
134 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
135 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
138 * reduce canvas height by 1 pixel
139 * - should not be enough height to draw 3 widths
142 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
143 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
146 * turn off scale above - can now fit in 2 and a bit widths
148 av.setScaleAboveWrapped(false);
149 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
150 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
153 * reduce height to enough for 2 widths and not quite a third
154 * i.e. two repeating heights + spacer + sequence - 1 pixel
156 canvasHeight = charHeight * (16 * 2 + 2) - 1;
157 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
158 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
161 * make canvas width enough for scales and 20 residues
163 canvasWidth = 2 * labelWidth + 20 * charWidth;
164 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
166 assertEquals(wrappedWidth, 20);
169 * reduce width by 1 pixel - rounds down to 19 residues
172 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
174 assertEquals(wrappedWidth, 19);
177 * turn off West scale - adds labelWidth (39) to available for residues
178 * which with the 11 remainder makes 50 which is 4 more charWidths rem 2
180 av.setScaleLeftWrapped(false);
181 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
183 assertEquals(wrappedWidth, 23);
186 * add 10 pixels to width to fit in another whole residue column
189 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
191 assertEquals(wrappedWidth, 23);
193 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
195 assertEquals(wrappedWidth, 24);
198 * turn off East scale to gain 39 more pixels (3 columns remainder 3)
200 av.setScaleRightWrapped(false);
201 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
203 assertEquals(wrappedWidth, 27);
206 * add 9 pixels to width to gain a residue column
209 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
211 assertEquals(wrappedWidth, 27); // 8px not enough
213 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
215 assertEquals(wrappedWidth, 28); // 9px is enough
218 * now West but not East scale - lose 39 pixels or 4 columns
220 av.setScaleLeftWrapped(true);
221 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
223 assertEquals(wrappedWidth, 24);
226 * adding 3 pixels to width regains one column
229 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
231 Assert.assertTrue(wrappedWidth == 24 || wrappedWidth == 25,
232 "WrappedWidth [" + wrappedWidth + "] should be 24 or 25"); // 2px
236 wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
238 assertEquals(wrappedWidth, 25); // 3px is enough
241 * turn off scales left and right, make width exactly 157 columns
243 av.setScaleLeftWrapped(false);
244 canvasWidth = al.getWidth() * charWidth;
245 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
246 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
250 * Test the method that computes wrapped width in residues, height of wrapped
251 * widths in pixels, and the number of widths visible
253 @Test(groups = "Functional")
254 public void testCalculateWrappedGeometry_withAnnotations()
256 Thread.currentThread().setName("SeqCanvasTest wAnn " + ++nTest);
257 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
258 "examples/uniref50.fa", DataSourceType.FILE);
259 AlignViewport av = af.getViewport();
260 AlignmentI al = av.getAlignment();
261 assertEquals(al.getWidth(), 157);
262 assertEquals(al.getHeight(), 15);
264 av.setWrapAlignment(true);
265 av.getRanges().setStartEndSeq(0, 14);
266 av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
267 int charHeight = av.getCharHeight();
268 int charWidth = av.getCharWidth();
271 charHeight == 17 && charWidth == 12
272 || charHeight == 19 && charWidth == 11,
273 "char height/width " + charHeight + "/" + charWidth);
274 // assertEquals(charHeight, Platform.isMac() ? 17 : 19);
275 // assertEquals(charWidth, Platform.isMac() ? 12 : 11);
277 SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
280 * first with scales above, left, right
282 av.setShowAnnotation(true);
283 av.setScaleAboveWrapped(true);
284 av.setScaleLeftWrapped(true);
285 av.setScaleRightWrapped(true);
287 FontMetrics fm = testee.getFontMetrics(av.getFont());
288 int labelWidth = fm.stringWidth("000") + charWidth;
289 // BH 2020.03.22 It is not really necessary to be this detailed. Different
291 // always have slightly different parameters. StringgWidths are not
292 // necessarily linear sums of the letters involved.
293 // for example, the calculation for JavaScript is a float that has to be
295 // ..............................mac................PC................linux?
297 labelWidth == 39 || labelWidth == 35 || labelWidth == 36);// 3 * 9 +
307 // int labelWidth = fm.stringWidth("000") + charWidth;
308 // assertEquals(labelWidth,
309 // Platform.isMac() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
311 int annotationHeight = testee.getAnnotationHeight();
314 * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
315 * take the whole multiple of character widths
317 int canvasWidth = 400;
318 int canvasHeight = 300;
319 int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
320 int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
321 assertEquals(wrappedWidth, residueColumns);
322 assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
323 assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
324 assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
326 int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
327 assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())
328 + SeqCanvas.SEQS_ANNOTATION_GAP + annotationHeight);
329 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
332 * repeat height is 17 * (2 + 15) = 289 + 3 + annotationHeight = 510
333 * make canvas height 2 of these plus 3 charHeights
334 * so just enough to draw 2 widths, gap + scale + the first sequence of a third
336 canvasHeight = charHeight * (17 * 2 + 3)
337 + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP);
338 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
339 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
342 * reduce canvas height by 1 pixel - should not be enough height
346 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
347 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
350 * turn off scale above - can now fit in 2 and a bit widths
352 av.setScaleAboveWrapped(false);
353 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
354 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
357 * reduce height to enough for 2 widths and not quite a third
358 * i.e. two repeating heights + spacer + sequence - 1 pixel
360 canvasHeight = charHeight * (16 * 2 + 2)
361 + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP) - 1;
362 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
363 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
366 * add 1 pixel to height - should now get 3 widths drawn
369 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
370 assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
373 private static int nTest = 0;
375 * Test simulates loading an unwrapped alignment, shrinking it vertically so
376 * not all sequences are visible, then changing to wrapped mode. The ranges
377 * endSeq should be unchanged, but the vertical repeat height should include
380 @Test(groups = "Functional")
381 public void testCalculateWrappedGeometry_fromScrolled()
384 ViewportRanges.sTest = "";
385 Thread.currentThread().setName("SeqCanvasTest fromScrolled " + ++nTest);
386 AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
387 "examples/uniref50.fa", DataSourceType.FILE);
388 AlignViewport av = af.getViewport();
389 AlignmentI al = av.getAlignment();
390 assertEquals(al.getWidth(), 157);
391 assertEquals(al.getHeight(), 15);
393 av.getRanges().setStartEndSeq(0, 3);
395 s += " SC1 " + av.getRanges() + " " + ViewportRanges.sTest;
396 av.setShowAnnotation(false);
397 av.setScaleAboveWrapped(true);
398 SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
399 av.setWrapAlignment(true);
400 av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
401 int charHeight = av.getCharHeight();
402 int charWidth = av.getCharWidth();
403 // Windows h=19, w=11; Mac (and Linux?) 17,11
404 Assert.assertTrue(charHeight == 17 && charWidth == 12
405 || charHeight == 19 && charWidth == 11,
406 "char height/width " + charHeight + "/" + charWidth);
407 int canvasWidth = 400;
408 int canvasHeight = 300;
409 testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
410 int repeatingHeight = (int) PA.getValue(testee,
411 "wrappedRepeatHeightPx");
412 assertEquals(av.getRanges().getEndSeq(), 0, "endSeq should be 3 " + s); // unchanged
413 assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
416 private static void flushEvents()
418 ((sun.awt.SunToolkit) Toolkit.getDefaultToolkit()).flushPendingEvents();