overlaps = seq.getFeatures().findFeatures(
+ visiblePositions.getBegin(), visiblePositions.getEnd(), type);
+
+ if (overlaps.size() > 1 && fc.isSimpleColour())
{
- if (!sequenceFeatures[sfindex].type.equals(type))
+ filterFeaturesForDisplay(overlaps);
+ }
+
+ for (SequenceFeature sf : overlaps)
+ {
+ Color featureColour = getColor(sf, fc);
+ if (featureColour == null)
{
+ /*
+ * feature excluded by filters, or colour threshold
+ */
continue;
}
- if (featureGroups != null
- && sequenceFeatures[sfindex].featureGroup != null
- && sequenceFeatures[sfindex].featureGroup.length() != 0
- && featureGroups
- .containsKey(sequenceFeatures[sfindex].featureGroup)
- && !featureGroups
- .get(sequenceFeatures[sfindex].featureGroup)
- .booleanValue())
+ /*
+ * if feature starts/ends outside the visible range,
+ * restrict to visible positions (or if a contact feature,
+ * to a single position)
+ */
+ int visibleStart = sf.getBegin();
+ if (visibleStart < visiblePositions.getBegin())
{
- continue;
+ visibleStart = sf.isContactFeature() ? sf.getEnd()
+ : visiblePositions.getBegin();
}
-
- if (!offscreenRender
- && (sequenceFeatures[sfindex].getBegin() > epos || sequenceFeatures[sfindex]
- .getEnd() < spos))
+ int visibleEnd = sf.getEnd();
+ if (visibleEnd > visiblePositions.getEnd())
{
- continue;
+ visibleEnd = sf.isContactFeature() ? sf.getBegin()
+ : visiblePositions.getEnd();
}
- if (offscreenRender && offscreenImage == null)
- {
- if (sequenceFeatures[sfindex].begin <= start
- && sequenceFeatures[sfindex].end >= start)
- {
- // this is passed out to the overview and other sequence renderers
- // (e.g. molecule viewer) to get displayed colour for rendered
- // sequence
- currentColour = new Integer(
- getColour(sequenceFeatures[sfindex]).getRGB());
- // used to be retreived from av.featuresDisplayed
- // currentColour = av.featuresDisplayed
- // .get(sequenceFeatures[sfindex].type);
+ int featureStartCol = seq.findIndex(visibleStart);
+ int featureEndCol = sf.begin == sf.end ? featureStartCol
+ : seq.findIndex(visibleEnd);
- }
- }
- else if (sequenceFeatures[sfindex].type.equals("disulfide bond"))
- {
+ // Color featureColour = getColour(sequenceFeature);
- renderFeature(g, seq,
- seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
- seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
- getColour(sequenceFeatures[sfindex])
- // new Color(((Integer) av.featuresDisplayed
- // .get(sequenceFeatures[sfindex].type)).intValue())
- , start, end, y1);
- renderFeature(g, seq,
- seq.findIndex(sequenceFeatures[sfindex].end) - 1,
- seq.findIndex(sequenceFeatures[sfindex].end) - 1,
- getColour(sequenceFeatures[sfindex])
- // new Color(((Integer) av.featuresDisplayed
- // .get(sequenceFeatures[sfindex].type)).intValue())
- , start, end, y1);
+ boolean isContactFeature = sf.isContactFeature();
+ if (isContactFeature)
+ {
+ boolean drawn = renderFeature(g, seq, featureStartCol - 1,
+ featureStartCol - 1, featureColour, start, end, y1,
+ colourOnly);
+ drawn |= renderFeature(g, seq, featureEndCol - 1,
+ featureEndCol - 1, featureColour, start, end, y1,
+ colourOnly);
+ if (drawn)
+ {
+ drawnColour = featureColour;
+ }
}
- else if (showFeature(sequenceFeatures[sfindex]))
+ else
{
- if (av_isShowSeqFeatureHeight
- && sequenceFeatures[sfindex].score != Float.NaN)
+ /*
+ * showing feature score by height of colour
+ * is not implemented as a selectable option
+ *
+ if (av.isShowSequenceFeaturesHeight()
+ && !Float.isNaN(sequenceFeature.score))
{
- renderScoreFeature(g, seq,
- seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
- seq.findIndex(sequenceFeatures[sfindex].end) - 1,
- getColour(sequenceFeatures[sfindex]), start, end, y1,
- normaliseScore(sequenceFeatures[sfindex]));
+ boolean drawn = renderScoreFeature(g, seq,
+ seq.findIndex(sequenceFeature.begin) - 1,
+ seq.findIndex(sequenceFeature.end) - 1, featureColour,
+ start, end, y1, normaliseScore(sequenceFeature),
+ colourOnly);
+ if (drawn)
+ {
+ drawnColour = featureColour;
+ }
}
else
{
- renderFeature(g, seq,
- seq.findIndex(sequenceFeatures[sfindex].begin) - 1,
- seq.findIndex(sequenceFeatures[sfindex].end) - 1,
- getColour(sequenceFeatures[sfindex]), start, end, y1);
+ */
+ boolean drawn = renderFeature(g, seq, featureStartCol - 1,
+ featureEndCol - 1, featureColour, start, end, y1,
+ colourOnly);
+ if (drawn)
+ {
+ drawnColour = featureColour;
}
+ /*}*/
}
-
}
+ }
+ /*
+ * draw 'complement' features above ours if configured to do so
+ */
+ if (av.isShowComplementFeatures() && av.isShowComplementFeaturesOnTop())
+ {
+ drawnColour = drawComplementFeatures(g, seq, start, end, y1,
+ colourOnly, visiblePositions, drawnColour);
}
- if (transparency != 1.0f && g != null && transparencyAvailable)
+ if (transparency != 1.0f && g != null)
{
+ /*
+ * reset transparency
+ */
Graphics2D g2 = (Graphics2D) g;
- g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,
- 1.0f));
+ g2.setComposite(NO_TRANSPARENCY);
}
- }
-
- boolean transparencyAvailable = true;
- protected void setTransparencyAvailable(boolean isTransparencyAvailable)
- {
- transparencyAvailable = isTransparencyAvailable;
+ return drawnColour;
}
- @Override
- public boolean isTransparencyAvailable()
+ /**
+ * Find any features on the CDS/protein complement of the sequence region and
+ * draw them, with visibility and colouring as configured in the complementary
+ * viewport
+ *
+ * @param g
+ * @param seq
+ * @param start
+ * @param end
+ * @param y1
+ * @param colourOnly
+ * @param visiblePositions
+ * @param drawnColour
+ * @return
+ */
+ Color drawComplementFeatures(final Graphics g, final SequenceI seq,
+ int start, int end, int y1, boolean colourOnly,
+ ContiguousI visiblePositions, Color drawnColour)
{
- return transparencyAvailable;
+ AlignViewportI comp = av.getCodingComplement();
+ FeatureRenderer fr2 = Desktop.getAlignFrameFor(comp)
+ .getFeatureRenderer();
+
+ final int visibleStart = visiblePositions.getBegin();
+ final int visibleEnd = visiblePositions.getEnd();
+
+ for (int pos = visibleStart; pos <= visibleEnd; pos++)
+ {
+ int column = seq.findIndex(pos);
+ MappedFeatures mf = fr2.findComplementFeaturesAtResidue(seq, pos);
+ if (mf != null)
+ {
+ for (SequenceFeature sf : mf.features)
+ {
+ FeatureColourI fc = fr2.getFeatureStyle(sf.getType());
+ Color featureColour = fr2.getColor(sf, fc);
+ renderFeature(g, seq, column - 1, column - 1, featureColour,
+ start, end, y1, colourOnly);
+ drawnColour = featureColour;
+ }
+ }
+ }
+ return drawnColour;
}
/**
@@ -427,9 +483,118 @@ public class FeatureRenderer extends
* discover and display.
*
*/
+ @Override
public void featuresAdded()
{
- lastSeq = null;
findAllFeatures();
}
+
+ /**
+ * Returns the sequence feature colour rendered at the given column position,
+ * or null if none found. The feature of highest render order (i.e. on top) is
+ * found, subject to both feature type and feature group being visible, and
+ * its colour returned. This method is suitable when no feature transparency
+ * applied (only the topmost visible feature colour is rendered).
+ *
+ * Note this method does not check for a gap in the column so would return the
+ * colour for features enclosing a gapped column. Check for gap before calling
+ * if different behaviour is wanted.
+ *
+ * @param seq
+ * @param column
+ * (1..)
+ * @return
+ */
+ Color findFeatureColour(SequenceI seq, int column)
+ {
+ /*
+ * check for new feature added while processing
+ */
+ updateFeatures();
+
+ /*
+ * show complement features on top (if configured to show them)
+ */
+ if (av.isShowComplementFeatures() && av.isShowComplementFeaturesOnTop())
+ {
+ Color col = findComplementFeatureColour(seq, column);
+ if (col != null)
+ {
+ return col;
+ }
+ }
+
+ /*
+ * inspect features in reverse renderOrder (the last in the array is
+ * displayed on top) until we find one that is rendered at the position
+ */
+ for (int renderIndex = renderOrder.length
+ - 1; renderIndex >= 0; renderIndex--)
+ {
+ String type = renderOrder[renderIndex];
+ if (!showFeatureOfType(type))
+ {
+ continue;
+ }
+
+ /*
+ * find features of this type, and the colour of the _last_ one
+ * (the one that would be drawn on top) that has a colour
+ */
+ List overlaps = seq.findFeatures(column, column,
+ type);
+ for (int i = overlaps.size() - 1 ; i >= 0 ; i--)
+ {
+ SequenceFeature sequenceFeature = overlaps.get(i);
+ if (!featureGroupNotShown(sequenceFeature))
+ {
+ Color col = getColour(sequenceFeature);
+ if (col != null)
+ {
+ return col;
+ }
+ }
+ }
+ }
+
+ /*
+ * show complement features underneath (if configured to show them)
+ */
+ Color col = null;
+ if (av.isShowComplementFeatures()
+ && !av.isShowComplementFeaturesOnTop())
+ {
+ col = findComplementFeatureColour(seq, column);
+ }
+
+ return col;
+ }
+
+ Color findComplementFeatureColour(SequenceI seq, int column)
+ {
+ AlignViewportI complement = av.getCodingComplement();
+ AlignFrame af = Desktop.getAlignFrameFor(complement);
+ FeatureRendererModel fr2 = af.getFeatureRenderer();
+ MappedFeatures mf = fr2.findComplementFeaturesAtResidue(seq,
+ seq.findPosition(column - 1));
+ if (mf == null)
+ {
+ return null;
+ }
+ ReverseListIterator it = new ReverseListIterator<>(
+ mf.features);
+ while (it.hasNext())
+ {
+ SequenceFeature sf = it.next();
+ if (!fr2.featureGroupNotShown(sf))
+ {
+ Color col = fr2.getColour(sf);
+ if (col != null)
+ {
+ return col;
+ }
+ }
+ }
+ return null;
+ }
}