eba5f59d76fcfae034215437b0add9b95a04f5e5
[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), ccs.applyConservation(colour, 7));
165     assertEquals(new Color(215, 195, 175), ccs.applyConservation(colour, 8));
166     assertEquals(new Color(195, 165, 135), ccs.applyConservation(colour, 9));
167     assertEquals(colour, ccs.applyConservation(colour, 10));
168     assertEquals(colour, ccs.applyConservation(colour, 11));
169     assertEquals(Color.WHITE, ccs.applyConservation(colour, 12));
170   }
171
172   @Test(groups = "Functional")
173   public void testFindColour_gapColour()
174   {
175     /*
176      * normally, a gap is coloured white
177      */
178     ResidueShader rs = new ResidueShader(new ZappoColourScheme());
179     assertEquals(Color.white, rs.findColour(' ', 7, null));
180
181     /*
182      * a User Colour Scheme may specify a bespoke gap colour
183      */
184     Color[] colours = new Color[ResidueProperties.maxProteinIndex + 1];
185     colours[5] = Color.blue; // Q colour
186     colours[23] = Color.red; // gap colour
187     ColourSchemeI cs = new UserColourScheme(colours);
188     rs = new ResidueShader(cs);
189   
190     assertEquals(Color.red, rs.findColour(' ', 7, null));
191     assertEquals(Color.blue, rs.findColour('Q', 7, null));
192   
193     /*
194      * stub Conservation to return a given consensus string
195      */
196     final String consSequence = "0123456789+*-";
197     Conservation cons = new Conservation(null,
198             Collections.<SequenceI> emptyList(), 0, 0)
199     {
200       @Override
201       public SequenceI getConsSequence()
202       {
203         return new Sequence("seq", consSequence);
204       }
205     };
206     rs.setConservation(cons);
207   
208     /*
209      * with 0% threshold, there should be no fading
210      */
211     rs.setConservationInc(0);
212     assertEquals(Color.red, rs.findColour(' ', 7, null));
213     assertEquals(Color.blue, rs.findColour('Q', 7, null));
214   
215     /*
216      * with 40% threshold, 'fade factor' is 
217      * (11-score)/10 * 40/20 = (11-score)/5
218      * so position 7, score 7 fades 80% of the way to white (255, 255, 255)
219      */
220     rs.setConservationInc(40);
221
222     /*
223      * gap colour is unchanged for Conservation
224      */
225     assertEquals(Color.red, rs.findColour(' ', 7, null));
226     assertEquals(Color.red, rs.findColour('-', 7, null));
227     assertEquals(Color.red, rs.findColour('.', 7, null));
228
229     /*
230      * residue colour is faded 80% of the way from
231      * blue(0, 0, 255) to white(255, 255, 255)
232      * making (204, 204, 255)
233      */
234     assertEquals(new Color(204, 204, 255), rs.findColour('Q', 7, null));
235
236     /*
237      * turn off By Conservation, apply Above Identity Threshold
238      * providing a stub Consensus that has modal residue "Q" with pid 60%
239      */
240     rs.setConservationApplied(false);
241     ProfilesI consensus = getStubConsensus("Q", 60f);
242     rs.setConsensus(consensus);
243
244     // with consensus pid (60) above threshold (50), colours are unchanged
245     rs.setThreshold(50, false);
246     assertEquals(Color.blue, rs.findColour('Q', 7, null));
247     assertEquals(Color.red, rs.findColour('-', 7, null));
248
249     // with consensus pid (60) below threshold (70),
250     // residue colour becomes white, gap colour is unchanged
251     rs.setThreshold(70, false);
252     assertEquals(Color.white, rs.findColour('Q', 7, null));
253     assertEquals(Color.red, rs.findColour('-', 7, null));
254   }
255
256   /**
257    * @param modalResidue
258    * @param pid
259    * @return
260    */
261   protected ProfilesI getStubConsensus(final String modalResidue,
262           final float pid)
263   {
264     ProfilesI consensus = new ProfilesI() {
265
266       @Override
267       public ProfileI get(int i)
268       {
269         return new ProfileI() {
270           @Override
271           public void setCounts(ResidueCount residueCounts)
272           {
273           }
274
275           @Override
276           public float getPercentageIdentity(boolean ignoreGaps)
277           {
278             return pid;
279           }
280
281           @Override
282           public ResidueCount getCounts()
283           {
284             return null;
285           }
286
287           @Override
288           public int getHeight()
289           {
290             return 0;
291           }
292
293           @Override
294           public int getGapped()
295           {
296             return 0;
297           }
298
299           @Override
300           public int getMaxCount()
301           {
302             return 0;
303           }
304
305           @Override
306           public String getModalResidue()
307           {
308             return modalResidue;
309           }
310
311           @Override
312           public int getNonGapped()
313           {
314             return 0;
315          }};
316       }
317
318       @Override
319       public int getStartColumn()
320       {
321         return 0;
322       }
323
324       @Override
325       public int getEndColumn()
326       {
327         return 0;
328       }
329       
330     };
331     return consensus;
332   }
333 }