Clover icon

jalviewX

  1. Project Clover database Wed Oct 31 2018 15:13:58 GMT
  2. Package jalview.renderer.seqfeatures

File FeatureRenderer.java

 

Coverage histogram

../../../img/srcFileCovDistChart7.png
28% of files have more coverage

Code metrics

72
120
7
1
476
266
50
0.42
17.14
7
7.14

Classes

Class Line # Actions
FeatureRenderer 38 120 50 73
0.6331658463.3%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
4    *
5    * This file is part of Jalview.
6    *
7    * Jalview is free software: you can redistribute it and/or
8    * modify it under the terms of the GNU General Public License
9    * as published by the Free Software Foundation, either version 3
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.renderer.seqfeatures;
22   
23    import jalview.api.AlignViewportI;
24    import jalview.api.FeatureColourI;
25    import jalview.datamodel.Range;
26    import jalview.datamodel.SequenceFeature;
27    import jalview.datamodel.SequenceI;
28    import jalview.util.Comparison;
29    import jalview.viewmodel.seqfeatures.FeatureRendererModel;
30   
31    import java.awt.AlphaComposite;
32    import java.awt.Color;
33    import java.awt.FontMetrics;
34    import java.awt.Graphics;
35    import java.awt.Graphics2D;
36    import java.util.List;
37   
 
