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