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"));
188 fc = new FeatureColour(Color.BLUE, Color.RED, 1f, 5f);
189 fc.setColourByLabel(true);
190 assertFalse(fc.isGraduatedColour());
191 assertTrue(fc.isColourByLabel());
192 assertFalse(fc.isColourByAttribute());
193 assertNull(fc.getAttributeName());
194 assertEquals(1f, fc.getMin());
195 assertEquals(5f, fc.getMax());
198 * update min-max bounds - converts to graduated colour
200 fc1 = new FeatureColour(fc, 2f, 6f);
201 assertTrue(fc1.isGraduatedColour());
202 assertFalse(fc1.isColourByLabel());
203 assertFalse(fc1.isColourByAttribute());
204 assertNull(fc1.getAttributeName());
205 assertEquals(2f, fc1.getMin());
206 assertEquals(6f, fc1.getMax());
209 @Test(groups = { "Functional" })
210 public void testGetColor_simpleColour()
212 FeatureColour fc = new FeatureColour(Color.RED);
213 assertEquals(Color.RED,
214 fc.getColor(new SequenceFeature("Cath", "", 1, 2, 0f, null)));
217 @Test(groups = { "Functional" })
218 public void testGetColor_colourByLabel()
220 FeatureColour fc = new FeatureColour();
221 fc.setColourByLabel(true);
222 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 1f,
224 Color expected = ColorUtils.createColourFromName("desc");
225 assertEquals(expected, fc.getColor(sf));
228 @Test(groups = { "Functional" })
229 public void testGetColor_Graduated()
232 * graduated colour from
234 * gray(128, 128, 128) to red(255, 0, 0)
236 FeatureColour fc = new FeatureColour(Color.GRAY, Color.RED, 0f, 100f);
237 // feature score is 75 which is 3/4 of the way from GRAY to RED
238 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 75f,
240 // the colour gradient is computed in float values from 0-1 (where 1 == 255)
241 float red = 128 / 255f + 3 / 4f * (255 - 128) / 255f;
242 float green = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
243 float blue = 128 / 255f + 3 / 4f * (0 - 128) / 255f;
244 Color expected = new Color(red, green, blue);
245 assertEquals(expected, fc.getColor(sf));
248 @Test(groups = { "Functional" })
249 public void testGetColor_aboveBelowThreshold()
251 // gradient from [50, 150] from WHITE(255, 255, 255) to BLACK(0, 0, 0)
252 FeatureColour fc = new FeatureColour(Color.WHITE, Color.BLACK, 50f,
254 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 70f,
258 * feature with score of Float.NaN is always assigned minimum colour
260 SequenceFeature sf2 = new SequenceFeature("type", "desc", 0, 20,
263 fc.setThreshold(100f); // ignore for now
264 assertEquals(new Color(204, 204, 204), fc.getColor(sf));
265 assertEquals(Color.white, fc.getColor(sf2));
267 fc.setAboveThreshold(true); // feature lies below threshold
268 assertNull(fc.getColor(sf));
269 assertEquals(Color.white, fc.getColor(sf2));
271 fc.setBelowThreshold(true);
272 fc.setThreshold(70f);
273 assertNull(fc.getColor(sf)); // feature score == threshold - hidden
274 assertEquals(Color.white, fc.getColor(sf2));
275 fc.setThreshold(69f);
276 assertNull(fc.getColor(sf)); // feature score > threshold - hidden
277 assertEquals(Color.white, fc.getColor(sf2));
281 * Test output of feature colours to Jalview features file format
283 @Test(groups = { "Functional" })
284 public void testToJalviewFormat()
287 * plain colour - to RGB hex code
289 FeatureColour fc = new FeatureColour(Color.RED);
290 String redHex = Format.getHexString(Color.RED);
291 String hexColour = redHex;
292 assertEquals("domain\t" + hexColour, fc.toJalviewFormat("domain"));
295 * colour by label (no threshold)
297 fc = new FeatureColour();
298 fc.setColourByLabel(true);
299 assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
302 * colour by label (autoscaled) (an odd state you can reach by selecting
303 * 'above threshold', then deselecting 'threshold is min/max' then 'colour
306 fc.setAutoScaled(true);
307 assertEquals("domain\tlabel", fc.toJalviewFormat("domain"));
310 * colour by label (above threshold) (min/max values are output though not
311 * used by this scheme)
313 fc.setAutoScaled(false);
314 fc.setThreshold(12.5f);
315 fc.setAboveThreshold(true);
316 assertEquals("domain\tlabel|||0.0|0.0|above|12.5",
317 fc.toJalviewFormat("domain"));
320 * colour by label (below threshold)
322 fc.setBelowThreshold(true);
323 assertEquals("domain\tlabel|||0.0|0.0|below|12.5",
324 fc.toJalviewFormat("domain"));
327 * graduated colour, no threshold
329 fc = new FeatureColour(Color.GREEN, Color.RED, 12f, 25f);
330 String greenHex = Format.getHexString(Color.GREEN);
331 String expected = String.format("domain\t%s|%s|abso|12.0|25.0|none",
333 assertEquals(expected, fc.toJalviewFormat("domain"));
336 * colour ranges over the actual score ranges (not min/max)
338 fc.setAutoScaled(true);
339 expected = String.format("domain\t%s|%s|12.0|25.0|none", greenHex,
341 assertEquals(expected, fc.toJalviewFormat("domain"));
344 * graduated colour below threshold
346 fc.setThreshold(12.5f);
347 fc.setBelowThreshold(true);
348 expected = String.format("domain\t%s|%s|12.0|25.0|below|12.5",
350 assertEquals(expected, fc.toJalviewFormat("domain"));
353 * graduated colour above threshold
355 fc.setThreshold(12.5f);
356 fc.setAboveThreshold(true);
357 fc.setAutoScaled(false);
358 expected = String.format("domain\t%s|%s|abso|12.0|25.0|above|12.5",
360 assertEquals(expected, fc.toJalviewFormat("domain"));
364 * Test parsing of feature colours from Jalview features file format
366 @Test(groups = { "Functional" })
367 public void testParseJalviewFeatureColour()
370 * simple colour by name
372 FeatureColour fc = FeatureColour.parseJalviewFeatureColour("red");
373 assertTrue(fc.isSimpleColour());
374 assertEquals(Color.RED, fc.getColour());
377 * simple colour by hex code
379 fc = FeatureColour.parseJalviewFeatureColour(Format
380 .getHexString(Color.RED));
381 assertTrue(fc.isSimpleColour());
382 assertEquals(Color.RED, fc.getColour());
385 * simple colour by rgb triplet
387 fc = FeatureColour.parseJalviewFeatureColour("255,0,0");
388 assertTrue(fc.isSimpleColour());
389 assertEquals(Color.RED, fc.getColour());
396 fc = FeatureColour.parseJalviewFeatureColour("oops");
397 fail("expected exception");
398 } catch (IllegalArgumentException e)
400 assertEquals("Invalid colour descriptor: oops", e.getMessage());
404 * colour by label (no threshold)
406 fc = FeatureColour.parseJalviewFeatureColour("label");
407 assertTrue(fc.isColourByLabel());
408 assertFalse(fc.hasThreshold());
411 * colour by label (with threshold)
414 .parseJalviewFeatureColour("label|||0.0|0.0|above|12.0");
415 assertTrue(fc.isColourByLabel());
416 assertTrue(fc.isAboveThreshold());
417 assertEquals(12.0f, fc.getThreshold());
420 * graduated colour (by name) (no threshold)
422 fc = FeatureColour.parseJalviewFeatureColour("red|green|10.0|20.0");
423 assertTrue(fc.isGraduatedColour());
424 assertFalse(fc.hasThreshold());
425 assertEquals(Color.RED, fc.getMinColour());
426 assertEquals(Color.GREEN, fc.getMaxColour());
427 assertEquals(10f, fc.getMin());
428 assertEquals(20f, fc.getMax());
429 assertTrue(fc.isAutoScaled());
432 * graduated colour (by hex code) (above threshold)
434 String descriptor = String.format("%s|%s|10.0|20.0|above|15",
435 Format.getHexString(Color.RED),
436 Format.getHexString(Color.GREEN));
437 fc = FeatureColour.parseJalviewFeatureColour(descriptor);
438 assertTrue(fc.isGraduatedColour());
439 assertTrue(fc.hasThreshold());
440 assertTrue(fc.isAboveThreshold());
441 assertEquals(15f, fc.getThreshold());
442 assertEquals(Color.RED, fc.getMinColour());
443 assertEquals(Color.GREEN, fc.getMaxColour());
444 assertEquals(10f, fc.getMin());
445 assertEquals(20f, fc.getMax());
446 assertTrue(fc.isAutoScaled());
449 * graduated colour (by RGB triplet) (below threshold), absolute scale
451 descriptor = String.format("255,0,0|0,255,0|abso|10.0|20.0|below|15");
452 fc = FeatureColour.parseJalviewFeatureColour(descriptor);
453 assertTrue(fc.isGraduatedColour());
454 assertFalse(fc.isAutoScaled());
455 assertTrue(fc.hasThreshold());
456 assertTrue(fc.isBelowThreshold());
457 assertEquals(15f, fc.getThreshold());
458 assertEquals(Color.RED, fc.getMinColour());
459 assertEquals(Color.GREEN, fc.getMaxColour());
460 assertEquals(10f, fc.getMin());
461 assertEquals(20f, fc.getMax());
464 .format("blue|255,0,255|absolute|20.0|95.0|below|66.0");
465 fc = FeatureColour.parseJalviewFeatureColour(descriptor);
466 assertTrue(fc.isGraduatedColour());
469 @Test(groups = { "Functional" })
470 public void testGetColor_colourByAttributeText()
472 FeatureColour fc = new FeatureColour();
473 fc.setColourByLabel(true);
474 fc.setAttributeName("consequence");
475 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 1f,
479 * if feature has no such attribute, use 'no value' colour
481 assertEquals(FeatureColour.DEFAULT_NO_COLOUR, fc.getColor(sf));
484 * if feature has attribute, generate colour from value
486 sf.setValue("consequence", "benign");
487 Color expected = ColorUtils.createColourFromName("benign");
488 assertEquals(expected, fc.getColor(sf));
491 @Test(groups = { "Functional" })
492 public void testGetColor_GraduatedByAttributeValue()
495 * graduated colour based on attribute value for AF
496 * given a min-max range of 0-100
498 FeatureColour fc = new FeatureColour(new Color(50, 100, 150),
499 new Color(150, 200, 250), Color.yellow, 0f, 100f);
500 String attName = "AF";
501 fc.setAttributeName(attName);
504 * first case: feature lacks the attribute - use 'no value' colour
506 SequenceFeature sf = new SequenceFeature("type", "desc", 0, 20, 75f,
508 assertEquals(Color.yellow, fc.getColor(sf));
511 * second case: attribute present but not numeric - treat as if absent
513 sf.setValue(attName, "twelve");
514 assertEquals(Color.yellow, fc.getColor(sf));
517 * third case: valid attribute value
519 sf.setValue(attName, "20.0");
520 Color expected = new Color(70, 120, 170);
521 assertEquals(expected, fc.getColor(sf));