JAL-1665 create dataset sequences in applet
[jalview.git] / src / jalview / renderer / seqfeatures / FeatureRenderer.java
1 package jalview.renderer.seqfeatures;
2
3 import jalview.datamodel.SequenceFeature;
4 import jalview.datamodel.SequenceI;
5
6 import java.awt.AlphaComposite;
7 import java.awt.Color;
8 import java.awt.FontMetrics;
9 import java.awt.Graphics;
10 import java.awt.Graphics2D;
11 import java.awt.image.BufferedImage;
12
13 public class FeatureRenderer extends
14         jalview.viewmodel.seqfeatures.FeatureRendererModel
15 {
16
17   FontMetrics fm;
18
19   int charOffset;
20
21   boolean offscreenRender = false;
22
23   /**
24    * DOCUMENT ME!
25    * 
26    * @param g
27    *          DOCUMENT ME!
28    * @param seq
29    *          DOCUMENT ME!
30    * @param sg
31    *          DOCUMENT ME!
32    * @param start
33    *          DOCUMENT ME!
34    * @param end
35    *          DOCUMENT ME!
36    * @param x1
37    *          DOCUMENT ME!
38    * @param y1
39    *          DOCUMENT ME!
40    * @param width
41    *          DOCUMENT ME!
42    * @param height
43    *          DOCUMENT ME!
44    */
45   protected SequenceI lastSeq;
46
47   char s;
48
49   int i;
50
51   int av_charHeight, av_charWidth;
52
53   boolean av_validCharWidth, av_isShowSeqFeatureHeight;
54
55   protected void updateAvConfig()
56   {
57     av_charHeight = av.getCharHeight();
58     av_charWidth = av.getCharWidth();
59     av_validCharWidth = av.isValidCharWidth();
60     av_isShowSeqFeatureHeight = av.isShowSequenceFeaturesHeight();
61   }
62
63   void renderFeature(Graphics g, SequenceI seq, int fstart, int fend,
64           Color featureColour, int start, int end, int y1)
65   {
66     updateAvConfig();
67     if (((fstart <= end) && (fend >= start)))
68     {
69       if (fstart < start)
70       { // fix for if the feature we have starts before the sequence start,
71         fstart = start; // but the feature end is still valid!!
72       }
73
74       if (fend >= end)
75       {
76         fend = end;
77       }
78       int pady = (y1 + av_charHeight) - av_charHeight / 5;
79       for (i = fstart; i <= fend; i++)
80       {
81         s = seq.getCharAt(i);
82
83         if (jalview.util.Comparison.isGap(s))
84         {
85           continue;
86         }
87
88         g.setColor(featureColour);
89
90         g.fillRect((i - start) * av_charWidth, y1, av_charWidth,
91                 av_charHeight);
92
93         if (offscreenRender || !av_validCharWidth)
94         {
95           continue;
96         }
97
98         g.setColor(Color.white);
99         charOffset = (av_charWidth - fm.charWidth(s)) / 2;
100         g.drawString(String.valueOf(s), charOffset
101                 + (av_charWidth * (i - start)), pady);
102
103       }
104     }
105   }
106
107   void renderScoreFeature(Graphics g, SequenceI seq, int fstart, int fend,
108           Color featureColour, int start, int end, int y1, byte[] bs)
109   {
110     updateAvConfig();
111     if (((fstart <= end) && (fend >= start)))
112     {
113       if (fstart < start)
114       { // fix for if the feature we have starts before the sequence start,
115         fstart = start; // but the feature end is still valid!!
116       }
117
118       if (fend >= end)
119       {
120         fend = end;
121       }
122       int pady = (y1 + av_charHeight) - av_charHeight / 5;
123       int ystrt = 0, yend = av_charHeight;
124       if (bs[0] != 0)
125       {
126         // signed - zero is always middle of residue line.
127         if (bs[1] < 128)
128         {
129           yend = av_charHeight * (128 - bs[1]) / 512;
130           ystrt = av_charHeight - yend / 2;
131         }
132         else
133         {
134           ystrt = av_charHeight / 2;
135           yend = av_charHeight * (bs[1] - 128) / 512;
136         }
137       }
138       else
139       {
140         yend = av_charHeight * bs[1] / 255;
141         ystrt = av_charHeight - yend;
142
143       }
144       for (i = fstart; i <= fend; i++)
145       {
146         s = seq.getCharAt(i);
147
148         if (jalview.util.Comparison.isGap(s))
149         {
150           continue;
151         }
152
153         g.setColor(featureColour);
154         int x = (i - start) * av_charWidth;
155         g.drawRect(x, y1, av_charWidth, av_charHeight);
156         g.fillRect(x, y1 + ystrt, av_charWidth, yend);
157
158         if (offscreenRender || !av_validCharWidth)
159         {
160           continue;
161         }
162
163         g.setColor(Color.black);
164         charOffset = (av_charWidth - fm.charWidth(s)) / 2;
165         g.drawString(String.valueOf(s), charOffset
166                 + (av_charWidth * (i - start)), pady);
167
168       }
169     }
170   }
171
172   BufferedImage offscreenImage;
173
174   public Color findFeatureColour(Color initialCol, SequenceI seq, int res)
175   {
176     return new Color(findFeatureColour(initialCol.getRGB(), seq, res));
177   }
178
179   /**
180    * This is used by the Molecule Viewer and Overview to get the accurate
181    * colourof the rendered sequence
182    */
183   public synchronized int findFeatureColour(int initialCol, final SequenceI seq,
184           int column)
185   {
186     if (!av.isShowSequenceFeatures())
187     {
188       return initialCol;
189     }
190
191     SequenceFeature[] sf = seq.getSequenceFeatures();
192     if (seq != lastSeq)
193     {
194       lastSeq = seq;
195       sequenceFeatures = sf;
196       if (sequenceFeatures != null)
197       {
198         sfSize = sequenceFeatures.length;
199       }
200     }
201     else
202     {
203       if (sequenceFeatures != sf)
204       {
205         sequenceFeatures = sf;
206         if (sequenceFeatures != null)
207         {
208           sfSize = sequenceFeatures.length;
209         }
210       }
211     }
212
213     if (sequenceFeatures == null || sfSize == 0)
214     {
215       return initialCol;
216     }
217
218     if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
219     {
220       return Color.white.getRGB();
221     }
222
223     // Only bother making an offscreen image if transparency is applied
224     if (transparency != 1.0f && offscreenImage == null)
225     {
226       offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
227     }
228
229     currentColour = null;
230     // TODO: non-threadsafe - each rendering thread needs its own instance of
231     // the feature renderer - or this should be synchronized.
232     offscreenRender = true;
233
234     if (offscreenImage != null)
235     {
236       offscreenImage.setRGB(0, 0, initialCol);
237       drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
238
239       return offscreenImage.getRGB(0, 0);
240     }
241     else
242     {
243       drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
244
245       if (currentColour == null)
246       {
247         return initialCol;
248       }
249       else
250       {
251         return ((Integer) currentColour).intValue();
252       }
253     }
254
255   }
256
257   private volatile SequenceFeature[] sequenceFeatures;
258
259   int sfSize;
260
261   int sfindex;
262
263   int spos;
264
265   int epos;
266
267   public synchronized void drawSequence(Graphics g, final SequenceI seq,
268           int start, int end, int y1)
269   {
270     SequenceFeature[] sf = seq.getSequenceFeatures();
271     if (sf == null || sf.length == 0)
272     {
273       return;
274     }
275
276     if (g != null)
277     {
278       fm = g.getFontMetrics();
279     }
280
281     updateFeatures();
282
283     if (lastSeq == null || seq != lastSeq
284  || sf != sequenceFeatures)
285     {
286       lastSeq = seq;
287       sequenceFeatures = sf;
288     }
289
290     if (transparency != 1 && g != null)
291     {
292       Graphics2D g2 = (Graphics2D) g;
293       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
294               transparency));
295     }
296
297     if (!offscreenRender)
298     {
299       spos = lastSeq.findPosition(start);
300       epos = lastSeq.findPosition(end);
301     }
302
303     sfSize = sequenceFeatures.length;
304     String type;
305     for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
306     {
307       type = renderOrder[renderIndex];
308
309       if (type == null || !showFeatureOfType(type))
310       {
311         continue;
312       }
313
314       // loop through all features in sequence to find
315       // current feature to render
316       for (sfindex = 0; sfindex < sfSize; sfindex++)
317       {
318         if (!sequenceFeatures[sfindex].type.equals(type))
319         {
320           continue;
321         }
322
323         if (featureGroups != null
324                 && sequenceFeatures[sfindex].featureGroup != null
325                 && sequenceFeatures[sfindex].featureGroup.length() != 0
326                 && featureGroups
327                         .containsKey(sequenceFeatures[sfindex].featureGroup)
328                 && !featureGroups
329                         .get(sequenceFeatures[sfindex].featureGroup)
330                         .booleanValue())
331         {
332           continue;
333         }
334
335         if (!offscreenRender
336                 && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
337                         .getEnd() < spos))
338         {
339           continue;
340         }
341
342         if (offscreenRender && offscreenImage == null)
343         {
344           if (sequenceFeatures[sfindex].begin <= start
345                   && sequenceFeatures[sfindex].end >= start)
346           {
347             // this is passed out to the overview and other sequence renderers
348             // (e.g. molecule viewer) to get displayed colour for rendered
349             // sequence
350             currentColour = new Integer(
351                     getColour(sequenceFeatures[sfindex]).getRGB());
352             // used to be retreived from av.featuresDisplayed
353             // currentColour = av.featuresDisplayed
354             // .get(sequenceFeatures[sfindex].type);
355
356           }
357         }
358         else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
359         {
360
361           renderFeature(g, seq,
362                   seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
363                   seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
364                   getColour(sequenceFeatures[sfindex])
365                   // new Color(((Integer) av.featuresDisplayed
366                   // .get(sequenceFeatures[sfindex].type)).intValue())
367                   , start, end, y1);
368           renderFeature(g, seq,
369                   seq.findIndex(sequenceFeatures[sfindex].end) - 1,
370                   seq.findIndex(sequenceFeatures[sfindex].end) - 1,
371                   getColour(sequenceFeatures[sfindex])
372                   // new Color(((Integer) av.featuresDisplayed
373                   // .get(sequenceFeatures[sfindex].type)).intValue())
374                   , start, end, y1);
375
376         }
377         else if (showFeature(sequenceFeatures[sfindex]))
378         {
379           if (av_isShowSeqFeatureHeight
380                   && sequenceFeatures[sfindex].score != Float.NaN)
381           {
382             renderScoreFeature(g, seq,
383                     seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
384                     seq.findIndex(sequenceFeatures[sfindex].end) - 1,
385                     getColour(sequenceFeatures[sfindex]), start, end, y1,
386                     normaliseScore(sequenceFeatures[sfindex]));
387           }
388           else
389           {
390             renderFeature(g, seq,
391                     seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
392                     seq.findIndex(sequenceFeatures[sfindex].end) - 1,
393                     getColour(sequenceFeatures[sfindex]), start, end, y1);
394           }
395         }
396
397       }
398
399     }
400
401     if (transparency != 1.0f && g != null && transparencyAvailable)
402     {
403       Graphics2D g2 = (Graphics2D) g;
404       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
405               1.0f));
406     }
407   }
408
409   boolean transparencyAvailable = true;
410
411   protected void setTransparencyAvailable(boolean isTransparencyAvailable)
412   {
413     transparencyAvailable = isTransparencyAvailable;
414   }
415
416   @Override
417   public boolean isTransparencyAvailable()
418   {
419     return transparencyAvailable;
420   }
421
422   /**
423    * Called when alignment in associated view has new/modified features to
424    * discover and display.
425    * 
426    */
427   public void featuresAdded()
428   {
429     lastSeq = null;
430     findAllFeatures();
431   }
432 }