38    public class FeatureRenderer extends FeatureRendererModel
39    {
40    private static final AlphaComposite NO_TRANSPARENCY = AlphaComposite
41    .getInstance(AlphaComposite.SRC_OVER, 1.0f);
42   
43    /**
44    * Constructor given a viewport
45    *
46    * @param viewport
47    */
 
48  235 toggle public FeatureRenderer(AlignViewportI viewport)
49    {
50  235 this.av = viewport;
51    }
52   
53    /**
54    * Renders the sequence using the given feature colour between the given start
55    * and end columns. Returns true if at least one column is drawn, else false
56    * (the feature range does not overlap the start and end positions).
57    *
58    * @param g
59    * @param seq
60    * @param featureStart
61    * @param featureEnd
62    * @param featureColour
63    * @param start
64    * @param end
65    * @param y1
66    * @param colourOnly
67    * @return
68    */
 
69  25891 toggle boolean renderFeature(Graphics g, SequenceI seq, int featureStart,
70    int featureEnd, Color featureColour, int start, int end, int y1,
71    boolean colourOnly)
72    {
73  25891 int charHeight = av.getCharHeight();
74  25891 int charWidth = av.getCharWidth();
75  25891 boolean validCharWidth = av.isValidCharWidth();
76   
77  25891 if (featureStart > end || featureEnd < start)
78    {
79  0 return false;
80    }
81   
82  25891 if (featureStart < start)
83    {
84  0 featureStart = start;
85    }
86  25891 if (featureEnd >= end)
87    {
88  613 featureEnd = end;
89    }
90  25891 int pady = (y1 + charHeight) - charHeight / 5;
91   
92  25891 FontMetrics fm = g.getFontMetrics();
93  64858 for (int i = featureStart; i <= featureEnd; i++)
94    {
95  38967 char s = seq.getCharAt(i);
96   
97  38967 if (Comparison.isGap(s))
98    {
99  0 continue;
100    }
101   
102  38967 g.setColor(featureColour);
103   
104  38967 g.fillRect((i - start) * charWidth, y1, charWidth, charHeight);
105   
106  38967 if (colourOnly || !validCharWidth)
107    {
108  6 continue;
109    }
110   
111  38961 g.setColor(Color.white);
112  38961 int charOffset = (charWidth - fm.charWidth(s)) / 2;
113  38961 g.drawString(String.valueOf(s),
114    charOffset + (charWidth * (i - start)), pady);
115    }
116  25891 return true;
117    }
118   
119    /**
120    * Renders the sequence using the given SCORE feature colour between the given
121    * start and end columns. Returns true if at least one column is drawn, else
122    * false (the feature range does not overlap the start and end positions).
123    *
124    * @param g
125    * @param seq
126    * @param fstart
127    * @param fend
128    * @param featureColour
129    * @param start
130    * @param end
131    * @param y1
132    * @param bs
133    * @param colourOnly
134    * @return
135    */
 
136  0 toggle boolean renderScoreFeature(Graphics g, SequenceI seq, int fstart,
137    int fend, Color featureColour, int start, int end, int y1,
138    byte[] bs, boolean colourOnly)
139    {
140  0 if (fstart > end || fend < start)
141    {
142  0 return false;
143    }
144   
145  0 if (fstart < start)
146    { // fix for if the feature we have starts before the sequence start,
147  0 fstart = start; // but the feature end is still valid!!
148    }
149   
150  0 if (fend >= end)
151    {
152  0 fend = end;
153    }
154  0 int charHeight = av.getCharHeight();
155  0 int pady = (y1 + charHeight) - charHeight / 5;
156  0 int ystrt = 0, yend = charHeight;
157  0 if (bs[0] != 0)
158    {
159    // signed - zero is always middle of residue line.
160  0 if (bs[1] < 128)
161    {
162  0 yend = charHeight * (128 - bs[1]) / 512;
163  0 ystrt = charHeight - yend / 2;
164    }
165    else
166    {
167  0 ystrt = charHeight / 2;
168  0 yend = charHeight * (bs[1] - 128) / 512;
169    }
170    }
171    else
172    {
173  0 yend = charHeight * bs[1] / 255;
174  0 ystrt = charHeight - yend;
175   
176    }
177   
178  0 FontMetrics fm = g.getFontMetrics();
179  0 int charWidth = av.getCharWidth();
180   
181  0 for (int i = fstart; i <= fend; i++)
182    {
183  0 char s = seq.getCharAt(i);
184   
185  0 if (Comparison.isGap(s))
186    {
187  0 continue;
188    }
189   
190  0 g.setColor(featureColour);
191  0 int x = (i - start) * charWidth;
192  0 g.drawRect(x, y1, charWidth, charHeight);
193  0 g.fillRect(x, y1 + ystrt, charWidth, yend);
194   
195  0 if (colourOnly || !av.isValidCharWidth())
196    {
197  0 continue;
198    }
199   
200  0 g.setColor(Color.black);
201  0 int charOffset = (charWidth - fm.charWidth(s)) / 2;
202  0 g.drawString(String.valueOf(s),
203    charOffset + (charWidth * (i - start)), pady);
204    }
205  0 return true;
206    }
207   
208    /**
209    * {@inheritDoc}
210    */
 
211  30 toggle @Override
212    public Color findFeatureColour(SequenceI seq, int column, Graphics g)
213    {
214  30 if (!av.isShowSequenceFeatures())
215    {
216  0 return null;
217    }
218   
219    // column is 'base 1' but getCharAt is an array index (ie from 0)
220  30 if (Comparison.isGap(seq.getCharAt(column - 1)))
221    {
222    /*
223    * returning null allows the colour scheme to provide gap colour
224    * - normally white, but can be customised
225    */
226  1 return null;
227    }
228   
229  29 Color renderedColour = null;
230  29 if (transparency == 1.0f)
231    {
232    /*
233    * simple case - just find the topmost rendered visible feature colour
234    */
235  25 renderedColour = findFeatureColour(seq, column);
236    }
237    else
238    {
239    /*
240    * transparency case - draw all visible features in render order to
241    * build up a composite colour on the graphics context
242    */
243  4 renderedColour = drawSequence(g, seq, column, column, 0, true);
244    }
245  29 return renderedColour;
246    }
247   
248    /**
249    * Draws the sequence features on the graphics context, or just determines the
250    * colour that would be drawn (if flag colourOnly is true). Returns the last
251    * colour drawn (which may not be the effective colour if transparency
252    * applies), or null if no feature is drawn in the range given.
253    *
254    * @param g
255    * the graphics context to draw on (may be null if colourOnly==true)
256    * @param seq
257    * @param start
258    * start column
259    * @param end
260    * end column
261    * @param y1
262    * vertical offset at which to draw on the graphics
263    * @param colourOnly
264    * if true, only do enough to determine the colour for the position,
265    * do not draw the character
266    * @return
267    */
 
268  1034 toggle public synchronized Color drawSequence(final Graphics g,
269    final SequenceI seq, int start, int end, int y1,
270    boolean colourOnly)
271    {
272    /*
273    * if columns are all gapped, or sequence has no features, nothing to do
274    */
275  1034 Range visiblePositions = seq.findPositions(start+1, end+1);
276  1034 if (visiblePositions == null || !seq.getFeatures().hasFeatures())
277    {
278  0 return null;
279    }
280   
281  1034 updateFeatures();
282   
283  1034 if (transparency != 1f && g != null)
284    {
285  4 Graphics2D g2 = (Graphics2D) g;
286  4 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
287    transparency));
288    }
289   
290  1034 Color drawnColour = null;
291   
292    /*
293    * iterate over features in ordering of their rendering (last is on top)
294    */
295  29973 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
296    {
297  28939 String type = renderOrder[renderIndex];
298  28939 if (!showFeatureOfType(type))
299    {
300  21405 continue;
301    }
302   
303  7534 FeatureColourI fc = getFeatureStyle(type);
304  7534 List<SequenceFeature> overlaps = seq.getFeatures().findFeatures(
305    visiblePositions.getBegin(), visiblePositions.getEnd(), type);
306   
307  7534 if (fc.isSimpleColour())
308    {
309  7534 filterFeaturesForDisplay(overlaps);
310    }
311   
312  7534 for (SequenceFeature sf : overlaps)
313    {
314  25891 Color featureColour = getColor(sf, fc);
315  25891 if (featureColour == null)
316    {
317    /*
318    * feature excluded by visibility settings, filters, or colour threshold
319    */
320  0 continue;
321    }
322   
323    /*
324    * if feature starts/ends outside the visible range,
325    * restrict to visible positions (or if a contact feature,
326    * to a single position)
327    */
328  25891 int visibleStart = sf.getBegin();
329  25891 if (visibleStart < visiblePositions.getBegin())
330    {
331  55 visibleStart = sf.isContactFeature() ? sf.getEnd()
332    : visiblePositions.getBegin();
333    }
334  25891 int visibleEnd = sf.getEnd();
335  25891 if (visibleEnd > visiblePositions.getEnd())
336    {
337  7 visibleEnd = sf.isContactFeature() ? sf.getBegin()
338    : visiblePositions.getEnd();
339    }
340   
341  25891 int featureStartCol = seq.findIndex(visibleStart);
342  25891 int featureEndCol = sf.begin == sf.end ? featureStartCol : seq
343    .findIndex(visibleEnd);
344   
345    // Color featureColour = getColour(sequenceFeature);
346   
347  25891 boolean isContactFeature = sf.isContactFeature();
348   
349  25891 if (isContactFeature)
350    {
351  0 boolean drawn = renderFeature(g, seq, featureStartCol - 1,
352    featureStartCol - 1, featureColour, start, end, y1,
353    colourOnly);
354  0 drawn |= renderFeature(g, seq, featureEndCol - 1,
355    featureEndCol - 1, featureColour, start, end, y1,
356    colourOnly);
357  0 if (drawn)
358    {
359  0 drawnColour = featureColour;
360    }
361    }
362    else
363    {
364    /*
365    * showing feature score by height of colour
366    * is not implemented as a selectable option
367    *
368    if (av.isShowSequenceFeaturesHeight()
369    && !Float.isNaN(sequenceFeature.score))
370    {
371    boolean drawn = renderScoreFeature(g, seq,
372    seq.findIndex(sequenceFeature.begin) - 1,
373    seq.findIndex(sequenceFeature.end) - 1, featureColour,
374    start, end, y1, normaliseScore(sequenceFeature),
375    colourOnly);
376    if (drawn)
377    {
378    drawnColour = featureColour;
379    }
380    }
381    else
382    {
383    */
384  25891 boolean drawn = renderFeature(g, seq,
385    featureStartCol - 1,
386    featureEndCol - 1, featureColour,
387    start, end, y1, colourOnly);
388  25891 if (drawn)
389    {
390  25891 drawnColour = featureColour;
391    }
392    /*}*/
393    }
394    }
395    }
396   
397  1034 if (transparency != 1.0f && g != null)
398    {
399    /*
400    * reset transparency
401    */
402  4 Graphics2D g2 = (Graphics2D) g;
403  4 g2.setComposite(NO_TRANSPARENCY);
404    }
405   
406  1034 return drawnColour;
407    }
408   
409    /**
410    * Called when alignment in associated view has new/modified features to
411    * discover and display.
412    *
413    */
 
