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