1 package jalview.renderer.seqfeatures;
3 import static org.testng.Assert.assertEquals;
4 import static org.testng.Assert.assertFalse;
5 import static org.testng.Assert.assertNotEquals;
6 import static org.testng.Assert.assertNull;
7 import static org.testng.Assert.assertTrue;
10 import java.util.List;
12 import org.testng.annotations.BeforeMethod;
13 import org.testng.annotations.BeforeTest;
14 import org.testng.annotations.Test;
16 import jalview.api.FeatureColourI;
17 import jalview.datamodel.SequenceFeature;
18 import jalview.datamodel.SequenceI;
19 import jalview.gui.AlignFrame;
20 import jalview.gui.AlignViewport;
21 import jalview.io.DataSourceType;
22 import jalview.io.FileLoader;
23 import jalview.schemes.FeatureColour;
24 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
25 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
28 * Unit tests for feature colour determination, including but not limited to
30 * <li>gap position</li>
31 * <li>no features present</li>
32 * <li>features present but show features turned off</li>
33 * <li>features displayed but selected feature turned off</li>
34 * <li>features displayed but feature group turned off</li>
35 * <li>feature displayed but none at the specified position</li>
36 * <li>multiple features at position, with no transparency</li>
37 * <li>multiple features at position, with transparency</li>
38 * <li>score graduated feature colour</li>
39 * <li>contact feature start at the selected position</li>
40 * <li>contact feature end at the selected position</li>
41 * <li>contact feature straddling the selected position (not shown)</li>
44 public class FeatureColourFinderTest
46 private AlignViewport av;
48 private SequenceI seq;
50 private FeatureColourFinder finder;
52 private AlignFrame af;
54 private FeatureRendererModel fr;
56 @BeforeTest(alwaysRun = true)
59 // aligned column 8 is sequence position 6
60 String s = ">s1\nABCDE---FGHIJKLMNOPQRSTUVWXYZ\n";
61 af = new FileLoader().LoadFileWaitTillLoaded(s, DataSourceType.PASTE);
62 av = af.getViewport();
63 seq = av.getAlignment().getSequenceAt(0);
64 fr = af.getFeatureRenderer();
65 finder = new FeatureColourFinder(fr);
69 * Clear down any sequence features before each test (not as easy as it
72 @BeforeMethod(alwaysRun = true)
73 public void setUpBeforeTest()
75 List<SequenceFeature> sfs = seq.getSequenceFeatures();
76 for (SequenceFeature sf : sfs)
78 seq.deleteFeature(sf);
80 fr.findAllFeatures(true);
83 * reset all feature groups to visible
85 for (String group : fr.getGroups(false))
87 fr.setGroupVisibility(group, true);
90 fr.clearRenderOrder();
91 av.setShowSequenceFeatures(true);
94 @Test(groups = "Functional")
95 public void testFindFeatureColour_noFeatures()
97 av.setShowSequenceFeatures(false);
98 Color c = finder.findFeatureColour(Color.blue, seq, 10);
99 assertEquals(c, Color.blue);
101 av.setShowSequenceFeatures(true);
102 c = finder.findFeatureColour(Color.blue, seq, 10);
103 assertEquals(c, Color.blue);
106 @Test(groups = "Functional")
107 public void testFindFeatureColour_noFeaturesShown()
109 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
110 Float.NaN, "MetalGroup"));
112 av.setShowSequenceFeatures(false);
113 Color c = finder.findFeatureColour(Color.blue, seq, 10);
114 assertEquals(c, Color.blue);
117 @Test(groups = "Functional")
118 public void testFindFeatureColour_singleFeatureAtPosition()
120 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
121 Float.NaN, "MetalGroup"));
122 fr.setColour("Metal", new FeatureColour(Color.red));
124 av.setShowSequenceFeatures(true);
125 Color c = finder.findFeatureColour(Color.blue, seq, 10);
126 assertEquals(c, Color.red);
130 * feature colour at a gap is null (not white) - a user defined colour scheme
131 * can then provide a bespoke gap colour if configured to do so
133 @Test(groups = "Functional")
134 public void testFindFeatureColour_gapPosition()
136 seq.addSequenceFeature(
137 new SequenceFeature("Metal", "Metal", 2, 12, 0f, null));
138 fr.setColour("Metal", new FeatureColour(Color.red));
140 av.setShowSequenceFeatures(true);
141 Color c = finder.findFeatureColour(null, seq, 6);
146 * Nested features coloured by label - expect the colour of the enclosed
149 @Test(groups = "Functional")
150 public void testFindFeatureColour_nestedFeatures()
152 SequenceFeature sf1 = new SequenceFeature("domain", "peptide", 1, 120, 0f, null);
153 seq.addSequenceFeature(sf1);
154 SequenceFeature sf2 = new SequenceFeature("domain", "binding", 10, 20,
156 seq.addSequenceFeature(sf2);
157 FeatureColourI fc = new FeatureColour(Color.red);
158 fc.setColourByLabel(true);
159 fr.setColour("domain", fc);
161 av.setShowSequenceFeatures(true);
162 Color c = finder.findFeatureColour(null, seq, 15);
163 assertEquals(c, fr.getColor(sf2, fc));
166 @Test(groups = "Functional")
167 public void testFindFeatureColour_multipleFeaturesAtPositionNoTransparency()
170 * featuresAdded -> FeatureRendererModel.updateRenderOrder which adds any
171 * new features 'on top' (but reverses the order of any added features)
173 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
174 Float.NaN, "MetalGroup"));
175 FeatureColour red = new FeatureColour(Color.red);
176 fr.setColour("Metal", red);
178 seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15,
179 Float.NaN, "DomainGroup"));
180 FeatureColour green = new FeatureColour(Color.green);
181 fr.setColour("Domain", green);
183 av.setShowSequenceFeatures(true);
186 * expect Domain (green) to be rendered above Metal (red)
188 Color c = finder.findFeatureColour(Color.blue, seq, 10);
189 assertEquals(c, Color.green);
192 * now promote Metal above Domain
193 * - currently no way other than mimicking reordering of
194 * table in Feature Settings
196 FeatureSettingsBean[] data = new FeatureSettingsBean[2];
197 data[0] = new FeatureSettingsBean("Metal", red, null, true);
198 data[1] = new FeatureSettingsBean("Domain", green, null, true);
199 fr.setFeaturePriority(data);
200 c = finder.findFeatureColour(Color.blue, seq, 10);
201 assertEquals(c, Color.red);
204 * ..and turn off display of Metal
206 data[0] = new FeatureSettingsBean("Metal", red, null, false);
207 fr.setFeaturePriority(data);
208 c = finder.findFeatureColour(Color.blue, seq, 10);
209 assertEquals(c, Color.green);
212 @Test(groups = "Functional")
213 public void testFindFeatureColour_singleFeatureNotAtPosition()
215 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 8, 12,
216 Float.NaN, "MetalGroup"));
217 fr.setColour("Metal", new FeatureColour(Color.red));
219 av.setShowSequenceFeatures(true);
220 // column 2 = sequence position 3
221 Color c = finder.findFeatureColour(Color.blue, seq, 2);
222 assertEquals(c, Color.blue);
225 @Test(groups = "Functional")
226 public void testFindFeatureColour_featureTypeNotDisplayed()
228 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
229 Float.NaN, "MetalGroup"));
230 FeatureColour red = new FeatureColour(Color.red);
231 fr.setColour("Metal", red);
233 av.setShowSequenceFeatures(true);
234 Color c = finder.findFeatureColour(Color.blue, seq, 10);
235 assertEquals(c, Color.red);
238 * turn off display of Metal - is this the easiest way to do it??
240 FeatureSettingsBean[] data = new FeatureSettingsBean[1];
241 data[0] = new FeatureSettingsBean("Metal", red, null, false);
242 fr.setFeaturePriority(data);
243 c = finder.findFeatureColour(Color.blue, seq, 10);
244 assertEquals(c, Color.blue);
247 * turn display of Metal back on
249 data[0] = new FeatureSettingsBean("Metal", red, null, true);
250 fr.setFeaturePriority(data);
251 c = finder.findFeatureColour(Color.blue, seq, 10);
252 assertEquals(c, Color.red);
255 @Test(groups = "Functional")
256 public void testFindFeatureColour_featureGroupNotDisplayed()
258 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
259 Float.NaN, "MetalGroup"));
260 FeatureColour red = new FeatureColour(Color.red);
261 fr.setColour("Metal", red);
263 av.setShowSequenceFeatures(true);
264 Color c = finder.findFeatureColour(Color.blue, seq, 10);
265 assertEquals(c, Color.red);
268 * turn off display of MetalGroup
270 fr.setGroupVisibility("MetalGroup", false);
271 c = finder.findFeatureColour(Color.blue, seq, 10);
272 assertEquals(c, Color.blue);
275 * turn display of MetalGroup back on
277 fr.setGroupVisibility("MetalGroup", true);
278 c = finder.findFeatureColour(Color.blue, seq, 10);
279 assertEquals(c, Color.red);
282 @Test(groups = "Functional")
283 public void testFindFeatureColour_contactFeature()
286 * currently contact feature == type "Disulphide Bond" or "Disulfide Bond" !!
288 seq.addSequenceFeature(new SequenceFeature("Disulphide Bond", "Contact",
289 2, 12, Float.NaN, "Disulphide"));
290 fr.setColour("Disulphide Bond", new FeatureColour(Color.red));
292 av.setShowSequenceFeatures(true);
295 * Contact positions are residues 2 and 12
296 * which are columns 1 and 14
297 * positions in between don't count for a contact feature!
299 Color c = finder.findFeatureColour(Color.blue, seq, 10);
300 assertEquals(c, Color.blue);
301 c = finder.findFeatureColour(Color.blue, seq, 8);
302 assertEquals(c, Color.blue);
303 c = finder.findFeatureColour(Color.blue, seq, 1);
304 assertEquals(c, Color.red);
305 c = finder.findFeatureColour(Color.blue, seq, 14);
306 assertEquals(c, Color.red);
309 @Test(groups = "Functional")
310 public void testFindFeatureAtEnd()
313 * terminal residue feature
315 seq.addSequenceFeature(new SequenceFeature("PDBRESNUM", "pdb res 1",
316 seq.getEnd(), seq.getEnd(), Float.NaN, "1seq.pdb"));
317 fr.setColour("PDBRESNUM", new FeatureColour(Color.red));
319 av.setShowSequenceFeatures(true);
322 * final column should have PDBRESNUM feature, the others not
324 Color c = finder.findFeatureColour(Color.blue, seq,
325 seq.getLength() - 2);
326 assertNotEquals(c, Color.red);
327 c = finder.findFeatureColour(Color.blue, seq, seq.getLength() - 1);
328 assertEquals(c, Color.red);
331 @Test(groups = "Functional")
332 public void testFindFeatureColour_graduatedFeatureColour()
334 seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2, 2,
336 seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 4, 4,
338 seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 7, 7,
342 * graduated colour from 0 to 10
344 Color min = new Color(100, 50, 150);
345 Color max = new Color(200, 0, 100);
346 FeatureColourI fc = new FeatureColour(null, min, max, null, 0, 10);
347 fr.setColour("kd", fc);
349 av.setShowSequenceFeatures(true);
352 * position 2, column 1, score 0 - minimum colour in range
354 Color c = finder.findFeatureColour(Color.blue, seq, 1);
355 assertEquals(c, min);
358 * position 7, column 9, score 10 - maximum colour in range
360 c = finder.findFeatureColour(Color.blue, seq, 9);
361 assertEquals(c, max);
364 * position 4, column 3, score 5 - half way from min to max
366 c = finder.findFeatureColour(Color.blue, seq, 3);
367 assertEquals(c, new Color(150, 25, 125));
370 @Test(groups = "Functional")
371 public void testFindFeatureColour_transparencySingleFeature()
373 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
374 Float.NaN, "MetalGroup"));
375 FeatureColour red = new FeatureColour(Color.red);
376 fr.setColour("Metal", red);
378 av.setShowSequenceFeatures(true);
381 * the FeatureSettings transparency slider has range 0-70 which
382 * corresponds to a transparency value of 1 - 0.3
383 * A value of 0.4 gives a combination of
384 * 0.4 * red(255, 0, 0) + 0.6 * cyan(0, 255, 255) = (102, 153, 153)
386 fr.setTransparency(0.4f);
387 Color c = finder.findFeatureColour(Color.cyan, seq, 10);
388 assertEquals(c, new Color(102, 153, 153));
391 @Test(groups = "Functional")
392 public void testFindFeatureColour_transparencyTwoFeatures()
394 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
395 Float.NaN, "MetalGroup"));
396 FeatureColour red = new FeatureColour(Color.red);
397 fr.setColour("Metal", red);
399 seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15,
400 Float.NaN, "DomainGroup"));
401 FeatureColour green = new FeatureColour(Color.green);
402 fr.setColour("Domain", green);
404 av.setShowSequenceFeatures(true);
407 * Domain (green) rendered above Metal (red) above background (cyan)
408 * 1) 0.6 * red(255, 0, 0) + 0.4 * cyan(0, 255, 255) = (153, 102, 102)
409 * 2) 0.6* green(0, 255, 0) + 0.4 * (153, 102, 102) = (61, 194, 41) rounded
411 fr.setTransparency(0.6f);
412 Color c = finder.findFeatureColour(Color.cyan, seq, 10);
413 assertEquals(c, new Color(61, 194, 41));
416 * now promote Metal above Domain
417 * - currently no way other than mimicking reordering of
418 * table in Feature Settings
419 * Metal (red) rendered above Domain (green) above background (cyan)
420 * 1) 0.6 * green(0, 255, 0) + 0.4 * cyan(0, 255, 255) = (0, 255, 102)
421 * 2) 0.6* red(255, 0, 0) + 0.4 * (0, 255, 102) = (153, 102, 41) rounded
423 FeatureSettingsBean[] data = new FeatureSettingsBean[2];
424 data[0] = new FeatureSettingsBean("Metal", red, null, true);
425 data[1] = new FeatureSettingsBean("Domain", green, null, true);
426 fr.setFeaturePriority(data);
427 c = finder.findFeatureColour(Color.cyan, seq, 10);
428 assertEquals(c, new Color(153, 102, 41));
431 * ..and turn off display of Metal
432 * Domain (green) above background (pink)
433 * 0.6 * green(0, 255, 0) + 0.4 * pink(255, 175, 175) = (102, 223, 70)
435 data[0] = new FeatureSettingsBean("Metal", red, null, false);
436 fr.setFeaturePriority(data);
437 c = finder.findFeatureColour(Color.pink, seq, 10);
438 assertEquals(c, new Color(102, 223, 70));
441 @Test(groups = "Functional")
442 public void testNoFeaturesDisplayed()
445 * no features on alignment to render
447 assertTrue(finder.noFeaturesDisplayed());
451 * it will be automatically set visible but we leave
452 * the viewport configured not to show features
454 av.setShowSequenceFeatures(false);
455 seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
456 Float.NaN, "MetalGroup"));
457 FeatureColour red = new FeatureColour(Color.red);
458 fr.setColour("Metal", red);
460 assertTrue(finder.noFeaturesDisplayed());
463 * turn on feature display
465 av.setShowSequenceFeatures(true);
466 assertFalse(finder.noFeaturesDisplayed());
469 * turn off display of Metal
471 FeatureSettingsBean[] data = new FeatureSettingsBean[1];
472 data[0] = new FeatureSettingsBean("Metal", red, null, false);
473 fr.setFeaturePriority(data);
474 assertTrue(finder.noFeaturesDisplayed());
477 * turn display of Metal back on
479 fr.setVisible("Metal");
480 assertFalse(finder.noFeaturesDisplayed());
483 * turn off MetalGroup - has no effect here since the group of a
484 * sequence feature instance is independent of its type
486 fr.setGroupVisibility("MetalGroup", false);
487 assertFalse(finder.noFeaturesDisplayed());
490 * a finder with no feature renderer
492 FeatureColourFinder finder2 = new FeatureColourFinder(null);
493 assertTrue(finder2.noFeaturesDisplayed());
496 @Test(groups = "Functional")
497 public void testFindFeatureColour_graduatedWithThreshold()
499 String kdFeature = "kd";
500 String metalFeature = "Metal";
501 seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity",
502 2, 2, 0f, "KdGroup"));
503 seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity",
504 4, 4, 5f, "KdGroup"));
505 seq.addSequenceFeature(new SequenceFeature(metalFeature, "Fe", 4, 4, 5f,
507 seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity",
508 7, 7, 10f, "KdGroup"));
511 * kd feature has graduated colour from 0 to 10
512 * above threshold value of 5
514 Color min = new Color(100, 50, 150);
515 Color max = new Color(200, 0, 100);
516 FeatureColourI fc = new FeatureColour(null, min, max, null, 0, 10);
517 fc.setAboveThreshold(true);
519 fr.setColour(kdFeature, fc);
520 FeatureColour green = new FeatureColour(Color.green);
521 fr.setColour(metalFeature, green);
525 * render order is kd above Metal
527 FeatureSettingsBean[] data = new FeatureSettingsBean[2];
528 data[0] = new FeatureSettingsBean(kdFeature, fc, null, true);
529 data[1] = new FeatureSettingsBean(metalFeature, green, null, true);
530 fr.setFeaturePriority(data);
532 av.setShowSequenceFeatures(true);
535 * position 2, column 1, score 0 - below threshold - default colour
537 Color c = finder.findFeatureColour(Color.blue, seq, 1);
538 assertEquals(c, Color.blue);
541 * position 4, column 3, score 5 - at threshold
542 * should return Green (colour of Metal feature)
544 c = finder.findFeatureColour(Color.blue, seq, 3);
545 assertEquals(c, Color.green);
548 * position 7, column 9, score 10 - maximum colour in range
550 c = finder.findFeatureColour(Color.blue, seq, 9);
551 assertEquals(c, max);
554 * now colour below threshold of 5
556 fc.setBelowThreshold(true);
559 * position 2, column 1, score 0 - min colour
561 c = finder.findFeatureColour(Color.blue, seq, 1);
562 assertEquals(c, min);
565 * position 4, column 3, score 5 - at threshold
566 * should return Green (colour of Metal feature)
568 c = finder.findFeatureColour(Color.blue, seq, 3);
569 assertEquals(c, Color.green);
572 * position 7, column 9, score 10 - above threshold - default colour
574 c = finder.findFeatureColour(Color.blue, seq, 9);
575 assertEquals(c, Color.blue);