414  17 toggle @Override
415    public void featuresAdded()
416    {
417  17 findAllFeatures();
418    }
419   
420    /**
421    * Returns the sequence feature colour rendered at the given column position,
422    * or null if none found. The feature of highest render order (i.e. on top) is
423    * found, subject to both feature type and feature group being visible, and
424    * its colour returned. This method is suitable when no feature transparency
425    * applied (only the topmost visible feature colour is rendered).
426    * <p>
427    * Note this method does not check for a gap in the column so would return the
428    * colour for features enclosing a gapped column. Check for gap before calling
429    * if different behaviour is wanted.
430    *
431    * @param seq
432    * @param column
433    * (1..)
434    * @return
435    */
 
436  25 toggle Color findFeatureColour(SequenceI seq, int column)
437    {
438    /*
439    * check for new feature added while processing
440    */
441  25 updateFeatures();
442   
443    /*
444    * inspect features in reverse renderOrder (the last in the array is
445    * displayed on top) until we find one that is rendered at the position
446    */
447  25 for (int renderIndex = renderOrder.length
448  37 - 1; renderIndex >= 0; renderIndex--)
449    {
450  30 String type = renderOrder[renderIndex];
451  30 if (!showFeatureOfType(type))
452    {
453  1 continue;
454    }
455   
456  29 List<SequenceFeature> overlaps = seq.findFeatures(column, column,
457    type);
458  29 for (SequenceFeature sequenceFeature : overlaps)
459    {
460  23 if (!featureGroupNotShown(sequenceFeature))
461    {
462  22 Color col = getColour(sequenceFeature);
463  22 if (col != null)
464    {
465  18 return col;
466    }
467    }
468    }
469    }
470   
471    /*
472    * no displayed feature found at position
473    */
474  7 return null;
475    }
476    }