+
+ /*
+ * a feature is redundant for rendering purposes if it has the
+ * same extent as another (so would just redraw the same colour);
+ * (checking type and isContactFeature as a fail-safe here, although
+ * currently they are guaranteed to match in this context)
+ */
+ if (lastFeature != null
+ && sf.getBegin() == lastFeature.getBegin()
+ && sf.getEnd() == lastFeature.getEnd()
+ && sf.isContactFeature() == lastFeature.isContactFeature()
+ && sf.getType().equals(lastFeature.getType()))
+ {
+ it.remove();
+ }
+ lastFeature = sf;
+ }
+ }
+
+ @Override
+ public Map<String, FeatureMatcherSetI> getFeatureFilters()
+ {
+ return featureFilters;
+ }
+
+ @Override
+ public void setFeatureFilters(Map<String, FeatureMatcherSetI> filters)
+ {
+ featureFilters = filters;
+ }
+
+ @Override
+ public FeatureMatcherSetI getFeatureFilter(String featureType)
+ {
+ return featureFilters.get(featureType);
+ }
+
+ @Override
+ public void setFeatureFilter(String featureType, FeatureMatcherSetI filter)
+ {
+ if (filter == null || filter.isEmpty())
+ {
+ featureFilters.remove(featureType);
+ }
+ else
+ {
+ featureFilters.put(featureType, filter);
+ }
+ }
+
+ /**
+ * Answers the colour for the feature, or null if the feature is excluded by
+ * feature group visibility, by filters, or by colour threshold settings. This
+ * method does not take feature visibility into account.
+ *
+ * @param sf
+ * @param fc
+ * @return
+ */
+ public Color getColor(SequenceFeature sf, FeatureColourI fc)
+ {
+ /*
+ * is the feature group displayed?
+ */
+ if (featureGroupNotShown(sf))
+ {
+ return null;
+ }
+
+ /*
+ * does the feature pass filters?
+ */
+ if (!featureMatchesFilters(sf))
+ {
+ return null;
+ }
+
+ return fc.getColor(sf);
+ }
+
+ /**
+ * Answers true if there no are filters defined for the feature type, or this
+ * feature matches the filters. Answers false if the feature fails to match
+ * filters.
+ *
+ * @param sf
+ * @return
+ */
+ protected boolean featureMatchesFilters(SequenceFeature sf)
+ {
+ FeatureMatcherSetI filter = featureFilters.get(sf.getType());
+ return filter == null ? true : filter.matches(sf);
+ }
+
+ @Override
+ public MappedFeatures findComplementFeaturesAtResidue(
+ final SequenceI sequence, final int pos)
+ {
+ SequenceI ds = sequence.getDatasetSequence();
+ if (ds == null)
+ {
+ ds = sequence;
+ }
+ final char residue = ds.getCharAt(pos - ds.getStart());
+
+ List<SequenceFeature> found = new ArrayList<>();
+ List<AlignedCodonFrame> mappings = this.av.getAlignment()
+ .getCodonFrame(sequence);
+
+ /*
+ * fudge: if no mapping found, check the complementary alignment
+ * todo: only store in one place? StructureSelectionManager?
+ */
+ if (mappings.isEmpty())
+ {
+ mappings = this.av.getCodingComplement().getAlignment()
+ .getCodonFrame(sequence);
+ }
+
+ /*
+ * todo: direct lookup of CDS for peptide and vice-versa; for now,
+ * have to search through an unordered list of mappings for a candidate
+ */
+ SequenceToSequenceMapping mapping = null;
+ SequenceI mapFrom = null;
+
+ for (AlignedCodonFrame acf : mappings)
+ {
+ mapping = acf.getCoveringCodonMapping(ds);
+ if (mapping == null)