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