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