X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fjalview%2Fdatamodel%2Ffeatures%2FFeatureStore.java;h=686ac26cf17b48ceaa9c1bbcbb5bf6173ce584ee;hb=0a94254977a8824ad154325a8ccb6d6d1d4e4dec;hp=13472139cb1534d5f7515f44014db3ff4aaf7b68;hpb=81d2ea57ab71c0806a036833f73711bc4ad5c76d;p=jalview.git diff --git a/src/jalview/datamodel/features/FeatureStore.java b/src/jalview/datamodel/features/FeatureStore.java index 1347213..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 */ @@ -812,13 +817,15 @@ public class FeatureStore } /** - * Adds the shift value to the start and end of all positional features. - * Returns true if at least one feature was updated, else false. + * Adds the shift amount to the start and end of all positional features whose + * start position is at or after fromPosition. Returns true if at least one + * feature was shifted, else false. * - * @param shift + * @param fromPosition + * @param shiftBy * @return */ - public synchronized boolean shiftFeatures(int shift) + public synchronized boolean shiftFeatures(int fromPosition, int shiftBy) { /* * Because begin and end are final fields (to ensure the data store's @@ -828,22 +835,128 @@ public class FeatureStore boolean modified = false; for (SequenceFeature sf : getPositionalFeatures()) { - modified = true; - int newBegin = sf.getBegin() + shift; - int newEnd = sf.getEnd() + shift; - - /* - * sanity check: don't shift left of the first residue - */ - if (newEnd > 0) + if (sf.getBegin() >= fromPosition) { - newBegin = Math.max(1, newBegin); - SequenceFeature sf2 = new SequenceFeature(sf, newBegin, newEnd, - sf.getFeatureGroup(), sf.getScore()); - addFeature(sf2); + modified = true; + int newBegin = sf.getBegin() + shiftBy; + int newEnd = sf.getEnd() + shiftBy; + + /* + * sanity check: don't shift left of the first residue + */ + if (newEnd > 0) + { + newBegin = Math.max(1, newBegin); + SequenceFeature sf2 = new SequenceFeature(sf, newBegin, newEnd, + sf.getFeatureGroup(), sf.getScore()); + addFeature(sf2); + } + delete(sf); } - delete(sf); } 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); + } + } + } + }