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