df94d8c55e3ea5263eaeaff40a741008fda4ba39
[jalview.git] / test / jalview / renderer / ResidueShaderTest.java
1 package jalview.renderer;
2
3 import static org.testng.AssertJUnit.assertEquals;
4 import static org.testng.AssertJUnit.assertFalse;
5 import static org.testng.AssertJUnit.assertTrue;
6
7 import jalview.analysis.Conservation;
8 import jalview.datamodel.Profile;
9 import jalview.datamodel.ProfileI;
10 import jalview.datamodel.Profiles;
11 import jalview.datamodel.ProfilesI;
12 import jalview.datamodel.ResidueCount;
13 import jalview.datamodel.Sequence;
14 import jalview.datamodel.SequenceI;
15 import jalview.schemes.ColourSchemeI;
16 import jalview.schemes.PIDColourScheme;
17 import jalview.schemes.ResidueProperties;
18 import jalview.schemes.UserColourScheme;
19 import jalview.schemes.ZappoColourScheme;
20
21 import java.awt.Color;
22 import java.util.Collections;
23
24 import org.testng.annotations.Test;
25
26 public class ResidueShaderTest
27 {
28
29   @Test(groups = "Functional")
30   public void testAboveThreshold()
31   {
32     /*
33      * make up profiles for this alignment:
34      * AR-Q
35      * AR--
36      * SR-T
37      * SR-T
38      */
39     ProfileI[] profiles = new ProfileI[4];
40     profiles[0] = new Profile(4, 0, 2, "AS");
41     profiles[1] = new Profile(4, 0, 4, "R");
42     profiles[2] = new Profile(4, 4, 0, "");
43     profiles[3] = new Profile(4, 1, 2, "T");
44     ResidueShader ccs = new ResidueShader(new PIDColourScheme());
45     ccs.setConsensus(new Profiles(profiles));
46
47     /*
48      * no threshold
49      */
50     ccs.setThreshold(0, true);
51     assertTrue(ccs.aboveThreshold('a', 0));
52     assertTrue(ccs.aboveThreshold('S', 0));
53     assertTrue(ccs.aboveThreshold('W', 0));
54     assertTrue(ccs.aboveThreshold('R', 1));
55     assertTrue(ccs.aboveThreshold('W', 2));
56     assertTrue(ccs.aboveThreshold('t', 3));
57     assertTrue(ccs.aboveThreshold('Q', 3));
58
59     /*
60      * with threshold, include gaps
61      */
62     ccs.setThreshold(60, false);
63     assertFalse(ccs.aboveThreshold('a', 0));
64     assertFalse(ccs.aboveThreshold('S', 0));
65     assertTrue(ccs.aboveThreshold('R', 1));
66     assertFalse(ccs.aboveThreshold('W', 2));
67     assertFalse(ccs.aboveThreshold('t', 3)); // 50% < 60%
68
69     /*
70      * with threshold, ignore gaps
71      */
72     ccs.setThreshold(60, true);
73     assertFalse(ccs.aboveThreshold('a', 0));
74     assertFalse(ccs.aboveThreshold('S', 0));
75     assertTrue(ccs.aboveThreshold('R', 1));
76     assertFalse(ccs.aboveThreshold('W', 2));
77     assertTrue(ccs.aboveThreshold('t', 3)); // 67% > 60%
78   }
79
80   /**
81    * Test colour bleaching based on conservation score and conservation slider.
82    * Scores of 10 or 11 should leave colours unchanged. Gap is always white.
83    */
84   @Test(groups = "Functional")
85   public void testApplyConservation()
86   {
87     ResidueShader ccs = new ResidueShader(new PIDColourScheme());
88
89     // no conservation present - no fading
90     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 12));
91
92     /*
93      * stub Conservation to return a given consensus string
94      */
95     final String consSequence = "0123456789+*-";
96     Conservation cons = new Conservation(null,
97             Collections.<SequenceI> emptyList(), 0, 0)
98     {
99       @Override
100       public SequenceI getConsSequence()
101       {
102         return new Sequence("seq", consSequence);
103       }
104     };
105     ccs.setConservation(cons);
106
107     // column out of range:
108     assertEquals(Color.RED,
109             ccs.applyConservation(Color.RED, consSequence.length()));
110
111     /*
112      * with 100% threshold, 'fade factor' is 
113      * (11-score)/10 * 100/20 = (11-score)/2
114      * which is >= 1 for all scores i.e. all fade to white except +, *
115      */
116     ccs.setConservationInc(100);
117     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 0));
118     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 1));
119     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 2));
120     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 3));
121     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 4));
122     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 5));
123     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 6));
124     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 7));
125     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 8));
126     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 9));
127     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 10));
128     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 11));
129     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 12));
130
131     /*
132      * with 0% threshold, there should be no fading
133      */
134     ccs.setConservationInc(0);
135     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 0));
136     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 1));
137     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 2));
138     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 3));
139     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 4));
140     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 5));
141     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 6));
142     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 7));
143     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 8));
144     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 9));
145     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 10));
146     assertEquals(Color.RED, ccs.applyConservation(Color.RED, 11));
147     assertEquals(Color.WHITE, ccs.applyConservation(Color.RED, 12)); // gap
148
149     /*
150      * with 40% threshold, 'fade factor' is 
151      * (11-score)/10 * 40/20 = (11-score)/5
152      * which is {>1, >1, >1, >1, >1, >1, 1, 0.8, 0.6, 0.4} for score 0-9
153      * e.g. score 7 colour fades 80% of the way to white (255, 255, 255)
154      */
155     ccs.setConservationInc(40);
156     Color colour = new Color(155, 105, 55);
157     assertEquals(Color.WHITE, ccs.applyConservation(colour, 0));
158     assertEquals(Color.WHITE, ccs.applyConservation(colour, 1));
159     assertEquals(Color.WHITE, ccs.applyConservation(colour, 2));
160     assertEquals(Color.WHITE, ccs.applyConservation(colour, 3));
161     assertEquals(Color.WHITE, ccs.applyConservation(colour, 4));
162     assertEquals(Color.WHITE, ccs.applyConservation(colour, 5));
163     assertEquals(Color.WHITE, ccs.applyConservation(colour, 6));
164     assertEquals(new Color(235, 225, 215),
165             ccs.applyConservation(colour, 7));
166     assertEquals(new Color(215, 195, 175),
167             ccs.applyConservation(colour, 8));
168     assertEquals(new Color(195, 165, 135),
169             ccs.applyConservation(colour, 9));
170     assertEquals(colour, ccs.applyConservation(colour, 10));
171     assertEquals(colour, ccs.applyConservation(colour, 11));
172     assertEquals(Color.WHITE, ccs.applyConservation(colour, 12));
173   }
174
175   @Test(groups = "Functional")
176   public void testFindColour_gapColour()
177   {
178     /*
179      * normally, a gap is coloured white
180      */
181     ResidueShader rs = new ResidueShader(new ZappoColourScheme());
182     assertEquals(Color.white, rs.findColour(' ', 7, null));
183
184     /*
185      * a User Colour Scheme may specify a bespoke gap colour
186      */
187     Color[] colours = new Color[ResidueProperties.maxProteinIndex + 1];
188     colours[5] = Color.blue; // Q colour
189     colours[23] = Color.red; // gap colour
190     ColourSchemeI cs = new UserColourScheme(colours);
191     rs = new ResidueShader(cs);
192
193     assertEquals(Color.red, rs.findColour(' ', 7, null));
194     assertEquals(Color.blue, rs.findColour('Q', 7, null));
195
196     /*
197      * stub Conservation to return a given consensus string
198      */
199     final String consSequence = "0123456789+*-";
200     Conservation cons = new Conservation(null,
201             Collections.<SequenceI> emptyList(), 0, 0)
202     {
203       @Override
204       public SequenceI getConsSequence()
205       {
206         return new Sequence("seq", consSequence);
207       }
208     };
209     rs.setConservation(cons);
210
211     /*
212      * with 0% threshold, there should be no fading
213      */
214     rs.setConservationInc(0);
215     assertEquals(Color.red, rs.findColour(' ', 7, null));
216     assertEquals(Color.blue, rs.findColour('Q', 7, null));
217
218     /*
219      * with 40% threshold, 'fade factor' is 
220      * (11-score)/10 * 40/20 = (11-score)/5
221      * so position 7, score 7 fades 80% of the way to white (255, 255, 255)
222      */
223     rs.setConservationInc(40);
224
225     /*
226      * gap colour is unchanged for Conservation
227      */
228     assertEquals(Color.red, rs.findColour(' ', 7, null));
229     assertEquals(Color.red, rs.findColour('-', 7, null));
230     assertEquals(Color.red, rs.findColour('.', 7, null));
231
232     /*
233      * residue colour is faded 80% of the way from
234      * blue(0, 0, 255) to white(255, 255, 255)
235      * making (204, 204, 255)
236      */
237     assertEquals(new Color(204, 204, 255), rs.findColour('Q', 7, null));
238
239     /*
240      * turn off By Conservation, apply Above Identity Threshold
241      * providing a stub Consensus that has modal residue "Q" with pid 60%
242      */
243     rs.setConservationApplied(false);
244     ProfilesI consensus = getStubConsensus("Q", 60f);
245     rs.setConsensus(consensus);
246
247     // with consensus pid (60) above threshold (50), colours are unchanged
248     rs.setThreshold(50, false);
249     assertEquals(Color.blue, rs.findColour('Q', 7, null));
250     assertEquals(Color.red, rs.findColour('-', 7, null));
251
252     // with consensus pid (60) below threshold (70),
253     // residue colour becomes white, gap colour is unchanged
254     rs.setThreshold(70, false);
255     assertEquals(Color.white, rs.findColour('Q', 7, null));
256     assertEquals(Color.red, rs.findColour('-', 7, null));
257   }
258
259   /**
260    * @param modalResidue
261    * @param pid
262    * @return
263    */
264   protected ProfilesI getStubConsensus(final String modalResidue,
265           final float pid)
266   {
267     ProfilesI consensus = new ProfilesI()
268     {
269
270       @Override
271       public ProfileI get(int i)
272       {
273         return new ProfileI()
274         {
275           @Override
276           public void setCounts(ResidueCount residueCounts)
277           {
278           }
279
280           @Override
281           public float getPercentageIdentity(boolean ignoreGaps)
282           {
283             return pid;
284           }
285
286           @Override
287           public ResidueCount getCounts()
288           {
289             return null;
290           }
291
292           @Override
293           public int getHeight()
294           {
295             return 0;
296           }
297
298           @Override
299           public int getGapped()
300           {
301             return 0;
302           }
303
304           @Override
305           public int getMaxCount()
306           {
307             return 0;
308           }
309
310           @Override
311           public String getModalResidue()
312           {
313             return modalResidue;
314           }
315
316           @Override
317           public int getNonGapped()
318           {
319             return 0;
320           }
321         };
322       }
323
324       @Override
325       public int getStartColumn()
326       {
327         return 0;
328       }
329
330       @Override
331       public int getEndColumn()
332       {
333         return 0;
334       }
335
336     };
337     return consensus;
338   }
339 }