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