21c401a447ee1d0cbf52a97dd56deedb57b65cc8
[jalview.git] / src / jalview / appletgui / FeatureRenderer.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 package jalview.appletgui;\r
20 \r
21 import jalview.datamodel.*;\r
22 \r
23 import java.awt.*;\r
24 \r
25 import java.util.*;\r
26 \r
27 /**\r
28  * DOCUMENT ME!\r
29  *\r
30  * @author $author$\r
31  * @version $Revision$\r
32  */\r
33 public class FeatureRenderer\r
34 {\r
35     AlignViewport av;\r
36 \r
37 \r
38     // A higher level for grouping features of a\r
39     // particular type\r
40     Hashtable featureGroups = null;\r
41 \r
42     // This is actually an Integer held in the hashtable,\r
43     // Retrieved using the key feature type\r
44     Object currentColour;\r
45 \r
46     String [] renderOrder;\r
47 \r
48     boolean drawText = true;\r
49     FontMetrics fm;\r
50     int charOffset;\r
51 \r
52     /**\r
53      * Creates a new FeatureRenderer object.\r
54      *\r
55      * @param av DOCUMENT ME!\r
56      */\r
57     public FeatureRenderer(AlignViewport av)\r
58     {\r
59         this.av = av;\r
60         initColours();\r
61     }\r
62 \r
63 \r
64     public Color findFeatureColour(Color initialCol, SequenceI seq, int i)\r
65     {\r
66       if(!av.showSequenceFeatures)\r
67         return initialCol;\r
68 \r
69         lastSequence = seq;\r
70         sequenceFeatures = lastSequence.getSequenceFeatures();\r
71         if(sequenceFeatures==null)\r
72           return initialCol;\r
73 \r
74         sfSize = sequenceFeatures.length;\r
75 \r
76       if(jalview.util.Comparison.isGap(lastSequence.getCharAt(i)))\r
77         return Color.white;\r
78 \r
79       currentColour = null;\r
80 \r
81       drawSequence(null, lastSequence, lastSequence.findPosition(i), -1,-1, -1, -1);\r
82 \r
83       if(currentColour==null)\r
84         return initialCol;\r
85 \r
86       return new Color( ((Integer)currentColour).intValue() );\r
87     }\r
88     /**\r
89      * This is used by the Molecule Viewer to get the accurate colour\r
90      * of the rendered sequence\r
91      */\r
92 \r
93     boolean overview = false;\r
94 \r
95     int white = Color.white.getRGB();\r
96     public int findFeatureColour(int initialCol, int seqIndex, int column)\r
97     {\r
98       if(!av.showSequenceFeatures)\r
99         return initialCol;\r
100 \r
101       if(seqIndex!=lastSequenceIndex)\r
102       {\r
103         lastSequence = av.alignment.getSequenceAt(seqIndex);\r
104         lastSequenceIndex = seqIndex;\r
105         sequenceFeatures = lastSequence.getSequenceFeatures();\r
106         if(sequenceFeatures==null)\r
107           return initialCol;\r
108 \r
109         sfSize = sequenceFeatures.length;\r
110       }\r
111 \r
112 \r
113       if(jalview.util.Comparison.isGap(lastSequence.getCharAt(column)))\r
114         return Color.white.getRGB();\r
115 \r
116       currentColour = null;\r
117 \r
118       drawSequence(null, lastSequence, lastSequence.findPosition(column), -1,-1, -1, -1);\r
119 \r
120       if(currentColour==null)\r
121         return initialCol;\r
122 \r
123       return  ((Integer)currentColour).intValue();\r
124     }\r
125 \r
126 \r
127     /**\r
128      * DOCUMENT ME!\r
129      *\r
130      * @param g DOCUMENT ME!\r
131      * @param seq DOCUMENT ME!\r
132      * @param sg DOCUMENT ME!\r
133      * @param start DOCUMENT ME!\r
134      * @param end DOCUMENT ME!\r
135      * @param x1 DOCUMENT ME!\r
136      * @param y1 DOCUMENT ME!\r
137      * @param width DOCUMENT ME!\r
138      * @param height DOCUMENT ME!\r
139      */\r
140    // String type;\r
141    // SequenceFeature sf;\r
142     int lastSequenceIndex=-1;\r
143     SequenceI lastSequence;\r
144     SequenceFeature [] sequenceFeatures;\r
145     int sfSize, sfindex, spos, epos;\r
146 \r
147     public void drawSequence(Graphics g, SequenceI seq,\r
148                              int start, int end, int y1, int width, int height)\r
149     {\r
150       if (   seq.getSequenceFeatures() == null\r
151           || seq.getSequenceFeatures().length==0)\r
152         return;\r
153 \r
154 \r
155       if(g!=null)\r
156         fm = g.getFontMetrics();\r
157 \r
158       if (av.featuresDisplayed == null || renderOrder==null)\r
159        {\r
160          findAllFeatures();\r
161          sequenceFeatures = seq.getSequenceFeatures();\r
162          sfSize = sequenceFeatures.length;\r
163        }\r
164        if(lastSequence==null || seq!=lastSequence)\r
165       {\r
166         lastSequence = seq;\r
167         sequenceFeatures = seq.getSequenceFeatures();\r
168         sfSize = sequenceFeatures.length;\r
169       }\r
170       if(!overview)\r
171       {\r
172         spos = lastSequence.findPosition(start);\r
173         epos = lastSequence.findPosition(end);\r
174       }\r
175       String type;\r
176       for(int renderIndex=0; renderIndex<renderOrder.length; renderIndex++)\r
177        {\r
178         type =  renderOrder[renderIndex];\r
179 \r
180         // loop through all features in sequence to find\r
181         // current feature to render\r
182         for (sfindex = 0; sfindex < sfSize; sfindex++)\r
183         {\r
184           if (!sequenceFeatures[sfindex].type.equals(type))\r
185             continue;\r
186 \r
187           if (featureGroups != null\r
188               && sequenceFeatures[sfindex].featureGroup != null\r
189               &&\r
190               featureGroups.containsKey(sequenceFeatures[sfindex].featureGroup)\r
191               &&\r
192               ! ( (Boolean) featureGroups.get(sequenceFeatures[sfindex].featureGroup)).\r
193               booleanValue())\r
194           {\r
195             continue;\r
196           }\r
197 \r
198           if (!overview && (sequenceFeatures[sfindex].getBegin() > epos\r
199                             || sequenceFeatures[sfindex].getEnd() < spos))\r
200             continue;\r
201 \r
202           if (overview)\r
203           {\r
204 \r
205             if (sequenceFeatures[sfindex].begin - 1 <= start &&\r
206                 sequenceFeatures[sfindex].end + 1 >= start)\r
207             {\r
208               currentColour = av.featuresDisplayed.get(sequenceFeatures[sfindex].\r
209                   type);\r
210             }\r
211 \r
212           }\r
213           else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))\r
214           {\r
215 \r
216             renderFeature(g, seq,\r
217                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
218                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
219                           new Color( ( (Integer) av.featuresDisplayed.get(\r
220                 sequenceFeatures[sfindex].type)).intValue()),\r
221                           start, end, y1, width, height);\r
222             renderFeature(g, seq,\r
223                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
224                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
225                           new Color( ( (Integer) av.featuresDisplayed.get(\r
226                 sequenceFeatures[sfindex].type)).intValue()),\r
227                           start, end, y1, width, height);\r
228 \r
229           }\r
230           else\r
231             renderFeature(g, seq,\r
232                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
233                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
234                           getColour(sequenceFeatures[sfindex].type),\r
235                           start, end, y1, width, height);\r
236 \r
237 \r
238         }\r
239 \r
240       }\r
241     }\r
242 \r
243 \r
244     char s;\r
245     int i;\r
246     void renderFeature(Graphics g, SequenceI seq,\r
247                        int fstart, int fend, Color featureColour, int start, int end,  int y1, int width, int height)\r
248     {\r
249 \r
250       if (((fstart <= end) && (fend >= start)))\r
251       {\r
252         if (fstart < start)\r
253         { // fix for if the feature we have starts before the sequence start,\r
254           fstart = start; // but the feature end is still valid!!\r
255         }\r
256 \r
257         if (fend >= end)\r
258         {\r
259           fend = end;\r
260         }\r
261 \r
262           for (i = fstart; i <= fend; i++)\r
263           {\r
264             s = seq.getSequence().charAt(i);\r
265 \r
266             if (jalview.util.Comparison.isGap(s))\r
267             {\r
268               continue;\r
269             }\r
270 \r
271             g.setColor(featureColour);\r
272 \r
273             g.fillRect( (i - start) * width, y1, width, height);\r
274 \r
275             g.setColor(Color.white);\r
276             charOffset = (width - fm.charWidth(s)) / 2;\r
277             g.drawString(String.valueOf(s),\r
278                          charOffset + (width * (i - start)),\r
279                          (y1 + height) - height / 5); //pady = height / 5;\r
280 \r
281           }\r
282       }\r
283     }\r
284 \r
285     void findAllFeatures()\r
286     {\r
287       av.featuresDisplayed = new Hashtable();\r
288       for (int i = 0; i < av.alignment.getHeight(); i++)\r
289       {\r
290         SequenceFeature [] features = av.alignment.getSequenceAt(i).getSequenceFeatures();\r
291 \r
292         if (features == null)\r
293           continue;\r
294 \r
295         int index = 0;\r
296         while (index < features.length)\r
297         {\r
298           if (!av.featuresDisplayed.containsKey(features[index].getType()))\r
299           {\r
300             av.featuresDisplayed.put(features[index].getType(),\r
301                                   new Integer( getColour(features[index].getType()).getRGB()) );\r
302           }\r
303           index++;\r
304         }\r
305       }\r
306 \r
307       renderOrder = new String[av.featuresDisplayed.size()];\r
308       Enumeration en = av.featuresDisplayed.keys();\r
309       int i = 0;\r
310       while(en.hasMoreElements())\r
311       {\r
312         renderOrder[i] = en.nextElement().toString();\r
313         i++;\r
314       }\r
315     }\r
316 \r
317     public Color getColour(String featureType)\r
318     {\r
319       return (Color)featureColours.get(featureType);\r
320     }\r
321 \r
322     public void addNewFeature(String name, Color col)\r
323     {\r
324 \r
325       setColour(name, col);\r
326       if(av.featuresDisplayed==null)\r
327         av.featuresDisplayed = new Hashtable();\r
328 \r
329 \r
330       av.featuresDisplayed.put(name, "NOGROUP");\r
331     }\r
332 \r
333     public void setColour(String featureType, Color col)\r
334     {\r
335       featureColours.put(featureType, col);\r
336     }\r
337 \r
338     public void setFeaturePriority(Object [][] data)\r
339     {\r
340       // The feature table will display high priority\r
341       // features at the top, but theses are the ones\r
342       // we need to render last, so invert the data\r
343       av.featuresDisplayed.clear();\r
344 \r
345       renderOrder = new String[data.length];\r
346 \r
347       if(data.length>0)\r
348       for(int i=0; i<data.length; i++)\r
349       {\r
350        String type = data[i][0].toString();\r
351        setColour(type, (Color)data[i][1]);\r
352        if( ((Boolean)data[i][2]).booleanValue() )\r
353        {\r
354          av.featuresDisplayed.put(type, new Integer(getColour(type).getRGB()));\r
355        }\r
356 \r
357        renderOrder[i] = type;\r
358       }\r
359 \r
360     }\r
361 \r
362     Hashtable featureColours = new Hashtable();\r
363     void initColours()\r
364     {\r
365       featureColours.put("active site", new Color(255, 75, 0));\r
366       featureColours.put("binding site", new Color(245, 85, 0));\r
367       featureColours.put("calcium-binding region", new Color(235, 95, 0));\r
368       featureColours.put("chain", new Color(225, 105, 0));\r
369       featureColours.put("coiled-coil region", new Color(215, 115, 0));\r
370       featureColours.put("compositionally biased region", new Color(205, 125, 0));\r
371       featureColours.put("cross-link", new Color(195, 135, 0));\r
372       featureColours.put("disulfide bond", new Color(185, 145, 0));\r
373       featureColours.put("DNA-binding region", new Color(175, 155, 0));\r
374       featureColours.put("domain", new Color(165, 165, 0));\r
375       featureColours.put("glycosylation site", new Color(155, 175, 0));\r
376       featureColours.put("helix", new Color(145, 185, 0));\r
377       featureColours.put("initiator methionine", new Color(135, 195, 5));\r
378       featureColours.put("lipid moiety-binding region", new Color(125, 205, 15));\r
379       featureColours.put("metal ion-binding site", new Color(115, 215, 25));\r
380       featureColours.put("modified residue", new Color(105, 225, 35));\r
381       featureColours.put("mutagenesis site", new Color(95, 235, 45));\r
382       featureColours.put("non-consecutive residues", new Color(85, 245, 55));\r
383       featureColours.put("non-terminal residue", new Color(75, 255, 65));\r
384       featureColours.put("nucleotide phosphate-binding region",\r
385                          new Color(65, 245, 75));\r
386       featureColours.put("peptide", new Color(55, 235, 85));\r
387       featureColours.put("propeptide", new Color(45, 225, 95));\r
388       featureColours.put("region of interest", new Color(35, 215, 105));\r
389       featureColours.put("repeat", new Color(25, 205, 115));\r
390       featureColours.put("selenocysteine", new Color(15, 195, 125));\r
391       featureColours.put("sequence conflict", new Color(5, 185, 135));\r
392       featureColours.put("sequence variant", new Color(0, 175, 145));\r
393       featureColours.put("short sequence motif", new Color(0, 165, 155));\r
394       featureColours.put("signal peptide", new Color(0, 155, 165));\r
395       featureColours.put("site", new Color(0, 145, 175));\r
396       featureColours.put("splice variant", new Color(0, 135, 185));\r
397       featureColours.put("strand", new Color(0, 125, 195));\r
398       featureColours.put("topological domain", new Color(0, 115, 205));\r
399       featureColours.put("transit peptide", new Color(0, 105, 215));\r
400       featureColours.put("transmembrane region", new Color(0, 95, 225));\r
401       featureColours.put("turn", new Color(0, 85, 235));\r
402       featureColours.put("unsure residue", new Color(0, 75, 245));\r
403       featureColours.put("zinc finger region", new Color(0, 65, 255));\r
404     }\r
405 \r
406 }\r
407 \r
408 \r
409 \r