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.renderer.seqfeatures;
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertFalse;
25 import static org.testng.Assert.assertNotEquals;
26 import static org.testng.Assert.assertNull;
27 import static org.testng.Assert.assertTrue;
29 import java.awt.Color;
30 import java.util.List;
32 import org.testng.annotations.BeforeMethod;
33 import org.testng.annotations.BeforeTest;
34 import org.testng.annotations.Test;
36 import jalview.api.FeatureColourI;
37 import jalview.datamodel.SequenceFeature;
38 import jalview.datamodel.SequenceI;
39 import jalview.gui.AlignFrame;
40 import jalview.gui.AlignViewport;
41 import jalview.io.DataSourceType;
42 import jalview.io.FileLoader;
43 import jalview.schemes.FeatureColour;
44 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
45 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
48 * Unit tests for feature colour determination, including but not limited to
50 * <li>gap position</li>
51 * <li>no features present</li>
52 * <li>features present but show features turned off</li>
53 * <li>features displayed but selected feature turned off</li>
54 * <li>features displayed but feature group turned off</li>
55 * <li>feature displayed but none at the specified position</li>
56 * <li>multiple features at position, with no transparency</li>
57 * <li>multiple features at position, with transparency</li>
58 * <li>score graduated feature colour</li>
59 * <li>contact feature start at the selected position</li>
60 * <li>contact feature end at the selected position</li>
61 * <li>contact feature straddling the selected position (not shown)</li>
64 public class FeatureColourFinderTest
66 private AlignViewport av;
68 private SequenceI seq;
70 private FeatureColourFinder finder;
72 private AlignFrame af;
74 private FeatureRendererModel fr;
76 @BeforeTest(alwaysRun = true)
79 // aligned column 8 is sequence position 6
80 String s = ">s1\nABCDE---FGHIJKLMNOPQRSTUVWXYZ\n";
81 af = new FileLoader().LoadFileWaitTillLoaded(s, DataSourceType.PASTE);
82 av = af.getViewport();
83 seq = av.getAlignment().getSequenceAt(0);
84 fr = af.getFeatureRenderer();
85 finder = new FeatureColourFinder(fr);
89 * Clear down any sequence features before each test (not as easy as it
92 @BeforeMethod(alwaysRun = true)
93 public void setUpBeforeTest()
95 List<SequenceFeature> sfs = seq.getSequenceFeatures();
96 for (SequenceFeature sf : sfs)
98 seq.deleteFeature(sf);
100 fr.findAllFeatures(true);
103 * reset all feature groups to visible
105 for (String group : fr.getGroups(false))
107 fr.setGroupVisibility(group, true);
110 fr.clearRenderOrder();
111 av.setShowSequenceFeatures(true);
114 @Test(groups = "Functional")
115 public void testFindFeatureColour_noFeatures()
117 av.setShowSequenceFeatures(false);
118 Color c = finder.findFeatureColour(Color.blue, seq, 10);
119 assertEquals(c, Color.blue);
121 av.setShowSequenceFeatures(true);
122 c = finder.findFeatureColour(Color.blue, seq, 10);
123 assertEquals(c, Color.blue);
126 @Test(groups = "Functional")
127 public void testFindFeatureColour_noFeaturesShown()
129 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
130 Float.NaN, "MetalGroup"));
132 av.setShowSequenceFeatures(false);
133 Color c = finder.findFeatureColour(Color.blue, seq, 10);
134 assertEquals(c, Color.blue);
137 @Test(groups = "Functional")
138 public void testFindFeatureColour_singleFeatureAtPosition()
140 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
141 Float.NaN, "MetalGroup"));
142 fr.setColour("Metal", new FeatureColour(Color.red));
144 av.setShowSequenceFeatures(true);
145 Color c = finder.findFeatureColour(Color.blue, seq, 10);
146 assertEquals(c, Color.red);
150 * feature colour at a gap is null (not white) - a user defined colour scheme
151 * can then provide a bespoke gap colour if configured to do so
153 @Test(groups = "Functional")
154 public void testFindFeatureColour_gapPosition()
156 seq.addSequenceFeature(
157 new SequenceFeature("Metal", "Metal", 2, 12, 0f, null));
158 fr.setColour("Metal", new FeatureColour(Color.red));
160 av.setShowSequenceFeatures(true);
161 Color c = finder.findFeatureColour(null, seq, 6);
166 * Nested features coloured by label - expect the colour of the enclosed
169 @Test(groups = "Functional")
170 public void testFindFeatureColour_nestedFeatures()
172 SequenceFeature sf1 = new SequenceFeature("domain", "peptide", 1, 120, 0f, null);
173 seq.addSequenceFeature(sf1);
174 SequenceFeature sf2 = new SequenceFeature("domain", "binding", 10, 20,
176 seq.addSequenceFeature(sf2);
177 FeatureColourI fc = new FeatureColour(Color.red);
178 fc.setColourByLabel(true);
179 fr.setColour("domain", fc);
181 av.setShowSequenceFeatures(true);
182 Color c = finder.findFeatureColour(null, seq, 15);
183 assertEquals(c, fr.getColor(sf2, fc));
186 @Test(groups = "Functional")
187 public void testFindFeatureColour_multipleFeaturesAtPositionNoTransparency()
190 * featuresAdded -> FeatureRendererModel.updateRenderOrder which adds any
191 * new features 'on top' (but reverses the order of any added features)
193 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
194 Float.NaN, "MetalGroup"));
195 FeatureColour red = new FeatureColour(Color.red);
196 fr.setColour("Metal", red);
198 seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15,
199 Float.NaN, "DomainGroup"));
200 FeatureColour green = new FeatureColour(Color.green);
201 fr.setColour("Domain", green);
203 av.setShowSequenceFeatures(true);
206 * expect Domain (green) to be rendered above Metal (red)
208 Color c = finder.findFeatureColour(Color.blue, seq, 10);
209 assertEquals(c, Color.green);
212 * now promote Metal above Domain
213 * - currently no way other than mimicking reordering of
214 * table in Feature Settings
216 FeatureSettingsBean[] data = new FeatureSettingsBean[2];
217 data[0] = new FeatureSettingsBean("Metal", red, null, true);
218 data[1] = new FeatureSettingsBean("Domain", green, null, true);
219 fr.setFeaturePriority(data);
220 c = finder.findFeatureColour(Color.blue, seq, 10);
221 assertEquals(c, Color.red);
224 * ..and turn off display of Metal
226 data[0] = new FeatureSettingsBean("Metal", red, null, false);
227 fr.setFeaturePriority(data);
228 c = finder.findFeatureColour(Color.blue, seq, 10);
229 assertEquals(c, Color.green);
232 @Test(groups = "Functional")
233 public void testFindFeatureColour_singleFeatureNotAtPosition()
235 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 8, 12,
236 Float.NaN, "MetalGroup"));
237 fr.setColour("Metal", new FeatureColour(Color.red));
239 av.setShowSequenceFeatures(true);
240 // column 2 = sequence position 3
241 Color c = finder.findFeatureColour(Color.blue, seq, 2);
242 assertEquals(c, Color.blue);
245 @Test(groups = "Functional")
246 public void testFindFeatureColour_featureTypeNotDisplayed()
248 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
249 Float.NaN, "MetalGroup"));
250 FeatureColour red = new FeatureColour(Color.red);
251 fr.setColour("Metal", red);
253 av.setShowSequenceFeatures(true);
254 Color c = finder.findFeatureColour(Color.blue, seq, 10);
255 assertEquals(c, Color.red);
258 * turn off display of Metal - is this the easiest way to do it??
260 FeatureSettingsBean[] data = new FeatureSettingsBean[1];
261 data[0] = new FeatureSettingsBean("Metal", red, null, false);
262 fr.setFeaturePriority(data);
263 c = finder.findFeatureColour(Color.blue, seq, 10);
264 assertEquals(c, Color.blue);
267 * turn display of Metal back on
269 data[0] = new FeatureSettingsBean("Metal", red, null, true);
270 fr.setFeaturePriority(data);
271 c = finder.findFeatureColour(Color.blue, seq, 10);
272 assertEquals(c, Color.red);
275 @Test(groups = "Functional")
276 public void testFindFeatureColour_featureGroupNotDisplayed()
278 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
279 Float.NaN, "MetalGroup"));
280 FeatureColour red = new FeatureColour(Color.red);
281 fr.setColour("Metal", red);
283 av.setShowSequenceFeatures(true);
284 Color c = finder.findFeatureColour(Color.blue, seq, 10);
285 assertEquals(c, Color.red);
288 * turn off display of MetalGroup
290 fr.setGroupVisibility("MetalGroup", false);
291 c = finder.findFeatureColour(Color.blue, seq, 10);
292 assertEquals(c, Color.blue);
295 * turn display of MetalGroup back on
297 fr.setGroupVisibility("MetalGroup", true);
298 c = finder.findFeatureColour(Color.blue, seq, 10);
299 assertEquals(c, Color.red);
302 @Test(groups = "Functional")
303 public void testFindFeatureColour_contactFeature()
306 * currently contact feature == type "Disulphide Bond" or "Disulfide Bond" !!
308 seq.addSequenceFeature(new SequenceFeature("Disulphide Bond", "Contact",
309 2, 12, Float.NaN, "Disulphide"));
310 fr.setColour("Disulphide Bond", new FeatureColour(Color.red));
312 av.setShowSequenceFeatures(true);
315 * Contact positions are residues 2 and 12
316 * which are columns 1 and 14
317 * positions in between don't count for a contact feature!
319 Color c = finder.findFeatureColour(Color.blue, seq, 10);
320 assertEquals(c, Color.blue);
321 c = finder.findFeatureColour(Color.blue, seq, 8);
322 assertEquals(c, Color.blue);
323 c = finder.findFeatureColour(Color.blue, seq, 1);
324 assertEquals(c, Color.red);
325 c = finder.findFeatureColour(Color.blue, seq, 14);
326 assertEquals(c, Color.red);
329 @Test(groups = "Functional")
330 public void testFindFeatureAtEnd()
333 * terminal residue feature
335 seq.addSequenceFeature(new SequenceFeature("PDBRESNUM", "pdb res 1",
336 seq.getEnd(), seq.getEnd(), Float.NaN, "1seq.pdb"));
337 fr.setColour("PDBRESNUM", new FeatureColour(Color.red));
339 av.setShowSequenceFeatures(true);
342 * final column should have PDBRESNUM feature, the others not
344 Color c = finder.findFeatureColour(Color.blue, seq,
345 seq.getLength() - 2);
346 assertNotEquals(c, Color.red);
347 c = finder.findFeatureColour(Color.blue, seq, seq.getLength() - 1);
348 assertEquals(c, Color.red);
351 @Test(groups = "Functional")
352 public void testFindFeatureColour_graduatedFeatureColour()
354 seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2, 2,
356 seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 4, 4,
358 seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 7, 7,
362 * graduated colour from 0 to 10
364 Color min = new Color(100, 50, 150);
365 Color max = new Color(200, 0, 100);
366 FeatureColourI fc = new FeatureColour(null, min, max, null, 0, 10);
367 fr.setColour("kd", fc);
369 av.setShowSequenceFeatures(true);
372 * position 2, column 1, score 0 - minimum colour in range
374 Color c = finder.findFeatureColour(Color.blue, seq, 1);
375 assertEquals(c, min);
378 * position 7, column 9, score 10 - maximum colour in range
380 c = finder.findFeatureColour(Color.blue, seq, 9);
381 assertEquals(c, max);
384 * position 4, column 3, score 5 - half way from min to max
386 c = finder.findFeatureColour(Color.blue, seq, 3);
387 assertEquals(c, new Color(150, 25, 125));
390 @Test(groups = "Functional")
391 public void testFindFeatureColour_transparencySingleFeature()
393 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
394 Float.NaN, "MetalGroup"));
395 FeatureColour red = new FeatureColour(Color.red);
396 fr.setColour("Metal", red);
398 av.setShowSequenceFeatures(true);
401 * the FeatureSettings transparency slider has range 0-70 which
402 * corresponds to a transparency value of 1 - 0.3
403 * A value of 0.4 gives a combination of
404 * 0.4 * red(255, 0, 0) + 0.6 * cyan(0, 255, 255) = (102, 153, 153)
406 fr.setTransparency(0.4f);
407 Color c = finder.findFeatureColour(Color.cyan, seq, 10);
408 assertEquals(c, new Color(102, 153, 153));
411 @Test(groups = "Functional")
412 public void testFindFeatureColour_transparencyTwoFeatures()
414 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
415 Float.NaN, "MetalGroup"));
416 FeatureColour red = new FeatureColour(Color.red);
417 fr.setColour("Metal", red);
419 seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15,
420 Float.NaN, "DomainGroup"));
421 FeatureColour green = new FeatureColour(Color.green);
422 fr.setColour("Domain", green);
424 av.setShowSequenceFeatures(true);
427 * Domain (green) rendered above Metal (red) above background (cyan)
428 * 1) 0.6 * red(255, 0, 0) + 0.4 * cyan(0, 255, 255) = (153, 102, 102)
429 * 2) 0.6* green(0, 255, 0) + 0.4 * (153, 102, 102) = (61, 194, 41) rounded
431 fr.setTransparency(0.6f);
432 Color c = finder.findFeatureColour(Color.cyan, seq, 10);
433 assertEquals(c, new Color(61, 194, 41));
436 * now promote Metal above Domain
437 * - currently no way other than mimicking reordering of
438 * table in Feature Settings
439 * Metal (red) rendered above Domain (green) above background (cyan)
440 * 1) 0.6 * green(0, 255, 0) + 0.4 * cyan(0, 255, 255) = (0, 255, 102)
441 * 2) 0.6* red(255, 0, 0) + 0.4 * (0, 255, 102) = (153, 102, 41) rounded
443 FeatureSettingsBean[] data = new FeatureSettingsBean[2];
444 data[0] = new FeatureSettingsBean("Metal", red, null, true);
445 data[1] = new FeatureSettingsBean("Domain", green, null, true);
446 fr.setFeaturePriority(data);
447 c = finder.findFeatureColour(Color.cyan, seq, 10);
448 assertEquals(c, new Color(153, 102, 41));
451 * ..and turn off display of Metal
452 * Domain (green) above background (pink)
453 * 0.6 * green(0, 255, 0) + 0.4 * pink(255, 175, 175) = (102, 223, 70)
455 data[0] = new FeatureSettingsBean("Metal", red, null, false);
456 fr.setFeaturePriority(data);
457 c = finder.findFeatureColour(Color.pink, seq, 10);
458 assertEquals(c, new Color(102, 223, 70));
461 @Test(groups = "Functional")
462 public void testNoFeaturesDisplayed()
465 * no features on alignment to render
467 assertTrue(finder.noFeaturesDisplayed());
471 * it will be automatically set visible but we leave
472 * the viewport configured not to show features
474 av.setShowSequenceFeatures(false);
475 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
476 Float.NaN, "MetalGroup"));
477 FeatureColour red = new FeatureColour(Color.red);
478 fr.setColour("Metal", red);
480 assertTrue(finder.noFeaturesDisplayed());
483 * turn on feature display
485 av.setShowSequenceFeatures(true);
486 assertFalse(finder.noFeaturesDisplayed());
489 * turn off display of Metal
491 FeatureSettingsBean[] data = new FeatureSettingsBean[1];
492 data[0] = new FeatureSettingsBean("Metal", red, null, false);
493 fr.setFeaturePriority(data);
494 assertTrue(finder.noFeaturesDisplayed());
497 * turn display of Metal back on
499 fr.setVisible("Metal");
500 assertFalse(finder.noFeaturesDisplayed());
503 * turn off MetalGroup - has no effect here since the group of a
504 * sequence feature instance is independent of its type
506 fr.setGroupVisibility("MetalGroup", false);
507 assertFalse(finder.noFeaturesDisplayed());
510 * a finder with no feature renderer
512 FeatureColourFinder finder2 = new FeatureColourFinder(null);
513 assertTrue(finder2.noFeaturesDisplayed());
516 @Test(groups = "Functional")
517 public void testFindFeatureColour_graduatedWithThreshold()
519 String kdFeature = "kd";
520 String metalFeature = "Metal";
521 seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity",
522 2, 2, 0f, "KdGroup"));
523 seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity",
524 4, 4, 5f, "KdGroup"));
525 seq.addSequenceFeature(new SequenceFeature(metalFeature, "Fe", 4, 4, 5f,
527 seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity",
528 7, 7, 10f, "KdGroup"));
531 * kd feature has graduated colour from 0 to 10
532 * above threshold value of 5
534 Color min = new Color(100, 50, 150);
535 Color max = new Color(200, 0, 100);
536 FeatureColourI fc = new FeatureColour(null, min, max, null, 0, 10);
537 fc.setAboveThreshold(true);
539 fr.setColour(kdFeature, fc);
540 FeatureColour green = new FeatureColour(Color.green);
541 fr.setColour(metalFeature, green);
545 * render order is kd above Metal
547 FeatureSettingsBean[] data = new FeatureSettingsBean[2];
548 data[0] = new FeatureSettingsBean(kdFeature, fc, null, true);
549 data[1] = new FeatureSettingsBean(metalFeature, green, null, true);
550 fr.setFeaturePriority(data);
552 av.setShowSequenceFeatures(true);
555 * position 2, column 1, score 0 - below threshold - default colour
557 Color c = finder.findFeatureColour(Color.blue, seq, 1);
558 assertEquals(c, Color.blue);
561 * position 4, column 3, score 5 - at threshold
562 * should return Green (colour of Metal feature)
564 c = finder.findFeatureColour(Color.blue, seq, 3);
565 assertEquals(c, Color.green);
568 * position 7, column 9, score 10 - maximum colour in range
570 c = finder.findFeatureColour(Color.blue, seq, 9);
571 assertEquals(c, max);
574 * now colour below threshold of 5
576 fc.setBelowThreshold(true);
579 * position 2, column 1, score 0 - min colour
581 c = finder.findFeatureColour(Color.blue, seq, 1);
582 assertEquals(c, min);
585 * position 4, column 3, score 5 - at threshold
586 * should return Green (colour of Metal feature)
588 c = finder.findFeatureColour(Color.blue, seq, 3);
589 assertEquals(c, Color.green);
592 * position 7, column 9, score 10 - above threshold - default colour
594 c = finder.findFeatureColour(Color.blue, seq, 9);
595 assertEquals(c, Color.blue);