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