43382f8c6bf518ede702ad00d7698aa38464758a
[jalview.git] / test / jalview / renderer / seqfeatures / FeatureColourFinderTest.java
1 package jalview.renderer.seqfeatures;
2
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;
8
9 import jalview.api.FeatureColourI;
10 import jalview.bin.Cache;
11 import jalview.datamodel.SequenceFeature;
12 import jalview.datamodel.SequenceI;
13 import jalview.gui.AlignFrame;
14 import jalview.gui.AlignViewport;
15 import jalview.gui.FeatureRenderer;
16 import jalview.io.DataSourceType;
17 import jalview.io.FileLoader;
18 import jalview.schemes.FeatureColour;
19 import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
20
21 import java.awt.Color;
22 import java.util.List;
23
24 import org.testng.annotations.BeforeMethod;
25 import org.testng.annotations.BeforeTest;
26 import org.testng.annotations.Test;
27
28 /**
29  * Unit tests for feature colour determination, including but not limited to
30  * <ul>
31  * <li>gap position</li>
32  * <li>no features present</li>
33  * <li>features present but show features turned off</li>
34  * <li>features displayed but selected feature turned off</li>
35  * <li>features displayed but feature group turned off</li>
36  * <li>feature displayed but none at the specified position</li>
37  * <li>multiple features at position, with no transparency</li>
38  * <li>multiple features at position, with transparency</li>
39  * <li>score graduated feature colour</li>
40  * <li>contact feature start at the selected position</li>
41  * <li>contact feature end at the selected position</li>
42  * <li>contact feature straddling the selected position (not shown)</li>
43  * </ul>
44  */
45 public class FeatureColourFinderTest
46 {
47   private AlignViewport av;
48
49   private SequenceI seq;
50
51   private FeatureColourFinder finder;
52
53   private AlignFrame af;
54
55   private FeatureRenderer fr;
56
57   @BeforeTest(alwaysRun = true)
58   public void setUp()
59   {
60     Cache.initLogger();
61     // aligned column 8 is sequence position 6
62     String s = ">s1\nABCDE---FGHIJKLMNOPQRSTUVWXYZ\n";
63     af = new FileLoader().LoadFileWaitTillLoaded(s,
64             DataSourceType.PASTE);
65     av = af.getViewport();
66     seq = av.getAlignment().getSequenceAt(0);
67     fr = af.getFeatureRenderer();
68     finder = new FeatureColourFinder(fr);
69   }
70
71   /**
72    * Clear down any sequence features before each test (not as easy as it
73    * sounds...)
74    */
75   @BeforeMethod(alwaysRun = true)
76   public void setUpBeforeTest()
77   {
78     //required for Windows, only when in batch mode?
79           setUp();
80     List<SequenceFeature> sfs = seq.getSequenceFeatures();
81     for (SequenceFeature sf : sfs)
82     {
83       seq.deleteFeature(sf);
84     }
85     fr.findAllFeatures(true);
86
87     /*
88      * reset all feature groups to visible
89      */
90     for (String group : fr.getGroups(false))
91     {
92       fr.setGroupVisibility(group, true);
93     }
94
95     fr.clearRenderOrder();
96     av.setShowSequenceFeatures(true);
97   }
98
99   @Test(groups = "Functional")
100   public void testFindFeatureColour_noFeatures()
101   {
102     av.setShowSequenceFeatures(false);
103     Color c = finder.findFeatureColour(Color.blue, seq, 10);
104     assertEquals(c, Color.blue);
105
106     av.setShowSequenceFeatures(true);
107     c = finder.findFeatureColour(Color.blue, seq, 10);
108     assertEquals(c, Color.blue);
109   }
110
111   @Test(groups = "Functional")
112   public void testFindFeatureColour_noFeaturesShown()
113   {
114     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
115             Float.NaN, "MetalGroup"));
116     fr.featuresAdded();
117     av.setShowSequenceFeatures(false);
118     Color c = finder.findFeatureColour(Color.blue, seq, 10);
119     assertEquals(c, Color.blue);
120   }
121
122   @Test(groups = "Functional")
123   public void testFindFeatureColour_singleFeatureAtPosition()
124   {
125     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
126             Float.NaN, "MetalGroup"));
127     fr.setColour("Metal", new FeatureColour(Color.red));
128     fr.featuresAdded();
129     av.setShowSequenceFeatures(true);
130     Color c = finder.findFeatureColour(Color.blue, seq, 10);
131     assertEquals(c, Color.red);
132   }
133
134   /**
135    * feature colour at a gap is null (not white) - a user defined colour scheme
136    * can then provide a bespoke gap colour if configured to do so
137    */
138   @Test(groups = "Functional")
139   public void testFindFeatureColour_gapPosition()
140   {
141     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12, 0f,
142             null));
143     fr.setColour("Metal", new FeatureColour(Color.red));
144     fr.featuresAdded();
145     av.setShowSequenceFeatures(true);
146     Color c = finder.findFeatureColour(null, seq, 6);
147     assertNull(c);
148   }
149
150   @Test(groups = "Functional")
151   public void testFindFeatureColour_multipleFeaturesAtPositionNoTransparency()
152   {
153     /*
154      * featuresAdded -> FeatureRendererModel.updateRenderOrder which adds any
155      * new features 'on top' (but reverses the order of any added features)
156      */
157     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
158             Float.NaN, "MetalGroup"));
159     FeatureColour red = new FeatureColour(Color.red);
160     fr.setColour("Metal", red);
161     fr.featuresAdded();
162     seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15,
163             Float.NaN, "DomainGroup"));
164     FeatureColour green = new FeatureColour(Color.green);
165     fr.setColour("Domain", green);
166     fr.featuresAdded();
167     av.setShowSequenceFeatures(true);
168
169     /*
170      * expect Domain (green) to be rendered above Metal (red)
171      */
172     Color c = finder.findFeatureColour(Color.blue, seq, 10);
173     assertEquals(c, Color.green);
174
175     /*
176      * now promote Metal above Domain
177      * - currently no way other than mimicking reordering of
178      * table in Feature Settings
179      */
180     FeatureSettingsBean[] data = new FeatureSettingsBean[2];
181     data[0] = new FeatureSettingsBean("Metal", red, null, true);
182     data[1] = new FeatureSettingsBean("Domain", green, null, true);
183     fr.setFeaturePriority(data);
184     c = finder.findFeatureColour(Color.blue, seq, 10);
185     assertEquals(c, Color.red);
186
187     /*
188      * ..and turn off display of Metal
189      */
190     data[0] = new FeatureSettingsBean("Metal", red, null, false);
191     fr.setFeaturePriority(data);
192     c = finder.findFeatureColour(Color.blue, seq, 10);
193     assertEquals(c, Color.green);
194   }
195
196   @Test(groups = "Functional")
197   public void testFindFeatureColour_singleFeatureNotAtPosition()
198   {
199     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 8, 12,
200             Float.NaN, "MetalGroup"));
201     fr.setColour("Metal", new FeatureColour(Color.red));
202     fr.featuresAdded();
203     av.setShowSequenceFeatures(true);
204     // column 2 = sequence position 3
205     Color c = finder.findFeatureColour(Color.blue, seq, 2);
206     assertEquals(c, Color.blue);
207   }
208
209   @Test(groups = "Functional")
210   public void testFindFeatureColour_featureTypeNotDisplayed()
211   {
212     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
213             Float.NaN, "MetalGroup"));
214     FeatureColour red = new FeatureColour(Color.red);
215     fr.setColour("Metal", red);
216     fr.featuresAdded();
217     av.setShowSequenceFeatures(true);
218     Color c = finder.findFeatureColour(Color.blue, seq, 10);
219     assertEquals(c, Color.red);
220
221     /*
222      * turn off display of Metal - is this the easiest way to do it??
223      */
224     FeatureSettingsBean[] data = new FeatureSettingsBean[1];
225     data[0] = new FeatureSettingsBean("Metal", red, null, false);
226     fr.setFeaturePriority(data);
227     c = finder.findFeatureColour(Color.blue, seq, 10);
228     assertEquals(c, Color.blue);
229
230     /*
231      * turn display of Metal back on
232      */
233     data[0] = new FeatureSettingsBean("Metal", red, null, true);
234     fr.setFeaturePriority(data);
235     c = finder.findFeatureColour(Color.blue, seq, 10);
236     assertEquals(c, Color.red);
237   }
238
239   @Test(groups = "Functional")
240   public void testFindFeatureColour_featureGroupNotDisplayed()
241   {
242     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
243             Float.NaN, "MetalGroup"));
244     FeatureColour red = new FeatureColour(Color.red);
245     fr.setColour("Metal", red);
246     fr.featuresAdded();
247     av.setShowSequenceFeatures(true);
248     Color c = finder.findFeatureColour(Color.blue, seq, 10);
249     assertEquals(c, Color.red);
250
251     /*
252      * turn off display of MetalGroup
253      */
254     fr.setGroupVisibility("MetalGroup", false);
255     c = finder.findFeatureColour(Color.blue, seq, 10);
256     assertEquals(c, Color.blue);
257
258     /*
259      * turn display of MetalGroup back on
260      */
261     fr.setGroupVisibility("MetalGroup", true);
262     c = finder.findFeatureColour(Color.blue, seq, 10);
263     assertEquals(c, Color.red);
264   }
265
266   @Test(groups = "Functional")
267   public void testFindFeatureColour_contactFeature()
268   {
269     /*
270      * currently contact feature == type "Disulphide Bond" or "Disulfide Bond" !!
271      */
272     seq.addSequenceFeature(new SequenceFeature("Disulphide Bond",
273             "Contact", 2, 12, Float.NaN, "Disulphide"));
274     fr.setColour("Disulphide Bond", new FeatureColour(Color.red));
275     fr.featuresAdded();
276     av.setShowSequenceFeatures(true);
277
278     /*
279      * Contact positions are residues 2 and 12
280      * which are columns 1 and 14
281      * positions in between don't count for a contact feature!
282      */
283     Color c = finder.findFeatureColour(Color.blue, seq, 10);
284     assertEquals(c, Color.blue);
285     c = finder.findFeatureColour(Color.blue, seq, 8);
286     assertEquals(c, Color.blue);
287     c = finder.findFeatureColour(Color.blue, seq, 1);
288     assertEquals(c, Color.red);
289     c = finder.findFeatureColour(Color.blue, seq, 14);
290     assertEquals(c, Color.red);
291   }
292
293   @Test(groups = "Functional")
294   public void testFindFeatureAtEnd()
295   {
296     /*
297      * terminal residue feature
298      */
299     seq.addSequenceFeature(new SequenceFeature("PDBRESNUM", "pdb res 1",
300             seq.getEnd(), seq.getEnd(), Float.NaN, "1seq.pdb"));
301     fr.setColour("PDBRESNUM", new FeatureColour(Color.red));
302     fr.featuresAdded();
303     av.setShowSequenceFeatures(true);
304
305     /*
306      * final column should have PDBRESNUM feature, the others not
307      */
308     Color c = finder.findFeatureColour(Color.blue, seq,
309             seq.getLength() - 2);
310     assertNotEquals(c, Color.red);
311     c = finder.findFeatureColour(Color.blue, seq, seq.getLength() - 1);
312     assertEquals(c, Color.red);
313   }
314
315   @Test(groups = "Functional")
316   public void testFindFeatureColour_graduatedFeatureColour()
317   {
318     seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 2,
319             2, 0f, "KdGroup"));
320     seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 4,
321             4, 5f, "KdGroup"));
322     seq.addSequenceFeature(new SequenceFeature("kd", "hydrophobicity", 7,
323             7, 10f, "KdGroup"));
324
325     /*
326      * graduated colour from 0 to 10
327      */
328     Color min = new Color(100, 50, 150);
329     Color max = new Color(200, 0, 100);
330     FeatureColourI fc = new FeatureColour(null, min, max, null, 0, 10);
331     fr.setColour("kd", fc);
332     fr.featuresAdded();
333     av.setShowSequenceFeatures(true);
334
335     /*
336      * position 2, column 1, score 0 - minimum colour in range
337      */
338     Color c = finder.findFeatureColour(Color.blue, seq, 1);
339     assertEquals(c, min);
340
341     /*
342      * position 7, column 9, score 10 - maximum colour in range
343      */
344     c = finder.findFeatureColour(Color.blue, seq, 9);
345     assertEquals(c, max);
346
347     /*
348      * position 4, column 3, score 5 - half way from min to max
349      */
350     c = finder.findFeatureColour(Color.blue, seq, 3);
351     assertEquals(c, new Color(150, 25, 125));
352   }
353
354   @Test(groups = "Functional")
355   public void testFindFeatureColour_transparencySingleFeature()
356   {
357     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
358             Float.NaN, "MetalGroup"));
359     FeatureColour red = new FeatureColour(Color.red);
360     fr.setColour("Metal", red);
361     fr.featuresAdded();
362     av.setShowSequenceFeatures(true);
363   
364     /*
365      * the FeatureSettings transparency slider has range 0-70 which
366      * corresponds to a transparency value of 1 - 0.3
367      * A value of 0.4 gives a combination of
368      * 0.4 * red(255, 0, 0) + 0.6 * cyan(0, 255, 255) = (102, 153, 153)
369      */
370     fr.setTransparency(0.4f);
371     Color c = finder.findFeatureColour(Color.cyan, seq, 10);
372     assertEquals(c, new Color(102, 153, 153));
373   }
374
375   @Test(groups = "Functional")
376   public void testFindFeatureColour_transparencyTwoFeatures()
377   {
378     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
379             Float.NaN, "MetalGroup"));
380     FeatureColour red = new FeatureColour(Color.red);
381     fr.setColour("Metal", red);
382     fr.featuresAdded();
383     seq.addSequenceFeature(new SequenceFeature("Domain", "Domain", 4, 15,
384             Float.NaN, "DomainGroup"));
385     FeatureColour green = new FeatureColour(Color.green);
386     fr.setColour("Domain", green);
387     fr.featuresAdded();
388     av.setShowSequenceFeatures(true);
389   
390     /*
391      * Domain (green) rendered above Metal (red) above background (cyan)
392      * 1) 0.6 * red(255, 0, 0) + 0.4 * cyan(0, 255, 255) = (153, 102, 102)
393      * 2) 0.6* green(0, 255, 0) + 0.4 * (153, 102, 102) = (61, 194, 41) rounded
394      */
395     fr.setTransparency(0.6f);
396     Color c = finder.findFeatureColour(Color.cyan, seq, 10);
397     assertEquals(c, new Color(61, 194, 41));
398   
399     /*
400      * now promote Metal above Domain
401      * - currently no way other than mimicking reordering of
402      * table in Feature Settings
403      * Metal (red) rendered above Domain (green) above background (cyan)
404      * 1) 0.6 * green(0, 255, 0) + 0.4 * cyan(0, 255, 255) = (0, 255, 102)
405      * 2) 0.6* red(255, 0, 0) + 0.4 * (0, 255, 102) = (153, 102, 41) rounded
406      */
407     FeatureSettingsBean[] data = new FeatureSettingsBean[2];
408     data[0] = new FeatureSettingsBean("Metal", red, null, true);
409     data[1] = new FeatureSettingsBean("Domain", green, null, true);
410     fr.setFeaturePriority(data);
411     c = finder.findFeatureColour(Color.cyan, seq, 10);
412     assertEquals(c, new Color(153, 102, 41));
413   
414     /*
415      * ..and turn off display of Metal
416      * Domain (green) above background (pink)
417      * 0.6 * green(0, 255, 0) + 0.4 * pink(255, 175, 175) = (102, 223, 70)
418      */
419     data[0] = new FeatureSettingsBean("Metal", red, null, false);
420     fr.setFeaturePriority(data);
421     c = finder.findFeatureColour(Color.pink, seq, 10);
422     assertEquals(c, new Color(102, 223, 70));
423   }
424
425   @Test(groups = "Functional")
426   public void testNoFeaturesDisplayed()
427   {
428     /*
429      * no features on alignment to render
430      */
431     assertTrue(finder.noFeaturesDisplayed());
432
433     /*
434      * add a feature
435      * it will be automatically set visible but we leave
436      * the viewport configured not to show features
437      */
438     av.setShowSequenceFeatures(false);
439     seq.addSequenceFeature(new SequenceFeature("Metal", "Metal", 2, 12,
440             Float.NaN, "MetalGroup"));
441     FeatureColour red = new FeatureColour(Color.red);
442     fr.setColour("Metal", red);
443     fr.featuresAdded();
444     assertTrue(finder.noFeaturesDisplayed());
445
446     /*
447      * turn on feature display
448      */
449     av.setShowSequenceFeatures(true);
450     assertFalse(finder.noFeaturesDisplayed());
451
452     /*
453      * turn off display of Metal
454      */
455     FeatureSettingsBean[] data = new FeatureSettingsBean[1];
456     data[0] = new FeatureSettingsBean("Metal", red, null, false);
457     fr.setFeaturePriority(data);
458     assertTrue(finder.noFeaturesDisplayed());
459
460     /*
461      * turn display of Metal back on
462      */
463     fr.setVisible("Metal");
464     assertFalse(finder.noFeaturesDisplayed());
465
466     /*
467      * turn off MetalGroup - has no effect here since the group of a
468      * sequence feature instance is independent of its type
469      */
470     fr.setGroupVisibility("MetalGroup", false);
471     assertFalse(finder.noFeaturesDisplayed());
472
473     /*
474      * a finder with no feature renderer
475      */
476     FeatureColourFinder finder2 = new FeatureColourFinder(null);
477     assertTrue(finder2.noFeaturesDisplayed());
478   }
479
480   @Test(groups = "Functional")
481   public void testFindFeatureColour_graduatedWithThreshold()
482   {
483     String kdFeature = "kd";
484     String metalFeature = "Metal";
485     seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 2,
486             2, 0f, "KdGroup"));
487     seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 4,
488             4, 5f, "KdGroup"));
489     seq.addSequenceFeature(new SequenceFeature(metalFeature, "Fe", 4, 4,
490             5f, "MetalGroup"));
491     seq.addSequenceFeature(new SequenceFeature(kdFeature, "hydrophobicity", 7,
492             7, 10f, "KdGroup"));
493   
494     /*
495      * kd feature has graduated colour from 0 to 10
496      * above threshold value of 5
497      */
498     Color min = new Color(100, 50, 150);
499     Color max = new Color(200, 0, 100);
500     FeatureColourI fc = new FeatureColour(null, min, max, null, 0, 10);
501     fc.setAboveThreshold(true);
502     fc.setThreshold(5f);
503     fr.setColour(kdFeature, fc);
504     FeatureColour green = new FeatureColour(Color.green);
505     fr.setColour(metalFeature, green);
506     fr.featuresAdded();
507
508     /*
509      * render order is kd above Metal
510      */
511     FeatureSettingsBean[] data = new FeatureSettingsBean[2];
512     data[0] = new FeatureSettingsBean(kdFeature, fc, null, true);
513     data[1] = new FeatureSettingsBean(metalFeature, green, null, true);
514     fr.setFeaturePriority(data);
515
516     av.setShowSequenceFeatures(true);
517   
518     /*
519      * position 2, column 1, score 0 - below threshold - default colour
520      */
521     Color c = finder.findFeatureColour(Color.blue, seq, 1);
522     assertEquals(c, Color.blue);
523
524     /*
525      * position 4, column 3, score 5 - at threshold
526      * should return Green (colour of Metal feature)
527      */
528     c = finder.findFeatureColour(Color.blue, seq, 3);
529     assertEquals(c, Color.green);
530   
531     /*
532      * position 7, column 9, score 10 - maximum colour in range
533      */
534     c = finder.findFeatureColour(Color.blue, seq, 9);
535     assertEquals(c, max);
536
537     /*
538      * now colour below threshold of 5
539      */
540     fc.setBelowThreshold(true);
541
542     /*
543      * position 2, column 1, score 0 - min colour
544      */
545     c = finder.findFeatureColour(Color.blue, seq, 1);
546     assertEquals(c, min);
547
548     /*
549      * position 4, column 3, score 5 - at threshold
550      * should return Green (colour of Metal feature)
551      */
552     c = finder.findFeatureColour(Color.blue, seq, 3);
553     assertEquals(c, Color.green);
554
555     /*
556      * position 7, column 9, score 10 - above threshold - default colour
557      */
558     c = finder.findFeatureColour(Color.blue, seq, 9);
559     assertEquals(c, Color.blue);
560   }
561 }