Features now array for speed of rendering
[jalview.git] / src / jalview / gui / 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.gui;\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 /**\r
29  * DOCUMENT ME!\r
30  *\r
31  * @author $author$\r
32  * @version $Revision$\r
33  */\r
34 public class FeatureRenderer\r
35 {\r
36     AlignViewport av;\r
37     Color resBoxColour;\r
38     float transparency = 1.0f;\r
39     FontMetrics fm;\r
40     int charOffset;\r
41 \r
42     // A higher level for grouping features of a\r
43    // particular type\r
44     Hashtable featureGroups = null;\r
45 \r
46 \r
47     // This is actually an Integer held in the hashtable,\r
48     // Retrieved using the key feature type\r
49     Object currentColour;\r
50 \r
51     String [] renderOrder;\r
52 \r
53 \r
54     /**\r
55      * Creates a new FeatureRenderer object.\r
56      *\r
57      * @param av DOCUMENT ME!\r
58      */\r
59     public FeatureRenderer(AlignViewport av)\r
60     {\r
61         this.av = av;\r
62         initColours();\r
63     }\r
64 \r
65 \r
66     public Color findFeatureColour(Color initialCol, SequenceI seq, int i)\r
67     {\r
68       if(!av.showSequenceFeatures)\r
69         return initialCol;\r
70 \r
71         lastSequence = seq;\r
72         sequenceFeatures = lastSequence.getDatasetSequence().getSequenceFeatures();\r
73         if(sequenceFeatures==null)\r
74           return initialCol;\r
75 \r
76         sfSize = sequenceFeatures.length;\r
77 \r
78       if(jalview.util.Comparison.isGap(lastSequence.getCharAt(i)))\r
79         return Color.white;\r
80 \r
81       currentColour = null;\r
82 \r
83       drawSequence(null, lastSequence, lastSequence.findPosition(i), -1,-1, -1, -1);\r
84 \r
85       if(currentColour==null)\r
86         return initialCol;\r
87 \r
88       return new Color( ((Integer)currentColour).intValue() );\r
89     }\r
90     /**\r
91      * This is used by the Molecule Viewer to get the accurate colour\r
92      * of the rendered sequence\r
93      */\r
94 \r
95     boolean overview = false;\r
96 \r
97     int white = Color.white.getRGB();\r
98     public int findFeatureColour(int initialCol, int seqIndex, int column)\r
99     {\r
100       if(!av.showSequenceFeatures)\r
101         return initialCol;\r
102 \r
103       if(seqIndex!=lastSequenceIndex)\r
104       {\r
105         lastSequence = av.alignment.getSequenceAt(seqIndex);\r
106         lastSequenceIndex = seqIndex;\r
107         sequenceFeatures = lastSequence.getDatasetSequence().getSequenceFeatures();\r
108         if(sequenceFeatures==null)\r
109           return initialCol;\r
110 \r
111         sfSize = sequenceFeatures.length;\r
112       }\r
113 \r
114 \r
115       if(jalview.util.Comparison.isGap(lastSequence.getCharAt(column)))\r
116         return Color.white.getRGB();\r
117 \r
118       currentColour = null;\r
119 \r
120       drawSequence(null, lastSequence, lastSequence.findPosition(column), -1,-1, -1, -1);\r
121 \r
122       if(currentColour==null)\r
123         return initialCol;\r
124 \r
125       return  ((Integer)currentColour).intValue();\r
126     }\r
127 \r
128 \r
129     /**\r
130      * DOCUMENT ME!\r
131      *\r
132      * @param g DOCUMENT ME!\r
133      * @param seq DOCUMENT ME!\r
134      * @param sg DOCUMENT ME!\r
135      * @param start DOCUMENT ME!\r
136      * @param end DOCUMENT ME!\r
137      * @param x1 DOCUMENT ME!\r
138      * @param y1 DOCUMENT ME!\r
139      * @param width DOCUMENT ME!\r
140      * @param height DOCUMENT ME!\r
141      */\r
142    // String type;\r
143    // SequenceFeature sf;\r
144     int lastSequenceIndex=-1;\r
145     SequenceI lastSequence;\r
146     SequenceFeature [] sequenceFeatures;\r
147     int sfSize, sfindex, spos, epos;\r
148 \r
149     public void drawSequence(Graphics g, SequenceI seq,\r
150                              int start, int end, int y1, int width, int height)\r
151     {\r
152       if (   seq.getDatasetSequence().getSequenceFeatures() == null\r
153           || seq.getDatasetSequence().getSequenceFeatures().length==0)\r
154         return;\r
155 \r
156 \r
157       if(g!=null)\r
158         fm = g.getFontMetrics();\r
159 \r
160 \r
161       if (av.featuresDisplayed == null || renderOrder==null)\r
162        {\r
163          findAllFeatures();\r
164          sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();\r
165          sfSize = sequenceFeatures.length;\r
166        }\r
167 \r
168        if(lastSequence==null || seq!=lastSequence)\r
169       {\r
170         lastSequence = seq;\r
171         sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();\r
172         sfSize = sequenceFeatures.length;\r
173       }\r
174 \r
175 \r
176       if (transparency != 1 && g!=null)\r
177       {\r
178         Graphics2D g2 = (Graphics2D) g;\r
179         g2.setComposite(\r
180             AlphaComposite.getInstance(\r
181                 AlphaComposite.SRC_OVER, transparency));\r
182       }\r
183 \r
184       if(!overview)\r
185       {\r
186         spos = lastSequence.findPosition(start);\r
187         epos = lastSequence.findPosition(end);\r
188       }\r
189 \r
190       String type;\r
191       for(int renderIndex=0; renderIndex<renderOrder.length; renderIndex++)\r
192        {\r
193         type =  renderOrder[renderIndex];\r
194 \r
195         // loop through all features in sequence to find\r
196         // current feature to render\r
197         for (sfindex = 0; sfindex < sfSize; sfindex++)\r
198         {\r
199           if (!sequenceFeatures[sfindex].type.equals(type))\r
200             continue;\r
201 \r
202           if (featureGroups != null\r
203               && sequenceFeatures[sfindex].featureGroup != null\r
204               &&\r
205               featureGroups.containsKey(sequenceFeatures[sfindex].featureGroup)\r
206               &&\r
207               ! ( (Boolean) featureGroups.get(sequenceFeatures[sfindex].featureGroup)).\r
208               booleanValue())\r
209           {\r
210             continue;\r
211           }\r
212 \r
213           if (!overview && (sequenceFeatures[sfindex].getBegin() > epos\r
214                             || sequenceFeatures[sfindex].getEnd() < spos))\r
215             continue;\r
216 \r
217           if (overview)\r
218           {\r
219 \r
220             if (sequenceFeatures[sfindex].begin - 1 <= start &&\r
221                 sequenceFeatures[sfindex].end + 1 >= start)\r
222             {\r
223               currentColour = av.featuresDisplayed.get(sequenceFeatures[sfindex].\r
224                   type);\r
225             }\r
226 \r
227           }\r
228           else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))\r
229           {\r
230 \r
231             renderFeature(g, seq,\r
232                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
233                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
234                           new Color( ( (Integer) av.featuresDisplayed.get(\r
235                 sequenceFeatures[sfindex].type)).intValue()),\r
236                           start, end, y1, width, height);\r
237             renderFeature(g, seq,\r
238                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
239                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
240                           new Color( ( (Integer) av.featuresDisplayed.get(\r
241                 sequenceFeatures[sfindex].type)).intValue()),\r
242                           start, end, y1, width, height);\r
243 \r
244           }\r
245           else\r
246             renderFeature(g, seq,\r
247                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,\r
248                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,\r
249                           getColour(sequenceFeatures[sfindex].type),\r
250                           start, end, y1, width, height);\r
251 \r
252 \r
253         }\r
254 \r
255       }\r
256 \r
257         if(transparency!=1.0f && g!=null)\r
258         {\r
259           Graphics2D g2 = (Graphics2D) g;\r
260           g2.setComposite(\r
261               AlphaComposite.getInstance(\r
262                   AlphaComposite.SRC_OVER, 1.0f));\r
263         }\r
264     }\r
265 \r
266 \r
267     char s;\r
268     int i;\r
269     void renderFeature(Graphics g, SequenceI seq,\r
270                        int fstart, int fend, Color featureColour, int start, int end,  int y1, int width, int height)\r
271     {\r
272 \r
273       if (((fstart <= end) && (fend >= start)))\r
274       {\r
275         if (fstart < start)\r
276         { // fix for if the feature we have starts before the sequence start,\r
277           fstart = start; // but the feature end is still valid!!\r
278         }\r
279 \r
280         if (fend >= end)\r
281         {\r
282           fend = end;\r
283         }\r
284 \r
285           for (i = fstart; i <= fend; i++)\r
286           {\r
287             s = seq.getSequence().charAt(i);\r
288 \r
289             if (jalview.util.Comparison.isGap(s))\r
290             {\r
291               continue;\r
292             }\r
293 \r
294             g.setColor(featureColour);\r
295 \r
296             g.fillRect( (i - start) * width, y1, width, height);\r
297 \r
298             g.setColor(Color.white);\r
299             charOffset = (width - fm.charWidth(s)) / 2;\r
300             g.drawString(String.valueOf(s),\r
301                          charOffset + (width * (i - start)),\r
302                          (y1 + height) - height / 5); //pady = height / 5;\r
303 \r
304           }\r
305       }\r
306     }\r
307 \r
308     void findAllFeatures()\r
309     {\r
310       av.featuresDisplayed = new Hashtable();\r
311       for (int i = 0; i < av.alignment.getHeight(); i++)\r
312       {\r
313         SequenceFeature [] features = av.alignment.getSequenceAt(i).getDatasetSequence().\r
314             getSequenceFeatures();\r
315 \r
316         if (features == null)\r
317           continue;\r
318 \r
319         int index = 0;\r
320         while (index < features.length)\r
321         {\r
322           if (!av.featuresDisplayed.containsKey(features[index].getType()))\r
323           {\r
324             av.featuresDisplayed.put(features[index].getType(),\r
325                                   new Integer( getColour(features[index].getType()).getRGB()) );\r
326           }\r
327           index++;\r
328         }\r
329       }\r
330 \r
331       renderOrder = new String[av.featuresDisplayed.size()];\r
332       Enumeration en = av.featuresDisplayed.keys();\r
333       int i = 0;\r
334       while(en.hasMoreElements())\r
335       {\r
336         renderOrder[i] = en.nextElement().toString();\r
337         i++;\r
338       }\r
339     }\r
340 \r
341     public Color getColour(String featureType)\r
342     {\r
343       return (Color)featureColours.get(featureType);\r
344     }\r
345 \r
346     public void addNewFeature(String name, Color col)\r
347     {\r
348 \r
349       setColour(name, col);\r
350       if(av.featuresDisplayed==null)\r
351         av.featuresDisplayed = new Hashtable();\r
352 \r
353 \r
354       av.featuresDisplayed.put(name, "NOGROUP");\r
355     }\r
356 \r
357     public void setColour(String featureType, Color col)\r
358     {\r
359       featureColours.put(featureType, col);\r
360     }\r
361 \r
362     public void setTransparency(float value)\r
363     {\r
364       transparency = value;\r
365     }\r
366 \r
367     public float getTransparency()\r
368     {\r
369       return transparency;\r
370     }\r
371 \r
372     public void setFeaturePriority(Object [][] data)\r
373     {\r
374       // The feature table will display high priority\r
375       // features at the top, but theses are the ones\r
376       // we need to render last, so invert the data\r
377       av.featuresDisplayed.clear();\r
378 \r
379       renderOrder = new String[data.length];\r
380 \r
381       if(data.length>0)\r
382       for(int i=data.length-1; i>-1; i--)\r
383       {\r
384        String type = data[i][0].toString();\r
385        setColour(type, (Color)data[i][1]);\r
386        if( ((Boolean)data[i][2]).booleanValue() )\r
387        {\r
388          av.featuresDisplayed.put(type, new Integer(getColour(type).getRGB()));\r
389        }\r
390        renderOrder[i] = type;\r
391       }\r
392 \r
393     }\r
394 \r
395     Hashtable featureColours = new Hashtable();\r
396     void initColours()\r
397     {\r
398       featureColours.put("active site", new Color(255, 75, 0));\r
399       featureColours.put("binding site", new Color(245, 85, 0));\r
400       featureColours.put("calcium-binding region", new Color(235, 95, 0));\r
401       featureColours.put("chain", new Color(225, 105, 0));\r
402       featureColours.put("coiled-coil region", new Color(215, 115, 0));\r
403       featureColours.put("compositionally biased region", new Color(205, 125, 0));\r
404       featureColours.put("cross-link", new Color(195, 135, 0));\r
405       featureColours.put("disulfide bond", new Color(230,230,0));\r
406       featureColours.put("DNA-binding region", new Color(175, 155, 0));\r
407       featureColours.put("domain", new Color(165, 165, 0));\r
408       featureColours.put("glycosylation site", new Color(155, 175, 0));\r
409       featureColours.put("helix", new Color(145, 185, 0));\r
410       featureColours.put("initiator methionine", new Color(135, 195, 5));\r
411       featureColours.put("lipid moiety-binding region", new Color(125, 205, 15));\r
412       featureColours.put("metal ion-binding site", new Color(115, 215, 25));\r
413       featureColours.put("modified residue", new Color(105, 225, 35));\r
414       featureColours.put("mutagenesis site", new Color(95, 235, 45));\r
415       featureColours.put("non-consecutive residues", new Color(85, 245, 55));\r
416       featureColours.put("non-terminal residue", new Color(75, 255, 65));\r
417       featureColours.put("nucleotide phosphate-binding region",new Color(65, 245, 75));\r
418       featureColours.put("peptide", new Color(55, 235, 85));\r
419       featureColours.put("propeptide", new Color(45, 225, 95));\r
420       featureColours.put("region of interest", new Color(35, 215, 105));\r
421       featureColours.put("repeat", new Color(25, 205, 115));\r
422       featureColours.put("selenocysteine", new Color(15, 195, 125));\r
423       featureColours.put("sequence conflict", new Color(5, 185, 135));\r
424       featureColours.put("sequence variant", new Color(0, 175, 145));\r
425       featureColours.put("short sequence motif", new Color(0, 165, 155));\r
426       featureColours.put("signal peptide", new Color(0, 155, 165));\r
427       featureColours.put("site", new Color(0, 145, 175));\r
428       featureColours.put("splice variant", new Color(0, 135, 185));\r
429       featureColours.put("strand", new Color(0, 125, 195));\r
430       featureColours.put("topological domain", new Color(0, 115, 205));\r
431       featureColours.put("transit peptide", new Color(0, 105, 215));\r
432       featureColours.put("transmembrane region", new Color(0, 95, 225));\r
433       featureColours.put("turn", new Color(0, 85, 235));\r
434       featureColours.put("unsure residue", new Color(0, 75, 245));\r
435       featureColours.put("zinc finger region", new Color(0, 65, 255));\r
436     }\r
437 \r
438 }\r