ce4ff59cfc0e61638249f166f3995df403feff59
[jalview.git] / test / jalview / gui / SeqCanvasTest.java
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.gui;
22
23 import static org.testng.Assert.assertEquals;
24
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.util.Platform;
31
32 import java.awt.Font;
33 import java.awt.FontMetrics;
34
35 import org.testng.Assert;
36 import org.testng.annotations.BeforeClass;
37 import org.testng.annotations.Test;
38
39 import junit.extensions.PA;
40
41 public class SeqCanvasTest
42 {
43   @BeforeClass(alwaysRun = true)
44   public void setUp()
45   {
46     Cache.initLogger();
47     Jalview.setSynchronous(true);
48   }
49
50   /**
51    * Test the method that computes wrapped width in residues, height of wrapped
52    * widths in pixels, and the number of widths visible
53    */
54   @Test(groups = "Functional")
55   public void testCalculateWrappedGeometry_noAnnotations()
56   {
57     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
58             "examples/uniref50.fa", DataSourceType.FILE);
59     AlignViewport av = af.getViewport();
60     AlignmentI al = av.getAlignment();
61     assertEquals(al.getWidth(), 157);
62     assertEquals(al.getHeight(), 15);
63     av.getRanges().setStartEndSeq(0, 14);
64
65     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
66
67     av.setWrapAlignment(true);
68     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
69     int charHeight = av.getCharHeight();
70     int charWidth = av.getCharWidth();
71     Assert.assertTrue(charHeight == 17 && charWidth == 12
72             || charHeight == 19 && charWidth == 11,
73             "char height/width " + charHeight + "/" + charWidth);
74     //
75     // assertEquals(charHeight, Platform.isMac() ? 17 : 19);
76     // assertEquals(charWidth, Platform.isMac() ? 12 : 11);
77
78     /*
79      * first with scales above, left, right
80      */
81     av.setShowAnnotation(false);
82     av.setScaleAboveWrapped(true);
83     av.setScaleLeftWrapped(true);
84     av.setScaleRightWrapped(true);
85     FontMetrics fm = testee.getFontMetrics(av.getFont());
86     int labelWidth = fm.stringWidth("000") + charWidth;
87     // BH 2020.03.22 It is not really necessary to be this detailed. Different
88     // OS-based UIs will
89     // always have slightly different parameters. StringgWidths are not
90     // necessarily linear sums of the letters involved.
91     // for example, the calculation for JavaScript is a float that has to be
92     // rounded.
93     // ..............................mac................PC................linux?
94     Assert.assertTrue(
95             labelWidth == 39 || labelWidth == 35 || labelWidth == 36);// 3 * 9 +
96                                                                       // charWidth
97                                                                       // ||
98                                                                       // labelWidth
99                                                                       // == 3 *
100                                                                       // 8 +
101                                                                       // charWidth,
102                                                                       // "labelWidth
103                                                                       // 36 or
104                                                                       // 39");
105
106     /*
107      * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
108      * take the whole multiple of character widths
109      */
110     int canvasWidth = 400;
111     int canvasHeight = 300;
112     int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
113     int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
114     assertEquals(wrappedWidth, residueColumns);
115     assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
116     assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
117     assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
118             2 * charHeight);
119     int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
120     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
121     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
122
123     /*
124      * repeat height is 17 * (2 + 15) = 289
125      * make canvas height 2 * 289 + 3 * charHeight so just enough to
126      * draw 2 widths and the first sequence of a third
127      */
128     canvasHeight = charHeight * (17 * 2 + 3);
129     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
130     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
131
132     /*
133      * reduce canvas height by 1 pixel 
134      * - should not be enough height to draw 3 widths
135      */
136     canvasHeight -= 1;
137     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
138     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
139
140     /*
141      * turn off scale above - can now fit in 2 and a bit widths
142      */
143     av.setScaleAboveWrapped(false);
144     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
145     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
146
147     /*
148      * reduce height to enough for 2 widths and not quite a third
149      * i.e. two repeating heights + spacer + sequence - 1 pixel
150      */
151     canvasHeight = charHeight * (16 * 2 + 2) - 1;
152     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
153     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
154
155     /*
156      * make canvas width enough for scales and 20 residues
157      */
158     canvasWidth = 2 * labelWidth + 20 * charWidth;
159     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
160             canvasHeight);
161     assertEquals(wrappedWidth, 20);
162
163     /*
164      * reduce width by 1 pixel - rounds down to 19 residues
165      */
166     canvasWidth -= 1;
167     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
168             canvasHeight);
169     assertEquals(wrappedWidth, 19);
170
171     /*
172      * turn off West scale - adds labelWidth (39) to available for residues
173      * which with the 11 remainder makes 50 which is 4 more charWidths rem 2
174      */
175     av.setScaleLeftWrapped(false);
176     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
177             canvasHeight);
178     assertEquals(wrappedWidth, 23);
179
180     /*
181      * add 10 pixels to width to fit in another whole residue column
182      */
183     canvasWidth += 9;
184     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
185             canvasHeight);
186     assertEquals(wrappedWidth, 23);
187     canvasWidth += 1;
188     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
189             canvasHeight);
190     assertEquals(wrappedWidth, 24);
191
192     /*
193      * turn off East scale to gain 39 more pixels (3 columns remainder 3)
194      */
195     av.setScaleRightWrapped(false);
196     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
197             canvasHeight);
198     assertEquals(wrappedWidth, 27);
199
200     /*
201      * add 9 pixels to width to gain a residue column
202      */
203     canvasWidth += 8;
204     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
205             canvasHeight);
206     assertEquals(wrappedWidth, 27); // 8px not enough
207     canvasWidth += 1;
208     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
209             canvasHeight);
210     assertEquals(wrappedWidth, 28); // 9px is enough
211
212     /*
213      * now West but not East scale - lose 39 pixels or 4 columns
214      */
215     av.setScaleLeftWrapped(true);
216     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
217             canvasHeight);
218     assertEquals(wrappedWidth, 24);
219
220     /*
221      * adding 3 pixels to width regains one column
222      */
223     canvasWidth += 2;
224     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
225             canvasHeight);
226     assertEquals(wrappedWidth, Platform.isMac() ? 24 : 25); // 2px not enough
227     canvasWidth += 1;
228     wrappedWidth = testee.calculateWrappedGeometry(canvasWidth,
229             canvasHeight);
230     assertEquals(wrappedWidth, 25); // 3px is enough
231
232     /*
233      * turn off scales left and right, make width exactly 157 columns
234      */
235     av.setScaleLeftWrapped(false);
236     canvasWidth = al.getWidth() * charWidth;
237     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
238     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
239   }
240
241   /**
242    * Test the method that computes wrapped width in residues, height of wrapped
243    * widths in pixels, and the number of widths visible
244    */
245   @Test(groups = "Functional")
246   public void testCalculateWrappedGeometry_withAnnotations()
247   {
248     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
249             "examples/uniref50.fa", DataSourceType.FILE);
250     AlignViewport av = af.getViewport();
251     AlignmentI al = av.getAlignment();
252     assertEquals(al.getWidth(), 157);
253     assertEquals(al.getHeight(), 15);
254   
255     av.setWrapAlignment(true);
256     av.getRanges().setStartEndSeq(0, 14);
257     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
258     int charHeight = av.getCharHeight();
259     int charWidth = av.getCharWidth();
260
261     Assert.assertTrue(
262             charHeight == 17 && charWidth == 12
263                     || charHeight == 19 && charWidth == 11,
264             "char height/width " + charHeight + "/" + charWidth);
265     // assertEquals(charHeight, Platform.isMac() ? 17 : 19);
266     // assertEquals(charWidth, Platform.isMac() ? 12 : 11);
267
268     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
269   
270     /*
271      * first with scales above, left, right
272      */
273     av.setShowAnnotation(true);
274     av.setScaleAboveWrapped(true);
275     av.setScaleLeftWrapped(true);
276     av.setScaleRightWrapped(true);
277
278     FontMetrics fm = testee.getFontMetrics(av.getFont());
279     int labelWidth = fm.stringWidth("000") + charWidth;
280     // BH 2020.03.22 It is not really necessary to be this detailed. Different
281     // OS-based UIs will
282     // always have slightly different parameters. StringgWidths are not
283     // necessarily linear sums of the letters involved.
284     // for example, the calculation for JavaScript is a float that has to be
285     // rounded.
286     // ..............................mac................PC................linux?
287     Assert.assertTrue(
288             labelWidth == 39 || labelWidth == 35 || labelWidth == 36);// 3 * 9 +
289                                                                       // charWidth
290                                                                       // ||
291                                                                       // labelWidth
292                                                                       // == 3 *
293                                                                       // 8 +
294                                                                       // charWidth,
295                                                                       // "labelWidth
296                                                                       // 36 or
297                                                                       // 39");
298     // int labelWidth = fm.stringWidth("000") + charWidth;
299     // assertEquals(labelWidth,
300     // Platform.isMac() ? 3 * 9 + charWidth : 3 * 8 + charWidth);
301
302     int annotationHeight = testee.getAnnotationHeight();
303
304     /*
305      * width 400 pixels leaves (400 - 2*labelWidth) for residue columns
306      * take the whole multiple of character widths
307      */
308     int canvasWidth = 400;
309     int canvasHeight = 300;
310     int residueColumns = (canvasWidth - 2 * labelWidth) / charWidth;
311     int wrappedWidth = testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
312     assertEquals(wrappedWidth, residueColumns);
313     assertEquals(PA.getValue(testee, "labelWidthWest"), labelWidth);
314     assertEquals(PA.getValue(testee, "labelWidthEast"), labelWidth);
315     assertEquals(PA.getValue(testee, "wrappedSpaceAboveAlignment"),
316             2 * charHeight);
317     int repeatingHeight = (int) PA.getValue(testee, "wrappedRepeatHeightPx");
318     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight())
319             + SeqCanvas.SEQS_ANNOTATION_GAP + annotationHeight);
320     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 1);
321   
322     /*
323      * repeat height is 17 * (2 + 15) = 289 + 3 + annotationHeight = 510
324      * make canvas height 2 of these plus 3 charHeights 
325      * so just enough to draw 2 widths, gap + scale + the first sequence of a third
326      */
327     canvasHeight = charHeight * (17 * 2 + 3)
328             + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP);
329     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
330     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
331   
332     /*
333      * reduce canvas height by 1 pixel - should not be enough height
334      * to draw 3 widths
335      */
336     canvasHeight -= 1;
337     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
338     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
339   
340     /*
341      * turn off scale above - can now fit in 2 and a bit widths
342      */
343     av.setScaleAboveWrapped(false);
344     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
345     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
346   
347     /*
348      * reduce height to enough for 2 widths and not quite a third
349      * i.e. two repeating heights + spacer + sequence - 1 pixel
350      */
351     canvasHeight = charHeight * (16 * 2 + 2)
352             + 2 * (annotationHeight + SeqCanvas.SEQS_ANNOTATION_GAP) - 1;
353     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
354     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 2);
355
356     /*
357      * add 1 pixel to height - should now get 3 widths drawn
358      */
359     canvasHeight += 1;
360     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
361     assertEquals(PA.getValue(testee, "wrappedVisibleWidths"), 3);
362   }
363
364   /**
365    * Test simulates loading an unwrapped alignment, shrinking it vertically so
366    * not all sequences are visible, then changing to wrapped mode. The ranges
367    * endSeq should be unchanged, but the vertical repeat height should include
368    * all sequences.
369    */
370   @Test(groups = "Functional")
371   public void testCalculateWrappedGeometry_fromScrolled()
372   {
373     AlignFrame af = new FileLoader().LoadFileWaitTillLoaded(
374             "examples/uniref50.fa", DataSourceType.FILE);
375     AlignViewport av = af.getViewport();
376     AlignmentI al = av.getAlignment();
377     assertEquals(al.getWidth(), 157);
378     assertEquals(al.getHeight(), 15);
379     av.getRanges().setStartEndSeq(0, 3);
380     av.setShowAnnotation(false);
381     av.setScaleAboveWrapped(true);
382
383     SeqCanvas testee = af.alignPanel.getSeqPanel().seqCanvas;
384     av.setWrapAlignment(true);
385     av.setFont(new Font("SansSerif", Font.PLAIN, 14), true);
386     int charHeight = av.getCharHeight();
387     int charWidth = av.getCharWidth();
388     // Windows h=19, w=11; Mac (and Linux?) 17,11
389     Assert.assertTrue(charHeight == 17 && charWidth == 12
390             || charHeight == 19 && charWidth == 11,
391             "char height/width " + charHeight + "/" + charWidth);
392     int canvasWidth = 400;
393     int canvasHeight = 300;
394     testee.calculateWrappedGeometry(canvasWidth, canvasHeight);
395     int repeatingHeight = (int) PA.getValue(testee,
396             "wrappedRepeatHeightPx");
397     assertEquals(av.getRanges().getEndSeq(), 3,
398             "endSeq should be 3; av.getRanges=" + av.getRanges()); // unchanged
399     assertEquals(repeatingHeight, charHeight * (2 + al.getHeight()));
400   }
401 }