c16d5411f6a6313647696dfb38add903aea7620e
[jalview.git] / test / jalview / schemes / FeatureColourTest.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.schemes;
22
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertTrue;
26 import static org.testng.AssertJUnit.fail;
27
28 import jalview.datamodel.SequenceFeature;
29 import jalview.gui.JvOptionPane;
30 import jalview.util.ColorUtils;
31 import jalview.util.Format;
32
33 import java.awt.Color;
34
35 import org.testng.annotations.BeforeClass;
36 import org.testng.annotations.Test;
37
38 public class FeatureColourTest
39 {
40
41   @BeforeClass(alwaysRun = true)
42   public void setUpJvOptionPane()
43   {
44     JvOptionPane.setInteractiveMode(false);
45     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
46   }
47
48   @Test(groups = { "Functional" })
49   public void testCopyConstructor()
50   {
51     /*
52      * plain colour
53      */
54     FeatureColour fc = new FeatureColour(Color.RED);
55     FeatureColour fc1 = new FeatureColour(fc);
56     assertTrue(fc1.getColour().equals(Color.RED));
57     assertFalse(fc1.isGraduatedColour());
58     assertFalse(fc1.isColourByLabel());
59
60     /*
61      * min-max colour
62      */
63     fc = new FeatureColour(Color.gray, Color.black, 10f, 20f);
64     fc.setAboveThreshold(true);
65     fc.setThreshold(12f);
66     fc1 = new FeatureColour(fc);
67     assertTrue(fc1.isGraduatedColour());
68     assertFalse(fc1.isColourByLabel());
69     assertTrue(fc1.isAboveThreshold());
70     assertEquals(12f, fc1.getThreshold());
71     assertEquals(Color.gray, fc1.getMinColour());
72     assertEquals(Color.black, fc1.getMaxColour());
73     assertEquals(10f, fc1.getMin());
74     assertEquals(20f, fc1.getMax());
75
76     /*
77      * colour by label
78      */
79     fc = new FeatureColour();
80     fc.setColourByLabel(true);
81     fc1 = new FeatureColour(fc);
82     assertTrue(fc1.isColourByLabel());
83     assertFalse(fc1.isGraduatedColour());
84   }
85
86   @Test(groups = { "Functional" })
87   public void testIsColored_simpleColour()
88   {
89     FeatureColour fc = new FeatureColour(Color.RED);
90     assertTrue(fc.isColored(new SequenceFeature()));
91   }
92
93   @Test(groups = { "Functional" })
94   public void testIsColored_colourByLabel()
95   {
96     FeatureColour fc = new FeatureColour();
97     fc.setColourByLabel(true);
98     assertTrue(fc.isColored(new SequenceFeature()));
99   }
100
101   @Test(groups = { "Functional" })
102   public void testIsColored_aboveThreshold()
103   {
104     // graduated colour range from score 20 to 100
105     FeatureColour fc = new FeatureColour(Color.WHITE, Color.BLACK, 20f,
106             100f);
107
108     // score 0 is adjusted to bottom of range
109     SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 0f,
110             null);
111     assertTrue(fc.isColored(sf));
112     assertEquals(Color.WHITE, fc.getColor(sf));
113
114     // score 120 is adjusted to top of range
115     sf.setScore(120f);
116     assertEquals(Color.BLACK, fc.getColor(sf));
117
118     // value below threshold is still rendered
119     // setting threshold has no effect yet...
120     fc.setThreshold(60f);
121     sf.setScore(36f);
122     assertTrue(fc.isColored(sf));
123     assertEquals(new Color(204, 204, 204), fc.getColor(sf));
124
125     // now apply threshold:
126     fc.setAboveThreshold(true);
127     assertFalse(fc.isColored(sf));
128     // colour is still returned though ?!?
129     assertEquals(new Color(204, 204, 204), fc.getColor(sf));
130
131     sf.setScore(84); // above threshold now
132     assertTrue(fc.isColored(sf));
133     assertEquals(new Color(51, 51, 51), fc.getColor(sf));
134   }
135
136   @Test(groups = { "Functional" })
137   public void testGetColor_simpleColour()
138   {
139     FeatureColour fc = new FeatureColour(Color.RED);
140     assertEquals(Color.RED, fc.getColor(new SequenceFeature()));
141   }
142
143   @Test(groups = { "Functional" })
144   public void testGetColor_colourByLabel()
145   {
146     FeatureColour fc = new FeatureColour();
147     fc.setColourByLabel(true);
148     SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 1f,
149             null);
150     Color expected = ColorUtils.createColourFromName("desc");
151     assertEquals(expected, fc.getColor(sf));
152   }
153
154   @Test(groups = { "Functional" })
155   public void testGetColor_Graduated()
156   {
157     // graduated colour from score 0 to 100, gray(128, 128, 128) to red(255, 0,
158     // 0)
159     FeatureColour fc = new FeatureColour(Color.GRAY, Color.RED, 0f, 100f);
160     // feature score is 75 which is 3/4 of the way from GRAY to RED
161     SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 75f,
162             null);
163     // the colour gradient is computed in float values from 0-1 (where 1 == 255)
164     float red = 128 / 255f + 3 / 4f * (255 - 128) / 255f;
165     float green = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
166     float blue = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
167     Color expected = new Color(red, green, blue);
168     assertEquals(expected, fc.getColor(sf));
169   }
170
171   @Test(groups = { "Functional" })
172   public void testGetColor_belowThreshold()
173   {
174     // gradient from [50, 150] from WHITE(255, 255, 255) to BLACK(0, 0, 0)
175     FeatureColour fc = new FeatureColour(Color.WHITE, Color.BLACK, 50f,
176             150f);
177     SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 70f,
178             null);
179     fc.setThreshold(100f); // ignore for now
180     assertTrue(fc.isColored(sf));
181     assertEquals(new Color(204, 204, 204), fc.getColor(sf));
182
183     fc.setAboveThreshold(true); // feature lies below threshold
184     assertFalse(fc.isColored(sf));
185     assertEquals(new Color(204, 204, 204), fc.getColor(sf));
186   }
187
188   /**
189    * Test output of feature colours to Jalview features file format
190    */
191   @Test(groups = { "Functional" })
192   public void testToJalviewFormat()
193   {
194     /*
195      * plain colour - to RGB hex code
196      */
197     FeatureColour fc = new FeatureColour(Color.RED);
198     String redHex = Format.getHexString(Color.RED);
199     String hexColour = redHex;
200     assertEquals("domain\t" + hexColour, fc.toJalviewFormat("domain"));
201
202     /*
203      * colour by label (no threshold)
204      */
205     fc = new FeatureColour();
206     fc.setColourByLabel(true);
207     assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
208
209     /*
210      * colour by label (autoscaled) (an odd state you can reach by selecting
211      * 'above threshold', then deselecting 'threshold is min/max' then 'colour
212      * by label')
213      */
214     fc.setAutoScaled(true);
215     assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
216
217     /*
218      * colour by label (above threshold) (min/max values are output though not
219      * used by this scheme)
220      */
221     fc.setAutoScaled(false);
222     fc.setThreshold(12.5f);
223     fc.setAboveThreshold(true);
224     assertEquals("domain\tlabel|||0.0|0.0|above|12.5",
225             fc.toJalviewFormat("domain"));
226
227     /*
228      * colour by label (below threshold)
229      */
230     fc.setBelowThreshold(true);
231     assertEquals("domain\tlabel|||0.0|0.0|below|12.5",
232             fc.toJalviewFormat("domain"));
233
234     /*
235      * graduated colour, no threshold
236      */
237     fc = new FeatureColour(Color.GREEN, Color.RED, 12f, 25f);
238     String greenHex = Format.getHexString(Color.GREEN);
239     String expected = String.format("domain\t%s|%s|abso|12.0|25.0|none",
240             greenHex, redHex);
241     assertEquals(expected, fc.toJalviewFormat("domain"));
242
243     /*
244      * colour ranges over the actual score ranges (not min/max)
245      */
246     fc.setAutoScaled(true);
247     expected = String.format("domain\t%s|%s|12.0|25.0|none", greenHex,
248             redHex);
249     assertEquals(expected, fc.toJalviewFormat("domain"));
250
251     /*
252      * graduated colour below threshold
253      */
254     fc.setThreshold(12.5f);
255     fc.setBelowThreshold(true);
256     expected = String.format("domain\t%s|%s|12.0|25.0|below|12.5",
257             greenHex, redHex);
258     assertEquals(expected, fc.toJalviewFormat("domain"));
259
260     /*
261      * graduated colour above threshold
262      */
263     fc.setThreshold(12.5f);
264     fc.setAboveThreshold(true);
265     fc.setAutoScaled(false);
266     expected = String.format("domain\t%s|%s|abso|12.0|25.0|above|12.5",
267             greenHex, redHex);
268     assertEquals(expected, fc.toJalviewFormat("domain"));
269   }
270
271   /**
272    * Test parsing of feature colours from Jalview features file format
273    */
274   @Test(groups = { "Functional" })
275   public void testParseJalviewFeatureColour()
276   {
277     /*
278      * simple colour by name
279      */
280     FeatureColour fc = FeatureColour.parseJalviewFeatureColour("red");
281     assertTrue(fc.isSimpleColour());
282     assertEquals(Color.RED, fc.getColour());
283
284     /*
285      * simple colour by hex code
286      */
287     fc = FeatureColour.parseJalviewFeatureColour(Format
288             .getHexString(Color.RED));
289     assertTrue(fc.isSimpleColour());
290     assertEquals(Color.RED, fc.getColour());
291
292     /*
293      * simple colour by rgb triplet
294      */
295     fc = FeatureColour.parseJalviewFeatureColour("255,0,0");
296     assertTrue(fc.isSimpleColour());
297     assertEquals(Color.RED, fc.getColour());
298
299     /*
300      * malformed colour
301      */
302     try
303     {
304       fc = FeatureColour.parseJalviewFeatureColour("oops");
305       fail("expected exception");
306     } catch (IllegalArgumentException e)
307     {
308       assertEquals("Invalid colour descriptor: oops", e.getMessage());
309     }
310
311     /*
312      * colour by label (no threshold)
313      */
314     fc = FeatureColour.parseJalviewFeatureColour("label");
315     assertTrue(fc.isColourByLabel());
316     assertFalse(fc.hasThreshold());
317
318     /*
319      * colour by label (with threshold)
320      */
321     fc = FeatureColour
322             .parseJalviewFeatureColour("label|||0.0|0.0|above|12.0");
323     assertTrue(fc.isColourByLabel());
324     assertTrue(fc.isAboveThreshold());
325     assertEquals(12.0f, fc.getThreshold());
326
327     /*
328      * graduated colour (by name) (no threshold)
329      */
330     fc = FeatureColour.parseJalviewFeatureColour("red|green|10.0|20.0");
331     assertTrue(fc.isGraduatedColour());
332     assertFalse(fc.hasThreshold());
333     assertEquals(Color.RED, fc.getMinColour());
334     assertEquals(Color.GREEN, fc.getMaxColour());
335     assertEquals(10f, fc.getMin());
336     assertEquals(20f, fc.getMax());
337     assertTrue(fc.isAutoScaled());
338
339     /*
340      * graduated colour (by hex code) (above threshold)
341      */
342     String descriptor = String.format("%s|%s|10.0|20.0|above|15",
343             Format.getHexString(Color.RED),
344             Format.getHexString(Color.GREEN));
345     fc = FeatureColour.parseJalviewFeatureColour(descriptor);
346     assertTrue(fc.isGraduatedColour());
347     assertTrue(fc.hasThreshold());
348     assertTrue(fc.isAboveThreshold());
349     assertEquals(15f, fc.getThreshold());
350     assertEquals(Color.RED, fc.getMinColour());
351     assertEquals(Color.GREEN, fc.getMaxColour());
352     assertEquals(10f, fc.getMin());
353     assertEquals(20f, fc.getMax());
354     assertTrue(fc.isAutoScaled());
355
356     /*
357      * graduated colour (by RGB triplet) (below threshold), absolute scale
358      */
359     descriptor = String.format("255,0,0|0,255,0|abso|10.0|20.0|below|15");
360     fc = FeatureColour.parseJalviewFeatureColour(descriptor);
361     assertTrue(fc.isGraduatedColour());
362     assertFalse(fc.isAutoScaled());
363     assertTrue(fc.hasThreshold());
364     assertTrue(fc.isBelowThreshold());
365     assertEquals(15f, fc.getThreshold());
366     assertEquals(Color.RED, fc.getMinColour());
367     assertEquals(Color.GREEN, fc.getMaxColour());
368     assertEquals(10f, fc.getMin());
369     assertEquals(20f, fc.getMax());
370
371     descriptor = String
372             .format("blue|255,0,255|absolute|20.0|95.0|below|66.0");
373     fc = FeatureColour.parseJalviewFeatureColour(descriptor);
374     assertTrue(fc.isGraduatedColour());
375   }
376 }