2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.renderer.seqfeatures;
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;
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;
38 public class FeatureRenderer extends FeatureRendererModel
40 private static final AlphaComposite NO_TRANSPARENCY = AlphaComposite
41 .getInstance(AlphaComposite.SRC_OVER, 1.0f);
44 * Constructor given a viewport
48 public FeatureRenderer(AlignViewportI viewport)
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).
62 * @param featureColour
69 boolean renderFeature(Graphics g, SequenceI seq, int featureStart,
70 int featureEnd, Color featureColour, int start, int end, int y1,
73 int charHeight = av.getCharHeight();
74 int charWidth = av.getCharWidth();
75 boolean validCharWidth = av.isValidCharWidth();
77 if (featureStart > end || featureEnd < start)
82 if (featureStart < start)
86 if (featureEnd >= end)
90 int pady = (y1 + charHeight) - charHeight / 5;
92 FontMetrics fm = g.getFontMetrics();
93 for (int i = featureStart; i <= featureEnd; i++)
95 char s = seq.getCharAt(i);
97 if (Comparison.isGap(s))
102 g.setColor(featureColour);
104 g.fillRect((i - start) * charWidth, y1, charWidth, charHeight);
106 if (colourOnly || !validCharWidth)
111 g.setColor(Color.white);
112 int charOffset = (charWidth - fm.charWidth(s)) / 2;
113 g.drawString(String.valueOf(s),
114 charOffset + (charWidth * (i - start)), pady);
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).
128 * @param featureColour
136 boolean renderScoreFeature(Graphics g, SequenceI seq, int fstart,
137 int fend, Color featureColour, int start, int end, int y1,
138 byte[] bs, boolean colourOnly)
140 if (fstart > end || fend < start)
146 { // fix for if the feature we have starts before the sequence start,
147 fstart = start; // but the feature end is still valid!!
154 int charHeight = av.getCharHeight();
155 int pady = (y1 + charHeight) - charHeight / 5;
156 int ystrt = 0, yend = charHeight;
159 // signed - zero is always middle of residue line.
162 yend = charHeight * (128 - bs[1]) / 512;
163 ystrt = charHeight - yend / 2;
167 ystrt = charHeight / 2;
168 yend = charHeight * (bs[1] - 128) / 512;
173 yend = charHeight * bs[1] / 255;
174 ystrt = charHeight - yend;
178 FontMetrics fm = g.getFontMetrics();
179 int charWidth = av.getCharWidth();
181 for (int i = fstart; i <= fend; i++)
183 char s = seq.getCharAt(i);
185 if (Comparison.isGap(s))
190 g.setColor(featureColour);
191 int x = (i - start) * charWidth;
192 g.drawRect(x, y1, charWidth, charHeight);
193 g.fillRect(x, y1 + ystrt, charWidth, yend);
195 if (colourOnly || !av.isValidCharWidth())
200 g.setColor(Color.black);
201 int charOffset = (charWidth - fm.charWidth(s)) / 2;
202 g.drawString(String.valueOf(s),
203 charOffset + (charWidth * (i - start)), pady);
212 public Color findFeatureColour(SequenceI seq, int column, Graphics g)
214 if (!av.isShowSequenceFeatures())
219 if (Comparison.isGap(seq.getCharAt(column)))
222 * returning null allows the colour scheme to provide gap colour
223 * - normally white, but can be customised
228 Color renderedColour = null;
229 if (transparency == 1.0f)
232 * simple case - just find the topmost rendered visible feature colour
234 renderedColour = findFeatureColour(seq, column);
239 * transparency case - draw all visible features in render order to
240 * build up a composite colour on the graphics context
242 renderedColour = drawSequence(g, seq, column, column, 0, true);
244 return renderedColour;
248 * Draws the sequence features on the graphics context, or just determines the
249 * colour that would be drawn (if flag colourOnly is true). Returns the last
250 * colour drawn (which may not be the effective colour if transparency
251 * applies), or null if no feature is drawn in the range given.
254 * the graphics context to draw on (may be null if colourOnly==true)
261 * vertical offset at which to draw on the graphics
263 * if true, only do enough to determine the colour for the position,
264 * do not draw the character
267 public synchronized Color drawSequence(final Graphics g,
268 final SequenceI seq, int start, int end, int y1,
272 * if columns are all gapped, or sequence has no features, nothing to do
274 Range visiblePositions = seq.findPositions(start+1, end+1);
275 if (visiblePositions == null || !seq.getFeatures().hasFeatures())
282 if (transparency != 1f && g != null)
284 Graphics2D g2 = (Graphics2D) g;
285 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
289 Color drawnColour = null;
292 * iterate over features in ordering of their rendering (last is on top)
294 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
296 String type = renderOrder[renderIndex];
297 if (!showFeatureOfType(type))
302 FeatureColourI fc = getFeatureStyle(type);
303 List<SequenceFeature> overlaps = seq.getFeatures().findFeatures(
304 visiblePositions.getBegin(), visiblePositions.getEnd(), type);
306 filterFeaturesForDisplay(overlaps, fc);
308 for (SequenceFeature sf : overlaps)
310 Color featureColour = fc.getColor(sf);
311 if (featureColour == null)
313 // score feature outwith threshold for colouring
318 * if feature starts/ends outside the visible range,
319 * restrict to visible positions (or if a contact feature,
320 * to a single position)
322 int visibleStart = sf.getBegin();
323 if (visibleStart < visiblePositions.getBegin())
325 visibleStart = sf.isContactFeature() ? sf.getEnd()
326 : visiblePositions.getBegin();
328 int visibleEnd = sf.getEnd();
329 if (visibleEnd > visiblePositions.getEnd())
331 visibleEnd = sf.isContactFeature() ? sf.getBegin()
332 : visiblePositions.getEnd();
335 int featureStartCol = seq.findIndex(visibleStart);
336 int featureEndCol = sf.begin == sf.end ? featureStartCol : seq
337 .findIndex(visibleEnd);
339 // Color featureColour = getColour(sequenceFeature);
341 boolean isContactFeature = sf.isContactFeature();
343 if (isContactFeature)
345 boolean drawn = renderFeature(g, seq, featureStartCol - 1,
346 featureStartCol - 1, featureColour, start, end, y1,
348 drawn |= renderFeature(g, seq, featureEndCol - 1,
349 featureEndCol - 1, featureColour, start, end, y1,
353 drawnColour = featureColour;
359 * showing feature score by height of colour
360 * is not implemented as a selectable option
362 if (av.isShowSequenceFeaturesHeight()
363 && !Float.isNaN(sequenceFeature.score))
365 boolean drawn = renderScoreFeature(g, seq,
366 seq.findIndex(sequenceFeature.begin) - 1,
367 seq.findIndex(sequenceFeature.end) - 1, featureColour,
368 start, end, y1, normaliseScore(sequenceFeature),
372 drawnColour = featureColour;
378 boolean drawn = renderFeature(g, seq,
380 featureEndCol - 1, featureColour,
381 start, end, y1, colourOnly);
384 drawnColour = featureColour;
391 if (transparency != 1.0f && g != null)
396 Graphics2D g2 = (Graphics2D) g;
397 g2.setComposite(NO_TRANSPARENCY);
406 * Answers true if the feature belongs to a feature group which is not
407 * currently displayed, else false
409 * @param sequenceFeature
413 protected boolean featureGroupNotShown(
414 final SequenceFeature sequenceFeature)
416 return featureGroups != null && sequenceFeature.featureGroup != null
417 && sequenceFeature.featureGroup.length() != 0
418 && featureGroups.containsKey(sequenceFeature.featureGroup)
419 && !featureGroups.get(sequenceFeature.featureGroup)
424 >>>>>>> refs/heads/develop
425 * Called when alignment in associated view has new/modified features to
426 * discover and display.
430 public void featuresAdded()
436 * Returns the sequence feature colour rendered at the given column position,
437 * or null if none found. The feature of highest render order (i.e. on top) is
438 * found, subject to both feature type and feature group being visible, and
439 * its colour returned. This method is suitable when no feature transparency
440 * applied (only the topmost visible feature colour is rendered).
442 * Note this method does not check for a gap in the column so would return the
443 * colour for features enclosing a gapped column. Check for gap before calling
444 * if different behaviour is wanted.
451 Color findFeatureColour(SequenceI seq, int column)
454 * check for new feature added while processing
459 * inspect features in reverse renderOrder (the last in the array is
460 * displayed on top) until we find one that is rendered at the position
462 for (int renderIndex = renderOrder.length
463 - 1; renderIndex >= 0; renderIndex--)
465 String type = renderOrder[renderIndex];
466 if (!showFeatureOfType(type))
471 List<SequenceFeature> overlaps = seq.findFeatures(column, column,
473 for (SequenceFeature sequenceFeature : overlaps)
475 if (!featureGroupNotShown(sequenceFeature))
477 Color col = getColour(sequenceFeature);
487 * no displayed feature found at position