From: hansonr Date: Thu, 1 Aug 2019 10:07:19 +0000 (-0500) Subject: JAL-3253-applet JAL-3383 Overview speed up -- see Issue comments. X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=fbf6a124e2ffe81bcde747fda6f004748eb3b479;p=jalview.git JAL-3253-applet JAL-3383 Overview speed up -- see Issue comments. --- diff --git a/src/jalview/bin/JalviewJS2.java b/src/jalview/bin/JalviewJS2.java index 71c06b3..1ed9f27 100644 --- a/src/jalview/bin/JalviewJS2.java +++ b/src/jalview/bin/JalviewJS2.java @@ -35,7 +35,7 @@ public class JalviewJS2 { args = new String[] { "open", "examples/uniref50.fa", "features", "examples/exampleFeatures.txt" - // , "noannotation" + , "noannotation" , "showoverview" }; } diff --git a/src/jalview/datamodel/Sequence.java b/src/jalview/datamodel/Sequence.java index 6b52480..7624d2c 100755 --- a/src/jalview/datamodel/Sequence.java +++ b/src/jalview/datamodel/Sequence.java @@ -1967,15 +1967,15 @@ public class Sequence extends ASequence implements SequenceI List result = getFeatures().findFeatures(startPos, endPos, types); - if (datasetSequence != null) - { - result = datasetSequence.getFeatures().findFeatures(startPos, endPos, - types); - } - else - { - result = sequenceFeatureStore.findFeatures(startPos, endPos, types); - } + // if (datasetSequence != null) + // { + // result = datasetSequence.getFeatures().findFeatures(startPos, endPos, + // types); + // } + // else + // { + // result = sequenceFeatureStore.findFeatures(startPos, endPos, types); + // } /* * if end column is gapped, endPos may be to the right, @@ -2152,4 +2152,19 @@ public class Sequence extends ASequence implements SequenceI { argb = null; } + + /** + * @author Bob Hanson 2019.07.30 + * + * allows passing the result ArrayList as a parameter to avoid unnecessary construction + * + */ + @Override + public void findFeatures(int column, String type, + List result) + { + getFeatures().findFeatures(findPosition(column - 1), type, result); + } + + } diff --git a/src/jalview/datamodel/SequenceI.java b/src/jalview/datamodel/SequenceI.java index e521029..013897d 100755 --- a/src/jalview/datamodel/SequenceI.java +++ b/src/jalview/datamodel/SequenceI.java @@ -584,11 +584,37 @@ public interface SequenceI extends ASequenceI */ public int firstResidueOutsideIterator(Iterator it); + /** + * @author Bob Hanson 2019.07.30 + * + * get a 4-byte color, with caching + * + */ public int getColor(int i); - public int setColor(int i, int rgb); + /** + * @author Bob Hanson 2019.07.30 + * + * set a 4-byte color, with caching + * + */ + public int setColor(int i, int argb); + /** + * @author Bob Hanson 2019.07.30 + * + * allows resetting the color cache + * + */ public void resetColors(); + /** + * @author Bob Hanson 2019.07.30 + * + * allows passing the result ArrayList as a parameter to avoid unnecessary construction + * + */ + void findFeatures(int column, String type, List result); + } diff --git a/src/jalview/datamodel/features/FeatureStore.java b/src/jalview/datamodel/features/FeatureStore.java index 54c0d59..686ac26 100644 --- a/src/jalview/datamodel/features/FeatureStore.java +++ b/src/jalview/datamodel/features/FeatureStore.java @@ -90,6 +90,10 @@ public class FeatureStore float nonPositionalMaxScore; + private SequenceFeature[] temp = new SequenceFeature[3]; + + private boolean isTainted; + /** * Constructor */ @@ -250,6 +254,7 @@ public class FeatureStore features = new IntervalStore<>(); } features.add(feature); + isTainted = true; } /** @@ -602,7 +607,7 @@ public class FeatureStore positionalMaxScore = Float.NaN; nonPositionalMinScore = Float.NaN; nonPositionalMaxScore = Float.NaN; - + isTainted = true; /* * scan non-positional features for groups and scores */ @@ -851,4 +856,107 @@ public class FeatureStore } return modified; } + + /** + * Find all features containing this position. + * Uses isTainted field to know when to reconstruct its temporary array. + * + * @param pos + * @return list of SequenceFeatures + * @author Bob Hanson 2019.07.30 + */ + public void findOverlappingFeatures(int pos, List result) + { + + if (contactFeatureStarts != null) + { + findContacts(contactFeatureStarts, pos, result, true); + findContacts(contactFeatureEnds, pos, result, false); + } + if (features != null) + { + int n = features.size(); + if (isTainted) + { + isTainted = false; + if (temp.length < n) + { + temp = new SequenceFeature[n << 1]; + } + features.toArray(temp); + } + findOverlaps(temp, n, pos, result); + } + } + + /** + * Binary search for contact start or end at a given (Overview) position. + * + * @param l + * @param pos + * @param result + * @param isStart + * + * @author Bob Hanson 2019.07.30 + */ + private static void findContacts(List l, int pos, + List result, boolean isStart) + { + int low = 0; + int high = l.size() - 1; + while (low <= high) + { + int mid = (low + high) >>> 1; + SequenceFeature f = l.get(mid); + switch (Long.signum((isStart ? f.begin : f.end) - pos)) + { + case -1: + low = mid + 1; + continue; + case 1: + high = mid - 1; + continue; + case 0: + int m = mid; + result.add(f); + // could be "5" in 12345556788 ? + while (++mid <= high && (f = l.get(mid)) != null + && (isStart ? f.begin : f.end) == pos) + { + result.add(f); + } + while (--m >= low && (f = l.get(m)) != null + && (isStart ? f.begin : f.end) == pos) + { + result.add(f); + } + return; + } + } + } + + /** + * Brute force point-interval overlap test + * + * @param features + * @param n + * @param pos + * @param result + */ + private static void findOverlaps(SequenceFeature[] features, int n, + int pos, + List result) + { + // BH I know, brute force. We need a single-position overlap + // method for IntervalStore, I think. + for (int i = n; --i >= 0;) + { + SequenceFeature f = features[i]; + if (f.begin <= pos && f.end >= pos) + { + result.add(f); + } + } + } + } diff --git a/src/jalview/datamodel/features/SequenceFeatures.java b/src/jalview/datamodel/features/SequenceFeatures.java index 9925f2b..93ee71b 100644 --- a/src/jalview/datamodel/features/SequenceFeatures.java +++ b/src/jalview/datamodel/features/SequenceFeatures.java @@ -109,12 +109,10 @@ public class SequenceFeatures implements SequenceFeaturesI String... type) { List result = new ArrayList<>(); - for (FeatureStore featureSet : varargToTypes(type)) { result.addAll(featureSet.findOverlappingFeatures(from, to)); } - return result; } @@ -219,6 +217,7 @@ public class SequenceFeatures implements SequenceFeaturesI return featureStore.values(); } + List types = new ArrayList<>(); List args = Arrays.asList(type); for (Entry featureType : featureStore.entrySet()) @@ -465,4 +464,21 @@ public class SequenceFeatures implements SequenceFeaturesI { featureStore.clear(); } + + /** + * Simplified find for features associated with a given position. + * + * @author Bob Hanson 2019.07.30 + */ + @Override + public void findFeatures(int pos, String type, List list) + { + FeatureStore fs = featureStore.get(type); + if (fs != null) + { + fs.findOverlappingFeatures(pos, list); + } + } + + } diff --git a/src/jalview/datamodel/features/SequenceFeaturesI.java b/src/jalview/datamodel/features/SequenceFeaturesI.java index ca0283e..ec4b38c 100644 --- a/src/jalview/datamodel/features/SequenceFeaturesI.java +++ b/src/jalview/datamodel/features/SequenceFeaturesI.java @@ -228,4 +228,6 @@ public interface SequenceFeaturesI * Deletes all positional and non-positional features */ void deleteAll(); + + void findFeatures(int pos, String type, List result); } diff --git a/src/jalview/gui/OverviewPanel.java b/src/jalview/gui/OverviewPanel.java index cc647a8..532028c 100755 --- a/src/jalview/gui/OverviewPanel.java +++ b/src/jalview/gui/OverviewPanel.java @@ -131,7 +131,7 @@ public class OverviewPanel extends JPanel && getHeight() == od.getHeight() + ph) { // BH: resizing is now exceptionally fast. - updateOverviewImage(); + // updateOverviewImage(); } else { diff --git a/src/jalview/renderer/seqfeatures/FeatureRenderer.java b/src/jalview/renderer/seqfeatures/FeatureRenderer.java index 13885b4..bed72c5 100644 --- a/src/jalview/renderer/seqfeatures/FeatureRenderer.java +++ b/src/jalview/renderer/seqfeatures/FeatureRenderer.java @@ -33,6 +33,7 @@ import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; +import java.util.ArrayList; import java.util.List; public class FeatureRenderer extends FeatureRendererModel @@ -417,6 +418,8 @@ public class FeatureRenderer extends FeatureRendererModel findAllFeatures(); } + private List overlaps = new ArrayList<>(); + /** * 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 @@ -428,12 +431,17 @@ public class FeatureRenderer extends FeatureRendererModel * colour for features enclosing a gapped column. Check for gap before calling * if different behaviour is wanted. * + * BH 2019.07.30 + * + * Adds a result ArrayList to parameters in order to avoid an unnecessary construction of that for every pixel checked. + * + * * @param seq * @param column * (1..) * @return */ - Color findFeatureColour(SequenceI seq, int column) + private Color findFeatureColour(SequenceI seq, int column) { /* * check for new feature added while processing @@ -453,16 +461,19 @@ public class FeatureRenderer extends FeatureRendererModel continue; } - List overlaps = seq.findFeatures(column, column, - type); - for (SequenceFeature sequenceFeature : overlaps) + overlaps.clear(); + seq.findFeatures(column, type, overlaps); + if (overlaps.size() > 0) { - if (!featureGroupNotShown(sequenceFeature)) + for (SequenceFeature sequenceFeature : overlaps) { - Color col = getColour(sequenceFeature); - if (col != null) + if (!featureGroupNotShown(sequenceFeature)) { - return col; + Color col = getColour(sequenceFeature); + if (col != null) + { + return col; + } } } }