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;
29 import jalview.datamodel.SequenceFeature;
30 import jalview.gui.JvOptionPane;
31 import jalview.util.ColorUtils;
32 import jalview.util.Format;
34 import java.awt.Color;
36 import junit.extensions.PA;
38 import org.testng.annotations.BeforeClass;
39 import org.testng.annotations.Test;
41 public class FeatureColourTest
44 @BeforeClass(alwaysRun = true)
45 public void setUpJvOptionPane()
47 JvOptionPane.setInteractiveMode(false);
48 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
51 @Test(groups = { "Functional" })
52 public void testCopyConstructor()
57 FeatureColour fc = new FeatureColour(Color.RED);
58 FeatureColour fc1 = new FeatureColour(fc);
59 assertTrue(fc1.getColour().equals(Color.RED));
60 assertFalse(fc1.isGraduatedColour());
61 assertFalse(fc1.isColourByLabel());
62 assertFalse(fc1.isColourByAttribute());
63 assertNull(fc1.getAttributeName());
68 fc = new FeatureColour(Color.gray, Color.black, 10f, 20f);
69 fc.setAboveThreshold(true);
71 fc1 = new FeatureColour(fc);
72 assertTrue(fc1.isGraduatedColour());
73 assertFalse(fc1.isColourByLabel());
74 assertTrue(fc1.isAboveThreshold());
75 assertFalse(fc1.isColourByAttribute());
76 assertNull(fc1.getAttributeName());
77 assertEquals(12f, fc1.getThreshold());
78 assertEquals(Color.gray, fc1.getMinColour());
79 assertEquals(Color.black, fc1.getMaxColour());
80 assertEquals(Color.gray, fc1.getNoColour());
81 assertEquals(10f, fc1.getMin());
82 assertEquals(20f, fc1.getMax());
85 * min-max-noValue colour
87 fc = new FeatureColour(Color.gray, Color.black, Color.green, 10f, 20f);
88 fc.setAboveThreshold(true);
90 fc1 = new FeatureColour(fc);
91 assertTrue(fc1.isGraduatedColour());
92 assertFalse(fc1.isColourByLabel());
93 assertFalse(fc1.isColourByAttribute());
94 assertNull(fc1.getAttributeName());
95 assertTrue(fc1.isAboveThreshold());
96 assertEquals(12f, fc1.getThreshold());
97 assertEquals(Color.gray, fc1.getMinColour());
98 assertEquals(Color.black, fc1.getMaxColour());
99 assertEquals(Color.green, fc1.getNoColour());
100 assertEquals(10f, fc1.getMin());
101 assertEquals(20f, fc1.getMax());
106 fc = new FeatureColour();
107 fc.setColourByLabel(true);
108 fc1 = new FeatureColour(fc);
109 assertTrue(fc1.isColourByLabel());
110 assertFalse(fc1.isGraduatedColour());
111 assertFalse(fc1.isColourByAttribute());
112 assertNull(fc1.getAttributeName());
115 * colour by attribute (label)
117 fc = new FeatureColour();
118 fc.setColourByLabel(true);
119 fc.setAttributeName("AF");
120 fc1 = new FeatureColour(fc);
121 assertTrue(fc1.isColourByLabel());
122 assertFalse(fc1.isGraduatedColour());
123 assertTrue(fc1.isColourByAttribute());
124 assertEquals("AF", fc1.getAttributeName());
127 * colour by attribute (value)
129 fc = new FeatureColour(Color.gray, Color.black, Color.green, 10f, 20f);
130 fc.setAboveThreshold(true);
131 fc.setThreshold(12f);
132 fc.setAttributeName("AF");
133 fc1 = new FeatureColour(fc);
134 assertTrue(fc1.isGraduatedColour());
135 assertFalse(fc1.isColourByLabel());
136 assertTrue(fc1.isColourByAttribute());
137 assertEquals("AF", fc1.getAttributeName());
138 assertTrue(fc1.isAboveThreshold());
139 assertEquals(12f, fc1.getThreshold());
140 assertEquals(Color.gray, fc1.getMinColour());
141 assertEquals(Color.black, fc1.getMaxColour());
142 assertEquals(Color.green, fc1.getNoColour());
143 assertEquals(10f, fc1.getMin());
144 assertEquals(20f, fc1.getMax());
147 @Test(groups = { "Functional" })
148 public void testCopyConstructor_minMax()
153 FeatureColour fc = new FeatureColour(Color.BLUE, Color.RED, 1f, 5f);
154 assertTrue(fc.isGraduatedColour());
155 assertFalse(fc.isColourByLabel());
156 assertFalse(fc.isColourByAttribute());
157 assertNull(fc.getAttributeName());
158 assertEquals(1f, fc.getMin());
159 assertEquals(5f, fc.getMax());
162 * update min-max bounds
164 FeatureColour fc1 = new FeatureColour(fc, 2f, 6f);
165 assertTrue(fc1.isGraduatedColour());
166 assertFalse(fc1.isColourByLabel());
167 assertFalse(fc1.isColourByAttribute());
168 assertNull(fc1.getAttributeName());
169 assertEquals(2f, fc1.getMin());
170 assertEquals(6f, fc1.getMax());
171 assertFalse((boolean) PA.getValue(fc1, "isHighToLow"));
174 * update min-max bounds - high to low
176 fc1 = new FeatureColour(fc, 23f, 16f);
177 assertTrue(fc1.isGraduatedColour());
178 assertFalse(fc1.isColourByLabel());
179 assertFalse(fc1.isColourByAttribute());
180 assertNull(fc1.getAttributeName());
181 assertEquals(23f, fc1.getMin());
182 assertEquals(16f, fc1.getMax());
183 assertTrue((boolean) PA.getValue(fc1, "isHighToLow"));
186 * graduated colour by attribute
188 fc1.setAttributeName("AF");
189 fc1 = new FeatureColour(fc1, 13f, 36f);
190 assertTrue(fc1.isGraduatedColour());
191 assertFalse(fc1.isColourByLabel());
192 assertTrue(fc1.isColourByAttribute());
193 assertEquals("AF", fc1.getAttributeName());
194 assertEquals(13f, fc1.getMin());
195 assertEquals(36f, fc1.getMax());
196 assertFalse((boolean) PA.getValue(fc1, "isHighToLow"));
201 fc = new FeatureColour(Color.BLUE, Color.RED, 1f, 5f);
202 fc.setColourByLabel(true);
203 assertFalse(fc.isGraduatedColour());
204 assertTrue(fc.isColourByLabel());
205 assertFalse(fc.isColourByAttribute());
206 assertNull(fc.getAttributeName());
207 assertEquals(1f, fc.getMin());
208 assertEquals(5f, fc.getMax());
211 * update min-max bounds
213 fc1 = new FeatureColour(fc, 2f, 6f);
214 assertFalse(fc1.isGraduatedColour());
215 assertTrue(fc1.isColourByLabel());
216 assertFalse(fc1.isColourByAttribute());
217 assertNull(fc1.getAttributeName());
218 assertEquals(2f, fc1.getMin());
219 assertEquals(6f, fc1.getMax());
222 * colour by attribute text
224 fc1.setAttributeName("AC");
225 fc1 = new FeatureColour(fc1, 13f, 36f);
226 assertFalse(fc1.isGraduatedColour());
227 assertTrue(fc1.isColourByLabel());
228 assertTrue(fc1.isColourByAttribute());
229 assertEquals("AC", fc1.getAttributeName());
230 assertEquals(13f, fc1.getMin());
231 assertEquals(36f, fc1.getMax());
234 @Test(groups = { "Functional" })
235 public void testGetColor_simpleColour()
237 FeatureColour fc = new FeatureColour(Color.RED);
238 assertEquals(Color.RED,
239 fc.getColor(new SequenceFeature("Cath", "", 1, 2, 0f, null)));
242 @Test(groups = { "Functional" })
243 public void testGetColor_colourByLabel()
245 FeatureColour fc = new FeatureColour();
246 fc.setColourByLabel(true);
247 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 1f,
249 Color expected = ColorUtils.createColourFromName("desc");
250 assertEquals(expected, fc.getColor(sf));
253 @Test(groups = { "Functional" })
254 public void testGetColor_Graduated()
257 * graduated colour from
259 * gray(128, 128, 128) to red(255, 0, 0)
261 FeatureColour fc = new FeatureColour(Color.GRAY, Color.RED, 0f, 100f);
262 // feature score is 75 which is 3/4 of the way from GRAY to RED
263 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 75f,
265 // the colour gradient is computed in float values from 0-1 (where 1 == 255)
266 float red = 128 / 255f + 3 / 4f * (255 - 128) / 255f;
267 float green = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
268 float blue = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
269 Color expected = new Color(red, green, blue);
270 assertEquals(expected, fc.getColor(sf));
273 @Test(groups = { "Functional" })
274 public void testGetColor_aboveBelowThreshold()
276 // gradient from [50, 150] from WHITE(255, 255, 255) to BLACK(0, 0, 0)
277 FeatureColour fc = new FeatureColour(Color.WHITE, Color.BLACK, 50f,
279 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 70f,
283 * feature with score of Float.NaN is always assigned minimum colour
285 SequenceFeature sf2 = new SequenceFeature("type", "desc", 0, 20,
288 fc.setThreshold(100f); // ignore for now
289 assertEquals(new Color(204, 204, 204), fc.getColor(sf));
290 assertEquals(Color.white, fc.getColor(sf2));
292 fc.setAboveThreshold(true); // feature lies below threshold
293 assertNull(fc.getColor(sf));
294 assertEquals(Color.white, fc.getColor(sf2));
296 fc.setBelowThreshold(true);
297 fc.setThreshold(70f);
298 assertNull(fc.getColor(sf)); // feature score == threshold - hidden
299 assertEquals(Color.white, fc.getColor(sf2));
300 fc.setThreshold(69f);
301 assertNull(fc.getColor(sf)); // feature score > threshold - hidden
302 assertEquals(Color.white, fc.getColor(sf2));
306 * Test output of feature colours to Jalview features file format
308 @Test(groups = { "Functional" })
309 public void testToJalviewFormat()
312 * plain colour - to RGB hex code
314 FeatureColour fc = new FeatureColour(Color.RED);
315 String redHex = Format.getHexString(Color.RED);
316 String hexColour = redHex;
317 assertEquals("domain\t" + hexColour, fc.toJalviewFormat("domain"));
320 * colour by label (no threshold)
322 fc = new FeatureColour();
323 fc.setColourByLabel(true);
324 assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
327 * colour by label (autoscaled) (an odd state you can reach by selecting
328 * 'above threshold', then deselecting 'threshold is min/max' then 'colour
331 fc.setAutoScaled(true);
332 assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
335 * colour by label (above threshold) (min/max values are output though not
336 * used by this scheme)
338 fc.setAutoScaled(false);
339 fc.setThreshold(12.5f);
340 fc.setAboveThreshold(true);
341 assertEquals("domain\tlabel|||0.0|0.0|above|12.5",
342 fc.toJalviewFormat("domain"));
345 * colour by label (below threshold)
347 fc.setBelowThreshold(true);
348 assertEquals("domain\tlabel|||0.0|0.0|below|12.5",
349 fc.toJalviewFormat("domain"));
352 * graduated colour, no threshold
354 fc = new FeatureColour(Color.GREEN, Color.RED, 12f, 25f);
355 String greenHex = Format.getHexString(Color.GREEN);
356 String expected = String.format("domain\t%s|%s|abso|12.0|25.0|none",
358 assertEquals(expected, fc.toJalviewFormat("domain"));
361 * colour ranges over the actual score ranges (not min/max)
363 fc.setAutoScaled(true);
364 expected = String.format("domain\t%s|%s|12.0|25.0|none", greenHex,
366 assertEquals(expected, fc.toJalviewFormat("domain"));
369 * graduated colour below threshold
371 fc.setThreshold(12.5f);
372 fc.setBelowThreshold(true);
373 expected = String.format("domain\t%s|%s|12.0|25.0|below|12.5",
375 assertEquals(expected, fc.toJalviewFormat("domain"));
378 * graduated colour above threshold
380 fc.setThreshold(12.5f);
381 fc.setAboveThreshold(true);
382 fc.setAutoScaled(false);
383 expected = String.format("domain\t%s|%s|abso|12.0|25.0|above|12.5",
385 assertEquals(expected, fc.toJalviewFormat("domain"));
389 * Test parsing of feature colours from Jalview features file format
391 @Test(groups = { "Functional" })
392 public void testParseJalviewFeatureColour()
395 * simple colour by name
397 FeatureColour fc = FeatureColour.parseJalviewFeatureColour("red");
398 assertTrue(fc.isSimpleColour());
399 assertEquals(Color.RED, fc.getColour());
402 * simple colour by hex code
404 fc = FeatureColour.parseJalviewFeatureColour(Format
405 .getHexString(Color.RED));
406 assertTrue(fc.isSimpleColour());
407 assertEquals(Color.RED, fc.getColour());
410 * simple colour by rgb triplet
412 fc = FeatureColour.parseJalviewFeatureColour("255,0,0");
413 assertTrue(fc.isSimpleColour());
414 assertEquals(Color.RED, fc.getColour());
421 fc = FeatureColour.parseJalviewFeatureColour("oops");
422 fail("expected exception");
423 } catch (IllegalArgumentException e)
425 assertEquals("Invalid colour descriptor: oops", e.getMessage());
429 * colour by label (no threshold)
431 fc = FeatureColour.parseJalviewFeatureColour("label");
432 assertTrue(fc.isColourByLabel());
433 assertFalse(fc.hasThreshold());
436 * colour by label (with threshold)
439 .parseJalviewFeatureColour("label|||0.0|0.0|above|12.0");
440 assertTrue(fc.isColourByLabel());
441 assertTrue(fc.isAboveThreshold());
442 assertEquals(12.0f, fc.getThreshold());
445 * graduated colour (by name) (no threshold)
447 fc = FeatureColour.parseJalviewFeatureColour("red|green|10.0|20.0");
448 assertTrue(fc.isGraduatedColour());
449 assertFalse(fc.hasThreshold());
450 assertEquals(Color.RED, fc.getMinColour());
451 assertEquals(Color.GREEN, fc.getMaxColour());
452 assertEquals(10f, fc.getMin());
453 assertEquals(20f, fc.getMax());
454 assertTrue(fc.isAutoScaled());
457 * graduated colour (by hex code) (above threshold)
459 String descriptor = String.format("%s|%s|10.0|20.0|above|15",
460 Format.getHexString(Color.RED),
461 Format.getHexString(Color.GREEN));
462 fc = FeatureColour.parseJalviewFeatureColour(descriptor);
463 assertTrue(fc.isGraduatedColour());
464 assertTrue(fc.hasThreshold());
465 assertTrue(fc.isAboveThreshold());
466 assertEquals(15f, fc.getThreshold());
467 assertEquals(Color.RED, fc.getMinColour());
468 assertEquals(Color.GREEN, fc.getMaxColour());
469 assertEquals(10f, fc.getMin());
470 assertEquals(20f, fc.getMax());
471 assertTrue(fc.isAutoScaled());
474 * graduated colour (by RGB triplet) (below threshold), absolute scale
476 descriptor = String.format("255,0,0|0,255,0|abso|10.0|20.0|below|15");
477 fc = FeatureColour.parseJalviewFeatureColour(descriptor);
478 assertTrue(fc.isGraduatedColour());
479 assertFalse(fc.isAutoScaled());
480 assertTrue(fc.hasThreshold());
481 assertTrue(fc.isBelowThreshold());
482 assertEquals(15f, fc.getThreshold());
483 assertEquals(Color.RED, fc.getMinColour());
484 assertEquals(Color.GREEN, fc.getMaxColour());
485 assertEquals(10f, fc.getMin());
486 assertEquals(20f, fc.getMax());
489 .format("blue|255,0,255|absolute|20.0|95.0|below|66.0");
490 fc = FeatureColour.parseJalviewFeatureColour(descriptor);
491 assertTrue(fc.isGraduatedColour());
494 @Test(groups = { "Functional" })
495 public void testGetColor_colourByAttributeText()
497 FeatureColour fc = new FeatureColour();
498 fc.setColourByLabel(true);
499 fc.setAttributeName("consequence");
500 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 1f,
504 * if feature has no such attribute, use 'no value' colour
506 assertEquals(FeatureColour.DEFAULT_NO_COLOUR, fc.getColor(sf));
509 * if feature has attribute, generate colour from value
511 sf.setValue("consequence", "benign");
512 Color expected = ColorUtils.createColourFromName("benign");
513 assertEquals(expected, fc.getColor(sf));
516 @Test(groups = { "Functional" })
517 public void testGetColor_GraduatedByAttributeValue()
520 * graduated colour based on attribute value for AF
521 * given a min-max range of 0-100
523 FeatureColour fc = new FeatureColour(new Color(50, 100, 150),
524 new Color(150, 200, 250), Color.yellow, 0f, 100f);
525 String attName = "AF";
526 fc.setAttributeName(attName);
529 * first case: feature lacks the attribute - use 'no value' colour
531 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 75f,
533 assertEquals(Color.yellow, fc.getColor(sf));
536 * second case: attribute present but not numeric - treat as if absent
538 sf.setValue(attName, "twelve");
539 assertEquals(Color.yellow, fc.getColor(sf));
542 * third case: valid attribute value
544 sf.setValue(attName, "20.0");
545 Color expected = new Color(70, 120, 170);
546 assertEquals(expected, fc.getColor(sf));