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.
21 package jalview.schemes;
23 import static org.testng.AssertJUnit.assertEquals;
24 import static org.testng.AssertJUnit.assertFalse;
25 import static org.testng.AssertJUnit.assertNull;
26 import static org.testng.AssertJUnit.assertTrue;
27 import static org.testng.AssertJUnit.fail;
28 import static org.testng.internal.junit.ArrayAsserts.assertArrayEquals;
30 import jalview.datamodel.SequenceFeature;
31 import jalview.gui.JvOptionPane;
32 import jalview.util.ColorUtils;
33 import jalview.util.Format;
35 import java.awt.Color;
37 import org.testng.annotations.BeforeClass;
38 import org.testng.annotations.Test;
40 import junit.extensions.PA;
42 public class FeatureColourTest
45 @BeforeClass(alwaysRun = true)
46 public void setUpJvOptionPane()
48 JvOptionPane.setInteractiveMode(false);
49 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
52 @Test(groups = { "Functional" })
53 public void testCopyConstructor()
58 FeatureColour fc = new FeatureColour(Color.RED);
59 FeatureColour fc1 = new FeatureColour(fc);
60 assertTrue(fc1.getColour().equals(Color.RED));
61 assertFalse(fc1.isGraduatedColour());
62 assertFalse(fc1.isColourByLabel());
63 assertFalse(fc1.isColourByAttribute());
64 assertNull(fc1.getAttributeName());
69 fc = new FeatureColour(Color.gray, Color.black, 10f, 20f);
70 fc.setAboveThreshold(true);
72 fc1 = new FeatureColour(fc);
73 assertTrue(fc1.isGraduatedColour());
74 assertFalse(fc1.isColourByLabel());
75 assertTrue(fc1.isAboveThreshold());
76 assertFalse(fc1.isColourByAttribute());
77 assertNull(fc1.getAttributeName());
78 assertEquals(12f, fc1.getThreshold());
79 assertEquals(Color.gray, fc1.getMinColour());
80 assertEquals(Color.black, fc1.getMaxColour());
81 assertEquals(Color.gray, fc1.getNoColour());
82 assertEquals(10f, fc1.getMin());
83 assertEquals(20f, fc1.getMax());
86 * min-max-noValue colour
88 fc = new FeatureColour(Color.gray, Color.black, Color.green, 10f, 20f);
89 fc.setAboveThreshold(true);
91 fc1 = new FeatureColour(fc);
92 assertTrue(fc1.isGraduatedColour());
93 assertFalse(fc1.isColourByLabel());
94 assertFalse(fc1.isColourByAttribute());
95 assertNull(fc1.getAttributeName());
96 assertTrue(fc1.isAboveThreshold());
97 assertEquals(12f, fc1.getThreshold());
98 assertEquals(Color.gray, fc1.getMinColour());
99 assertEquals(Color.black, fc1.getMaxColour());
100 assertEquals(Color.green, fc1.getNoColour());
101 assertEquals(10f, fc1.getMin());
102 assertEquals(20f, fc1.getMax());
107 fc = new FeatureColour();
108 fc.setColourByLabel(true);
109 fc1 = new FeatureColour(fc);
110 assertTrue(fc1.isColourByLabel());
111 assertFalse(fc1.isGraduatedColour());
112 assertFalse(fc1.isColourByAttribute());
113 assertNull(fc1.getAttributeName());
116 * colour by attribute (label)
118 fc = new FeatureColour();
119 fc.setColourByLabel(true);
120 fc.setAttributeName("AF");
121 fc1 = new FeatureColour(fc);
122 assertTrue(fc1.isColourByLabel());
123 assertFalse(fc1.isGraduatedColour());
124 assertTrue(fc1.isColourByAttribute());
125 assertArrayEquals(new String[] { "AF" }, fc1.getAttributeName());
128 * colour by attribute (value)
130 fc = new FeatureColour(Color.gray, Color.black, Color.green, 10f, 20f);
131 fc.setAboveThreshold(true);
132 fc.setThreshold(12f);
133 fc.setAttributeName("AF");
134 fc1 = new FeatureColour(fc);
135 assertTrue(fc1.isGraduatedColour());
136 assertFalse(fc1.isColourByLabel());
137 assertTrue(fc1.isColourByAttribute());
138 assertArrayEquals(new String[] { "AF" }, fc1.getAttributeName());
139 assertTrue(fc1.isAboveThreshold());
140 assertEquals(12f, fc1.getThreshold());
141 assertEquals(Color.gray, fc1.getMinColour());
142 assertEquals(Color.black, fc1.getMaxColour());
143 assertEquals(Color.green, fc1.getNoColour());
144 assertEquals(10f, fc1.getMin());
145 assertEquals(20f, fc1.getMax());
148 @Test(groups = { "Functional" })
149 public void testCopyConstructor_minMax()
154 FeatureColour fc = new FeatureColour(Color.BLUE, Color.RED, 1f, 5f);
155 assertTrue(fc.isGraduatedColour());
156 assertFalse(fc.isColourByLabel());
157 assertFalse(fc.isColourByAttribute());
158 assertNull(fc.getAttributeName());
159 assertEquals(1f, fc.getMin());
160 assertEquals(5f, fc.getMax());
163 * update min-max bounds
165 FeatureColour fc1 = new FeatureColour(fc, 2f, 6f);
166 assertTrue(fc1.isGraduatedColour());
167 assertFalse(fc1.isColourByLabel());
168 assertFalse(fc1.isColourByAttribute());
169 assertNull(fc1.getAttributeName());
170 assertEquals(2f, fc1.getMin());
171 assertEquals(6f, fc1.getMax());
172 assertFalse((boolean) PA.getValue(fc1, "isHighToLow"));
175 * update min-max bounds - high to low
177 fc1 = new FeatureColour(fc, 23f, 16f);
178 assertTrue(fc1.isGraduatedColour());
179 assertFalse(fc1.isColourByLabel());
180 assertFalse(fc1.isColourByAttribute());
181 assertNull(fc1.getAttributeName());
182 assertEquals(23f, fc1.getMin());
183 assertEquals(16f, fc1.getMax());
184 assertTrue((boolean) PA.getValue(fc1, "isHighToLow"));
187 * graduated colour by attribute
189 fc1.setAttributeName("AF");
190 fc1 = new FeatureColour(fc1, 13f, 36f);
191 assertTrue(fc1.isGraduatedColour());
192 assertFalse(fc1.isColourByLabel());
193 assertTrue(fc1.isColourByAttribute());
194 assertEquals(new String[] { "AF" }, fc1.getAttributeName());
195 assertEquals(13f, fc1.getMin());
196 assertEquals(36f, fc1.getMax());
197 assertFalse((boolean) PA.getValue(fc1, "isHighToLow"));
202 fc = new FeatureColour(Color.BLUE, Color.RED, 1f, 5f);
203 fc.setColourByLabel(true);
204 assertFalse(fc.isGraduatedColour());
205 assertTrue(fc.isColourByLabel());
206 assertFalse(fc.isColourByAttribute());
207 assertNull(fc.getAttributeName());
208 assertEquals(1f, fc.getMin());
209 assertEquals(5f, fc.getMax());
212 * update min-max bounds
214 fc1 = new FeatureColour(fc, 2f, 6f);
215 assertFalse(fc1.isGraduatedColour());
216 assertTrue(fc1.isColourByLabel());
217 assertFalse(fc1.isColourByAttribute());
218 assertNull(fc1.getAttributeName());
219 assertEquals(2f, fc1.getMin());
220 assertEquals(6f, fc1.getMax());
223 * colour by attribute text
225 fc1.setAttributeName("AC");
226 fc1 = new FeatureColour(fc1, 13f, 36f);
227 assertFalse(fc1.isGraduatedColour());
228 assertTrue(fc1.isColourByLabel());
229 assertTrue(fc1.isColourByAttribute());
230 assertEquals("AC", fc1.getAttributeName());
231 assertEquals(13f, fc1.getMin());
232 assertEquals(36f, fc1.getMax());
235 @Test(groups = { "Functional" })
236 public void testGetColor_simpleColour()
238 FeatureColour fc = new FeatureColour(Color.RED);
239 assertEquals(Color.RED,
240 fc.getColor(new SequenceFeature("Cath", "", 1, 2, 0f, null)));
243 @Test(groups = { "Functional" })
244 public void testGetColor_colourByLabel()
246 FeatureColour fc = new FeatureColour();
247 fc.setColourByLabel(true);
248 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 1f,
250 Color expected = ColorUtils.createColourFromName("desc");
251 assertEquals(expected, fc.getColor(sf));
254 @Test(groups = { "Functional" })
255 public void testGetColor_Graduated()
258 * graduated colour from
260 * gray(128, 128, 128) to red(255, 0, 0)
262 FeatureColour fc = new FeatureColour(Color.GRAY, Color.RED, 0f, 100f);
263 // feature score is 75 which is 3/4 of the way from GRAY to RED
264 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 75f,
266 // the colour gradient is computed in float values from 0-1 (where 1 == 255)
267 float red = 128 / 255f + 3 / 4f * (255 - 128) / 255f;
268 float green = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
269 float blue = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
270 Color expected = new Color(red, green, blue);
271 assertEquals(expected, fc.getColor(sf));
274 @Test(groups = { "Functional" })
275 public void testGetColor_aboveBelowThreshold()
277 // gradient from [50, 150] from WHITE(255, 255, 255) to BLACK(0, 0, 0)
278 FeatureColour fc = new FeatureColour(Color.WHITE, Color.BLACK, 50f,
280 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 70f,
284 * feature with score of Float.NaN is always assigned minimum colour
286 SequenceFeature sf2 = new SequenceFeature("type", "desc", 0, 20,
289 fc.setThreshold(100f); // ignore for now
290 assertEquals(new Color(204, 204, 204), fc.getColor(sf));
291 assertEquals(Color.white, fc.getColor(sf2));
293 fc.setAboveThreshold(true); // feature lies below threshold
294 assertNull(fc.getColor(sf));
295 assertEquals(Color.white, fc.getColor(sf2));
297 fc.setBelowThreshold(true);
298 fc.setThreshold(70f);
299 assertNull(fc.getColor(sf)); // feature score == threshold - hidden
300 assertEquals(Color.white, fc.getColor(sf2));
301 fc.setThreshold(69f);
302 assertNull(fc.getColor(sf)); // feature score > threshold - hidden
303 assertEquals(Color.white, fc.getColor(sf2));
307 * Test output of feature colours to Jalview features file format
309 @Test(groups = { "Functional" })
310 public void testToJalviewFormat()
313 * plain colour - to RGB hex code
315 FeatureColour fc = new FeatureColour(Color.RED);
316 String redHex = Format.getHexString(Color.RED);
317 String hexColour = redHex;
318 assertEquals("domain\t" + hexColour, fc.toJalviewFormat("domain"));
321 * colour by label (no threshold)
323 fc = new FeatureColour();
324 fc.setColourByLabel(true);
325 assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
328 * colour by label (autoscaled) (an odd state you can reach by selecting
329 * 'above threshold', then deselecting 'threshold is min/max' then 'colour
332 fc.setAutoScaled(true);
333 assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
336 * colour by label (above threshold) (min/max values are output though not
337 * used by this scheme)
339 fc.setAutoScaled(false);
340 fc.setThreshold(12.5f);
341 fc.setAboveThreshold(true);
342 assertEquals("domain\tlabel|||0.0|0.0|above|12.5",
343 fc.toJalviewFormat("domain"));
346 * colour by label (below threshold)
348 fc.setBelowThreshold(true);
349 assertEquals("domain\tlabel|||0.0|0.0|below|12.5",
350 fc.toJalviewFormat("domain"));
353 * graduated colour, no threshold
355 fc = new FeatureColour(Color.GREEN, Color.RED, 12f, 25f);
356 String greenHex = Format.getHexString(Color.GREEN);
357 String expected = String.format("domain\t%s|%s|abso|12.0|25.0|none",
359 assertEquals(expected, fc.toJalviewFormat("domain"));
362 * colour ranges over the actual score ranges (not min/max)
364 fc.setAutoScaled(true);
365 expected = String.format("domain\t%s|%s|12.0|25.0|none", greenHex,
367 assertEquals(expected, fc.toJalviewFormat("domain"));
370 * graduated colour below threshold
372 fc.setThreshold(12.5f);
373 fc.setBelowThreshold(true);
374 expected = String.format("domain\t%s|%s|12.0|25.0|below|12.5",
376 assertEquals(expected, fc.toJalviewFormat("domain"));
379 * graduated colour above threshold
381 fc.setThreshold(12.5f);
382 fc.setAboveThreshold(true);
383 fc.setAutoScaled(false);
384 expected = String.format("domain\t%s|%s|abso|12.0|25.0|above|12.5",
386 assertEquals(expected, fc.toJalviewFormat("domain"));
390 * Test parsing of feature colours from Jalview features file format
392 @Test(groups = { "Functional" })
393 public void testParseJalviewFeatureColour()
396 * simple colour by name
398 FeatureColour fc = FeatureColour.parseJalviewFeatureColour("red");
399 assertTrue(fc.isSimpleColour());
400 assertEquals(Color.RED, fc.getColour());
403 * simple colour by hex code
405 fc = FeatureColour.parseJalviewFeatureColour(Format
406 .getHexString(Color.RED));
407 assertTrue(fc.isSimpleColour());
408 assertEquals(Color.RED, fc.getColour());
411 * simple colour by rgb triplet
413 fc = FeatureColour.parseJalviewFeatureColour("255,0,0");
414 assertTrue(fc.isSimpleColour());
415 assertEquals(Color.RED, fc.getColour());
422 fc = FeatureColour.parseJalviewFeatureColour("oops");
423 fail("expected exception");
424 } catch (IllegalArgumentException e)
426 assertEquals("Invalid colour descriptor: oops", e.getMessage());
430 * colour by label (no threshold)
432 fc = FeatureColour.parseJalviewFeatureColour("label");
433 assertTrue(fc.isColourByLabel());
434 assertFalse(fc.hasThreshold());
437 * colour by label (with threshold)
440 .parseJalviewFeatureColour("label|||0.0|0.0|above|12.0");
441 assertTrue(fc.isColourByLabel());
442 assertTrue(fc.isAboveThreshold());
443 assertEquals(12.0f, fc.getThreshold());
446 * graduated colour (by name) (no threshold)
448 fc = FeatureColour.parseJalviewFeatureColour("red|green|10.0|20.0");
449 assertTrue(fc.isGraduatedColour());
450 assertFalse(fc.hasThreshold());
451 assertEquals(Color.RED, fc.getMinColour());
452 assertEquals(Color.GREEN, fc.getMaxColour());
453 assertEquals(10f, fc.getMin());
454 assertEquals(20f, fc.getMax());
455 assertTrue(fc.isAutoScaled());
458 * graduated colour (by hex code) (above threshold)
460 String descriptor = String.format("%s|%s|10.0|20.0|above|15",
461 Format.getHexString(Color.RED),
462 Format.getHexString(Color.GREEN));
463 fc = FeatureColour.parseJalviewFeatureColour(descriptor);
464 assertTrue(fc.isGraduatedColour());
465 assertTrue(fc.hasThreshold());
466 assertTrue(fc.isAboveThreshold());
467 assertEquals(15f, fc.getThreshold());
468 assertEquals(Color.RED, fc.getMinColour());
469 assertEquals(Color.GREEN, fc.getMaxColour());
470 assertEquals(10f, fc.getMin());
471 assertEquals(20f, fc.getMax());
472 assertTrue(fc.isAutoScaled());
475 * graduated colour (by RGB triplet) (below threshold), absolute scale
477 descriptor = String.format("255,0,0|0,255,0|abso|10.0|20.0|below|15");
478 fc = FeatureColour.parseJalviewFeatureColour(descriptor);
479 assertTrue(fc.isGraduatedColour());
480 assertFalse(fc.isAutoScaled());
481 assertTrue(fc.hasThreshold());
482 assertTrue(fc.isBelowThreshold());
483 assertEquals(15f, fc.getThreshold());
484 assertEquals(Color.RED, fc.getMinColour());
485 assertEquals(Color.GREEN, fc.getMaxColour());
486 assertEquals(10f, fc.getMin());
487 assertEquals(20f, fc.getMax());
490 .format("blue|255,0,255|absolute|20.0|95.0|below|66.0");
491 fc = FeatureColour.parseJalviewFeatureColour(descriptor);
492 assertTrue(fc.isGraduatedColour());
495 @Test(groups = { "Functional" })
496 public void testGetColor_colourByAttributeText()
498 FeatureColour fc = new FeatureColour();
499 fc.setColourByLabel(true);
500 fc.setAttributeName("consequence");
501 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 1f,
505 * if feature has no such attribute, use 'no value' colour
507 assertEquals(FeatureColour.DEFAULT_NO_COLOUR, fc.getColor(sf));
510 * if feature has attribute, generate colour from value
512 sf.setValue("consequence", "benign");
513 Color expected = ColorUtils.createColourFromName("benign");
514 assertEquals(expected, fc.getColor(sf));
517 @Test(groups = { "Functional" })
518 public void testGetColor_GraduatedByAttributeValue()
521 * graduated colour based on attribute value for AF
522 * given a min-max range of 0-100
524 FeatureColour fc = new FeatureColour(new Color(50, 100, 150),
525 new Color(150, 200, 250), Color.yellow, 0f, 100f);
526 String attName = "AF";
527 fc.setAttributeName(attName);
530 * first case: feature lacks the attribute - use 'no value' colour
532 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 75f,
534 assertEquals(Color.yellow, fc.getColor(sf));
537 * second case: attribute present but not numeric - treat as if absent
539 sf.setValue(attName, "twelve");
540 assertEquals(Color.yellow, fc.getColor(sf));
543 * third case: valid attribute value
545 sf.setValue(attName, "20.0");
546 Color expected = new Color(70, 120, 170);
547 assertEquals(expected, fc.getColor(sf));