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