a2d6bdebbe9d8cf8265a53a78fb4ab98d95b266f
[jalview.git] / src / jalview / gui / FeatureRenderer.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2006 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import jalview.datamodel.*;
22
23 import java.awt.*;
24
25 import java.util.*;
26
27 import java.awt.image.*;
28
29
30 /**
31  * DOCUMENT ME!
32  *
33  * @author $author$
34  * @version $Revision$
35  */
36 public class FeatureRenderer
37 {
38     AlignViewport av;
39     Color resBoxColour;
40     float transparency = 1.0f;
41     FontMetrics fm;
42     int charOffset;
43
44     Hashtable featureColours = new Hashtable();
45
46
47     // A higher level for grouping features of a
48    // particular type
49     Hashtable featureGroups = null;
50
51     // This is actually an Integer held in the hashtable,
52     // Retrieved using the key feature type
53     Object currentColour;
54
55     String [] renderOrder;
56
57     /**
58      * Creates a new FeatureRenderer object.
59      *
60      * @param av DOCUMENT ME!
61      */
62     public FeatureRenderer(AlignViewport av)
63     {
64         this.av = av;
65     }
66
67     public void transferSettings(FeatureRenderer fr)
68     {
69       renderOrder = fr.renderOrder;
70       featureGroups = fr.featureGroups;
71       featureColours = fr.featureColours;
72       transparency =  fr.transparency;
73     }
74
75     BufferedImage offscreenImage;
76     boolean offscreenRender = false;
77     public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
78     {
79       return new Color( findFeatureColour (initialCol.getRGB(),
80                         seq, res ));
81     }
82
83     /**
84      * This is used by the Molecule Viewer and Overview to
85      * get the accurate colourof the rendered sequence
86      */
87     public int findFeatureColour(int initialCol, SequenceI seq, int column)
88     {
89       if(!av.showSequenceFeatures)
90         return initialCol;
91
92       if(seq!=lastSeq)
93       {
94         lastSeq = seq;
95         sequenceFeatures = lastSeq.getDatasetSequence().getSequenceFeatures();
96         if(sequenceFeatures==null)
97           return initialCol;
98
99         sfSize = sequenceFeatures.length;
100       }
101
102       if(jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
103         return Color.white.getRGB();
104
105
106       //Only bother making an offscreen image if transparency is applied
107       if(transparency!=1.0f && offscreenImage==null)
108       {
109         offscreenImage = new BufferedImage(1,1,BufferedImage.TYPE_INT_ARGB);
110       }
111
112       currentColour = null;
113
114       offscreenRender = true;
115
116       if(offscreenImage!=null)
117       {
118         offscreenImage.setRGB(0,0,initialCol);
119         drawSequence(offscreenImage.getGraphics(),
120                      lastSeq,
121                      column,column,0);
122
123         return offscreenImage.getRGB(0,0);
124       }
125       else
126       {
127         drawSequence(null,
128                     lastSeq,
129                     lastSeq.findPosition(column),
130                     -1, -1);
131
132         if (currentColour == null)
133           return initialCol;
134         else
135           return  ((Integer)currentColour).intValue();
136       }
137
138
139     }
140
141
142     /**
143      * DOCUMENT ME!
144      *
145      * @param g DOCUMENT ME!
146      * @param seq DOCUMENT ME!
147      * @param sg DOCUMENT ME!
148      * @param start DOCUMENT ME!
149      * @param end DOCUMENT ME!
150      * @param x1 DOCUMENT ME!
151      * @param y1 DOCUMENT ME!
152      * @param width DOCUMENT ME!
153      * @param height DOCUMENT ME!
154      */
155    // String type;
156    // SequenceFeature sf;
157     SequenceI lastSeq;
158     SequenceFeature [] sequenceFeatures;
159     int sfSize, sfindex, spos, epos;
160
161     public void drawSequence(Graphics g, SequenceI seq,
162                              int start, int end, int y1)
163     {
164       if ( seq.getDatasetSequence().getSequenceFeatures() == null
165           || seq.getDatasetSequence().getSequenceFeatures().length==0)
166         return;
167
168       if(g!=null)
169         fm = g.getFontMetrics();
170
171
172       if (av.featuresDisplayed == null
173           || renderOrder==null
174           || newFeatureAdded)
175        {
176          findAllFeatures();
177          if(av.featuresDisplayed.size()<1)
178            return;
179
180          sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
181          sfSize = sequenceFeatures.length;
182        }
183
184        if(lastSeq==null || seq!=lastSeq)
185       {
186         lastSeq = seq;
187         sequenceFeatures = seq.getDatasetSequence().getSequenceFeatures();
188         sfSize = sequenceFeatures.length;
189       }
190
191
192       if (transparency != 1 && g!=null)
193       {
194         Graphics2D g2 = (Graphics2D) g;
195         g2.setComposite(
196             AlphaComposite.getInstance(
197                 AlphaComposite.SRC_OVER, transparency));
198       }
199
200       if(!offscreenRender)
201       {
202         spos = lastSeq.findPosition(start);
203         epos = lastSeq.findPosition(end);
204       }
205
206
207       String type;
208       for(int renderIndex=0; renderIndex<renderOrder.length; renderIndex++)
209        {
210         type =  renderOrder[renderIndex];
211
212         if(!av.featuresDisplayed.containsKey(type))
213           continue;
214
215         // loop through all features in sequence to find
216         // current feature to render
217         for (sfindex = 0; sfindex < sfSize; sfindex++)
218         {
219           if(sequenceFeatures.length<=sfindex)
220           {
221             continue;
222           }
223           if (!sequenceFeatures[sfindex].type.equals(type))
224             continue;
225
226           if (featureGroups != null
227               && sequenceFeatures[sfindex].featureGroup != null
228               &&
229               featureGroups.containsKey(sequenceFeatures[sfindex].featureGroup)
230               &&
231               ! ( (Boolean) featureGroups.get(sequenceFeatures[sfindex].featureGroup)).
232               booleanValue())
233           {
234             continue;
235           }
236
237           if (!offscreenRender && (sequenceFeatures[sfindex].getBegin() > epos
238                             || sequenceFeatures[sfindex].getEnd() < spos))
239             continue;
240
241           if (offscreenRender && offscreenImage==null)
242           {
243             if (sequenceFeatures[sfindex].begin <= start &&
244                 sequenceFeatures[sfindex].end  >= start)
245             {
246               currentColour = av.featuresDisplayed.get(sequenceFeatures[sfindex].
247                  type);
248             }
249           }
250           else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
251           {
252
253             renderFeature(g, seq,
254                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
255                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
256                           new Color( ( (Integer) av.featuresDisplayed.get(
257                 sequenceFeatures[sfindex].type)).intValue()),
258                           start, end, y1);
259             renderFeature(g, seq,
260                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,
261                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,
262                           new Color( ( (Integer) av.featuresDisplayed.get(
263                 sequenceFeatures[sfindex].type)).intValue()),
264                           start, end, y1);
265
266           }
267           else
268             renderFeature(g, seq,
269                           seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
270                           seq.findIndex(sequenceFeatures[sfindex].end) - 1,
271                           getColour(sequenceFeatures[sfindex].type),
272                           start, end, y1);
273
274
275         }
276
277       }
278
279         if(transparency!=1.0f && g!=null)
280         {
281           Graphics2D g2 = (Graphics2D) g;
282           g2.setComposite(
283               AlphaComposite.getInstance(
284                   AlphaComposite.SRC_OVER, 1.0f));
285         }
286     }
287
288
289     char s;
290     int i;
291     void renderFeature(Graphics g, SequenceI seq,
292                        int fstart, int fend, Color featureColour, int start, int end,  int y1)
293     {
294
295       if (((fstart <= end) && (fend >= start)))
296       {
297         if (fstart < start)
298         { // fix for if the feature we have starts before the sequence start,
299           fstart = start; // but the feature end is still valid!!
300         }
301
302         if (fend >= end)
303         {
304           fend = end;
305         }
306           int pady = (y1 + av.charHeight) - av.charHeight / 5;
307           for (i = fstart; i <= fend; i++)
308           {
309             s = seq.getSequence().charAt(i);
310
311             if (jalview.util.Comparison.isGap(s))
312             {
313               continue;
314             }
315
316             g.setColor(featureColour);
317
318             g.fillRect( (i - start) * av.charWidth, y1, av.charWidth,av.charHeight);
319
320             if(offscreenRender || !av.validCharWidth)
321               continue;
322
323             g.setColor(Color.white);
324             charOffset = (av.charWidth - fm.charWidth(s)) / 2;
325             g.drawString(String.valueOf(s),
326                          charOffset + (av.charWidth * (i - start)),
327                          pady);
328
329           }
330       }
331     }
332
333
334     boolean newFeatureAdded = false;
335
336     public void featuresAdded()
337     {
338       findAllFeatures();
339     }
340
341    boolean findingFeatures = false;
342    synchronized void findAllFeatures()
343    {
344       newFeatureAdded = false;
345
346       if(findingFeatures)
347       {
348         newFeatureAdded = true;
349         return;
350       }
351
352       findingFeatures = true;
353       jalview.schemes.UserColourScheme ucs = new
354           jalview.schemes.UserColourScheme();
355
356       if(av.featuresDisplayed==null)
357         av.featuresDisplayed = new Hashtable();
358
359       av.featuresDisplayed.clear();
360
361       Vector allfeatures = new Vector();
362       for (int i = 0; i < av.alignment.getHeight(); i++)
363       {
364         SequenceFeature [] features
365             = av.alignment.getSequenceAt(i).getDatasetSequence().getSequenceFeatures();
366
367         if (features == null)
368           continue;
369
370         int index = 0;
371         while (index < features.length)
372         {
373           if (!av.featuresDisplayed.containsKey(features[index].getType()))
374           {
375             if(!(features[index].begin == 0 && features[index].end ==0))
376             {
377               // If beginning and end are 0, the feature is for the whole sequence
378               // and we don't want to render the feature in the normal way
379
380               if (getColour(features[index].getType()) == null)
381               {
382                  featureColours.put(features[index].getType(),
383                                    ucs.createColourFromName(features[index].
384                     getType()));
385               }
386
387               av.featuresDisplayed.put(features[index].getType(),
388                                        new Integer(getColour(features[index].
389                   getType()).getRGB()));
390               allfeatures.addElement(features[index].getType());
391             }
392           }
393           index++;
394         }
395       }
396
397       renderOrder = new String[allfeatures.size()];
398       Enumeration en = allfeatures.elements();
399       int i = allfeatures.size()-1;
400       while(en.hasMoreElements())
401       {
402         renderOrder[i] = en.nextElement().toString();
403         i--;
404       }
405
406       findingFeatures = false;
407     }
408
409     public Color getColour(String featureType)
410     {
411       Color colour = (Color)featureColours.get(featureType);
412       return colour;
413     }
414
415
416     public void addNewFeature(String name, Color col, String group)
417     {
418
419       setColour(name, col);
420       if(av.featuresDisplayed==null)
421         av.featuresDisplayed = new Hashtable();
422
423       if(group == null)
424         group = "NOGROUP";
425
426       av.featuresDisplayed.put(name, new Integer(col.getRGB()));
427     }
428
429     public void setColour(String featureType, Color col)
430     {
431       featureColours.put(featureType, col);
432     }
433
434     public void setTransparency(float value)
435     {
436       transparency = value;
437     }
438
439     public float getTransparency()
440     {
441       return transparency;
442     }
443
444     public void setFeaturePriority(Object [][] data)
445     {
446       // The feature table will display high priority
447       // features at the top, but theses are the ones
448       // we need to render last, so invert the data
449       if(av.featuresDisplayed!=null)
450         av.featuresDisplayed.clear();
451       else
452         av.featuresDisplayed = new Hashtable();
453
454       renderOrder = new String[data.length];
455
456       if (data.length > 0)
457         for (int i = 0; i < data.length; i++)
458         {
459           String type = data[i][0].toString();
460           setColour(type, (Color) data[i][1]);
461           if ( ( (Boolean) data[i][2]).booleanValue())
462           {
463             av.featuresDisplayed.put(type, new Integer(getColour(type).getRGB()));
464           }
465
466           renderOrder[data.length - i - 1] = type;
467         }
468
469     }
470
471
472
473
474 }