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