JAL-1483 formatting
[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     final SequenceI aseq = (seq.getDatasetSequence() != null) ? seq
192             .getDatasetSequence() : seq;
193     if (seq != lastSeq)
194     {
195       lastSeq = seq;
196       sequenceFeatures = aseq.getSequenceFeatures();
197       if (sequenceFeatures != null)
198       {
199         sfSize = sequenceFeatures.length;
200       }
201     }
202     else
203     {
204       if (sequenceFeatures != aseq.getSequenceFeatures())
205       {
206         sequenceFeatures = aseq.getSequenceFeatures();
207         if (sequenceFeatures != null)
208         {
209           sfSize = sequenceFeatures.length;
210         }
211       }
212     }
213
214     if (sequenceFeatures == null || sfSize == 0)
215     {
216       return initialCol;
217     }
218
219     if (jalview.util.Comparison.isGap(lastSeq.getCharAt(column)))
220     {
221       return Color.white.getRGB();
222     }
223
224     // Only bother making an offscreen image if transparency is applied
225     if (transparency != 1.0f && offscreenImage == null)
226     {
227       offscreenImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
228     }
229
230     currentColour = null;
231     // TODO: non-threadsafe - each rendering thread needs its own instance of
232     // the feature renderer - or this should be synchronized.
233     offscreenRender = true;
234
235     if (offscreenImage != null)
236     {
237       offscreenImage.setRGB(0, 0, initialCol);
238       drawSequence(offscreenImage.getGraphics(), lastSeq, column, column, 0);
239
240       return offscreenImage.getRGB(0, 0);
241     }
242     else
243     {
244       drawSequence(null, lastSeq, lastSeq.findPosition(column), -1, -1);
245
246       if (currentColour == null)
247       {
248         return initialCol;
249       }
250       else
251       {
252         return ((Integer) currentColour).intValue();
253       }
254     }
255
256   }
257
258   private volatile SequenceFeature[] sequenceFeatures;
259
260   int sfSize;
261
262   int sfindex;
263
264   int spos;
265
266   int epos;
267
268   public synchronized void drawSequence(Graphics g, final SequenceI seq,
269           int start, int end, int y1)
270   {
271     final SequenceI aseq = (seq.getDatasetSequence() != null) ? seq
272             .getDatasetSequence() : seq;
273     if (aseq.getSequenceFeatures() == null
274             || aseq.getSequenceFeatures().length == 0)
275     {
276       return;
277     }
278
279     if (g != null)
280     {
281       fm = g.getFontMetrics();
282     }
283
284     updateFeatures();
285
286     if (lastSeq == null || seq != lastSeq
287             || aseq.getSequenceFeatures() != sequenceFeatures)
288     {
289       lastSeq = seq;
290       sequenceFeatures = aseq.getSequenceFeatures();
291     }
292
293     if (transparency != 1 && g != null)
294     {
295       Graphics2D g2 = (Graphics2D) g;
296       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
297               transparency));
298     }
299
300     if (!offscreenRender)
301     {
302       spos = lastSeq.findPosition(start);
303       epos = lastSeq.findPosition(end);
304     }
305
306     sfSize = sequenceFeatures.length;
307     String type;
308     for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
309     {
310       type = renderOrder[renderIndex];
311
312       if (type == null || !showFeatureOfType(type))
313       {
314         continue;
315       }
316
317       // loop through all features in sequence to find
318       // current feature to render
319       for (sfindex = 0; sfindex < sfSize; sfindex++)
320       {
321         if (!sequenceFeatures[sfindex].type.equals(type))
322         {
323           continue;
324         }
325
326         if (featureGroups != null
327                 && sequenceFeatures[sfindex].featureGroup != null
328                 && sequenceFeatures[sfindex].featureGroup.length() != 0
329                 && featureGroups
330                         .containsKey(sequenceFeatures[sfindex].featureGroup)
331                 && !featureGroups
332                         .get(sequenceFeatures[sfindex].featureGroup)
333                         .booleanValue())
334         {
335           continue;
336         }
337
338         if (!offscreenRender
339                 && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
340                         .getEnd() < spos))
341         {
342           continue;
343         }
344
345         if (offscreenRender && offscreenImage == null)
346         {
347           if (sequenceFeatures[sfindex].begin <= start
348                   && sequenceFeatures[sfindex].end >= start)
349           {
350             // this is passed out to the overview and other sequence renderers
351             // (e.g. molecule viewer) to get displayed colour for rendered
352             // sequence
353             currentColour = new Integer(
354                     getColour(sequenceFeatures[sfindex]).getRGB());
355             // used to be retreived from av.featuresDisplayed
356             // currentColour = av.featuresDisplayed
357             // .get(sequenceFeatures[sfindex].type);
358
359           }
360         }
361         else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
362         {
363
364           renderFeature(g, seq,
365                   seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
366                   seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
367                   getColour(sequenceFeatures[sfindex])
368                   // new Color(((Integer) av.featuresDisplayed
369                   // .get(sequenceFeatures[sfindex].type)).intValue())
370                   , start, end, y1);
371           renderFeature(g, seq,
372                   seq.findIndex(sequenceFeatures[sfindex].end) - 1,
373                   seq.findIndex(sequenceFeatures[sfindex].end) - 1,
374                   getColour(sequenceFeatures[sfindex])
375                   // new Color(((Integer) av.featuresDisplayed
376                   // .get(sequenceFeatures[sfindex].type)).intValue())
377                   , start, end, y1);
378
379         }
380         else if (showFeature(sequenceFeatures[sfindex]))
381         {
382           if (av_isShowSeqFeatureHeight
383                   && sequenceFeatures[sfindex].score != Float.NaN)
384           {
385             renderScoreFeature(g, seq,
386                     seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
387                     seq.findIndex(sequenceFeatures[sfindex].end) - 1,
388                     getColour(sequenceFeatures[sfindex]), start, end, y1,
389                     normaliseScore(sequenceFeatures[sfindex]));
390           }
391           else
392           {
393             renderFeature(g, seq,
394                     seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
395                     seq.findIndex(sequenceFeatures[sfindex].end) - 1,
396                     getColour(sequenceFeatures[sfindex]), start, end, y1);
397           }
398         }
399
400       }
401
402     }
403
404     if (transparency != 1.0f && g != null && transparencyAvailable)
405     {
406       Graphics2D g2 = (Graphics2D) g;
407       g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
408               1.0f));
409     }
410   }
411
412   boolean transparencyAvailable = true;
413
414   protected void setTransparencyAvailable(boolean isTransparencyAvailable)
415   {
416     transparencyAvailable = isTransparencyAvailable;
417   }
418
419   @Override
420   public boolean isTransparencyAvailable()
421   {
422     return transparencyAvailable;
423   }
424
425   /**
426    * Called when alignment in associated view has new/modified features to
427    * discover and display.
428    * 
429    */
430   public void featuresAdded()
431   {
432     lastSeq = null;
433     findAllFeatures();
434   }
435 }