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.datamodel.SequenceFeature;
25 import jalview.datamodel.SequenceI;
26 import jalview.util.Comparison;
27 import jalview.viewmodel.seqfeatures.FeatureRendererModel;
29 import java.awt.AlphaComposite;
30 import java.awt.Color;
31 import java.awt.FontMetrics;
32 import java.awt.Graphics;
33 import java.awt.Graphics2D;
34 import java.util.List;
36 public class FeatureRenderer extends FeatureRendererModel
38 private static final AlphaComposite NO_TRANSPARENCY = AlphaComposite
39 .getInstance(AlphaComposite.SRC_OVER, 1.0f);
42 * Constructor given a viewport
46 public FeatureRenderer(AlignViewportI viewport)
52 * Renders the sequence using the given feature colour between the given start
53 * and end columns. Returns true if at least one column is drawn, else false
54 * (the feature range does not overlap the start and end positions).
60 * @param featureColour
67 boolean renderFeature(Graphics g, SequenceI seq, int featureStart,
68 int featureEnd, Color featureColour, int start, int end, int y1,
71 int charHeight = av.getCharHeight();
72 int charWidth = av.getCharWidth();
73 boolean validCharWidth = av.isValidCharWidth();
75 if (featureStart > end || featureEnd < start)
80 if (featureStart < start)
84 if (featureEnd >= end)
88 int pady = (y1 + charHeight) - charHeight / 5;
90 FontMetrics fm = g.getFontMetrics();
91 for (int i = featureStart; i <= featureEnd; i++)
93 char s = seq.getCharAt(i);
95 if (Comparison.isGap(s))
100 g.setColor(featureColour);
102 g.fillRect((i - start) * charWidth, y1, charWidth,
105 if (colourOnly || !validCharWidth)
110 g.setColor(Color.white);
111 int charOffset = (charWidth - fm.charWidth(s)) / 2;
112 g.drawString(String.valueOf(s), charOffset
113 + (charWidth * (i - start)), pady);
119 * Renders the sequence using the given SCORE feature colour between the given
120 * start and end columns. Returns true if at least one column is drawn, else
121 * false (the feature range does not overlap the start and end positions).
127 * @param featureColour
135 boolean renderScoreFeature(Graphics g, SequenceI seq, int fstart,
136 int fend, Color featureColour, int start, int end, int y1,
137 byte[] bs, boolean colourOnly)
139 if (fstart > end || fend < start)
145 { // fix for if the feature we have starts before the sequence start,
146 fstart = start; // but the feature end is still valid!!
153 int charHeight = av.getCharHeight();
154 int pady = (y1 + charHeight) - charHeight / 5;
155 int ystrt = 0, yend = charHeight;
158 // signed - zero is always middle of residue line.
161 yend = charHeight * (128 - bs[1]) / 512;
162 ystrt = charHeight - yend / 2;
166 ystrt = charHeight / 2;
167 yend = charHeight * (bs[1] - 128) / 512;
172 yend = charHeight * bs[1] / 255;
173 ystrt = charHeight - yend;
177 FontMetrics fm = g.getFontMetrics();
178 int charWidth = av.getCharWidth();
180 for (int i = fstart; i <= fend; i++)
182 char s = seq.getCharAt(i);
184 if (Comparison.isGap(s))
189 g.setColor(featureColour);
190 int x = (i - start) * charWidth;
191 g.drawRect(x, y1, charWidth, charHeight);
192 g.fillRect(x, y1 + ystrt, charWidth, yend);
194 if (colourOnly || !av.isValidCharWidth())
199 g.setColor(Color.black);
200 int charOffset = (charWidth - fm.charWidth(s)) / 2;
201 g.drawString(String.valueOf(s), charOffset
202 + (charWidth * (i - start)), pady);
211 public Color findFeatureColour(SequenceI seq, int column, Graphics g)
213 if (!av.isShowSequenceFeatures())
218 if (Comparison.isGap(seq.getCharAt(column)))
223 Color renderedColour = null;
224 if (transparency == 1.0f)
227 * simple case - just find the topmost rendered visible feature colour
229 renderedColour = findFeatureColour(seq, seq.findPosition(column));
234 * transparency case - draw all visible features in render order to
235 * build up a composite colour on the graphics context
237 renderedColour = drawSequence(g, seq, column, column, 0, true);
239 return renderedColour;
243 * Draws the sequence features on the graphics context, or just determines the
244 * colour that would be drawn (if flag colourOnly is true). Returns the last
245 * colour drawn (which may not be the effective colour if transparency
246 * applies), or null if no feature is drawn in the range given.
249 * the graphics context to draw on (may be null if colourOnly==true)
256 * vertical offset at which to draw on the graphics
258 * if true, only do enough to determine the colour for the position,
259 * do not draw the character
262 public synchronized Color drawSequence(final Graphics g,
263 final SequenceI seq, int start, int end, int y1,
266 if (!seq.getFeatures().hasFeatures())
273 if (transparency != 1f && g != null)
275 Graphics2D g2 = (Graphics2D) g;
276 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
280 int startPos = seq.findPosition(start);
281 int endPos = seq.findPosition(end);// todo a performant overload of this!
283 Color drawnColour = null;
286 * iterate over features in ordering of their rendering (last is on top)
288 for (int renderIndex = 0; renderIndex < renderOrder.length; renderIndex++)
290 String type = renderOrder[renderIndex];
291 if (!showFeatureOfType(type))
296 List<SequenceFeature> overlaps = seq.findFeatures(startPos, endPos,
298 for (SequenceFeature sequenceFeature : overlaps)
301 * a feature type may be flagged as shown but the group
302 * an instance of it belongs to may be hidden
304 if (featureGroupNotShown(sequenceFeature))
309 Color featureColour = getColour(sequenceFeature);
310 boolean isContactFeature = sequenceFeature.isContactFeature();
312 if (isContactFeature)
314 boolean drawn = renderFeature(g, seq,
315 seq.findIndex(sequenceFeature.begin) - 1,
316 seq.findIndex(sequenceFeature.begin) - 1, featureColour,
317 start, end, y1, colourOnly);
318 drawn |= renderFeature(g, seq,
319 seq.findIndex(sequenceFeature.end) - 1,
320 seq.findIndex(sequenceFeature.end) - 1, featureColour,
321 start, end, y1, colourOnly);
324 drawnColour = featureColour;
327 else if (showFeature(sequenceFeature))
330 * showing feature score by height of colour
331 * is not implemented as a selectable option
333 if (av.isShowSequenceFeaturesHeight()
334 && !Float.isNaN(sequenceFeature.score))
336 boolean drawn = renderScoreFeature(g, seq,
337 seq.findIndex(sequenceFeature.begin) - 1,
338 seq.findIndex(sequenceFeature.end) - 1, featureColour,
339 start, end, y1, normaliseScore(sequenceFeature),
343 drawnColour = featureColour;
349 boolean drawn = renderFeature(g, seq,
350 seq.findIndex(sequenceFeature.begin) - 1,
351 seq.findIndex(sequenceFeature.end) - 1, featureColour,
352 start, end, y1, colourOnly);
355 drawnColour = featureColour;
362 if (transparency != 1.0f && g != null)
367 Graphics2D g2 = (Graphics2D) g;
368 g2.setComposite(NO_TRANSPARENCY);
375 * Called when alignment in associated view has new/modified features to
376 * discover and display.
380 public void featuresAdded()
386 * Returns the sequence feature colour rendered at the given sequence
387 * position, or null if none found. The feature of highest render order (i.e.
388 * on top) is found, subject to both feature type and feature group being
389 * visible, and its colour returned.
395 Color findFeatureColour(SequenceI seq, int pos)
398 * check for new feature added while processing
403 * inspect features in reverse renderOrder (the last in the array is
404 * displayed on top) until we find one that is rendered at the position
406 for (int renderIndex = renderOrder.length - 1; renderIndex >= 0; renderIndex--)
408 String type = renderOrder[renderIndex];
409 if (!showFeatureOfType(type))
414 List<SequenceFeature> overlaps = seq.findFeatures(pos, pos, type);
415 for (SequenceFeature sequenceFeature : overlaps)
417 if (!featureGroupNotShown(sequenceFeature))
419 return getColour(sequenceFeature);
425 * no displayed feature found at position