changeset 1432:5b9692768beb single-point

Further snap fixes
author Chris Cannam
date Wed, 20 Mar 2019 15:46:17 +0000
parents af824022bffd
children 9abddbd57667
files layer/NoteLayer.cpp layer/RegionLayer.cpp layer/TimeValueLayer.cpp
diffstat 3 files changed, 90 insertions(+), 186 deletions(-) [+]
line wrap: on
line diff
--- a/layer/NoteLayer.cpp	Wed Mar 20 14:59:34 2019 +0000
+++ b/layer/NoteLayer.cpp	Wed Mar 20 15:46:17 2019 +0000
@@ -534,72 +534,33 @@
         return Layer::snapToFeatureFrame(v, frame, resolution, snap);
     }
 
+    // SnapLeft / SnapRight: return frame of nearest feature in that
+    // direction no matter how far away
+    //
+    // SnapNeighbouring: return frame of feature that would be used in
+    // an editing operation, i.e. closest feature in either direction
+    // but only if it is "close enough"
+
     resolution = m_model->getResolution();
-    EventVector points;
 
     if (snap == SnapNeighbouring) {
-        
-        points = getLocalPoints(v, v->getXForFrame(frame));
+        EventVector points = getLocalPoints(v, v->getXForFrame(frame));
         if (points.empty()) return false;
         frame = points.begin()->getFrame();
         return true;
     }    
 
-    //!!! I think this is not quite right - we want to be able to snap
-    //!!! to events that are nearby but not covering the given frame,
-    //!!! and I think that worked with the old code. Compare and
-    //!!! revise.
-
-    points = m_model->getEventsCovering(frame);
-    sv_frame_t snapped = frame;
-    bool found = false;
-
-    for (EventVector::const_iterator i = points.begin();
-         i != points.end(); ++i) {
-
-        if (snap == SnapRight) {
-
-            if (i->getFrame() > frame) {
-                snapped = i->getFrame();
-                found = true;
-                break;
-            }
-
-        } else if (snap == SnapLeft) {
-
-            if (i->getFrame() <= frame) {
-                snapped = i->getFrame();
-                found = true; // don't break, as the next may be better
-            } else {
-                break;
-            }
-
-        } else { // nearest
-
-            EventVector::const_iterator j = i;
-            ++j;
-
-            if (j == points.end()) {
-
-                snapped = i->getFrame();
-                found = true;
-                break;
-
-            } else if (j->getFrame() >= frame) {
-
-                if (j->getFrame() - frame < frame - i->getFrame()) {
-                    snapped = j->getFrame();
-                } else {
-                    snapped = i->getFrame();
-                }
-                found = true;
-                break;
-            }
-        }
+    Event e;
+    if (m_model->getNearestEventMatching
+        (frame,
+         [](Event) { return true; },
+         snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
+         e)) {
+        frame = e.getFrame();
+        return true;
     }
 
-    frame = snapped;
-    return found;
+    return false;
 }
 
 void
--- a/layer/RegionLayer.cpp	Wed Mar 20 14:59:34 2019 +0000
+++ b/layer/RegionLayer.cpp	Wed Mar 20 15:46:17 2019 +0000
@@ -445,83 +445,69 @@
         return Layer::snapToFeatureFrame(v, frame, resolution, snap);
     }
 
+    // SnapLeft / SnapRight: return frame of nearest feature in that
+    // direction no matter how far away
+    //
+    // SnapNeighbouring: return frame of feature that would be used in
+    // an editing operation, i.e. closest feature in either direction
+    // but only if it is "close enough"
+
     resolution = m_model->getResolution();
-    EventVector points;
 
     if (snap == SnapNeighbouring) {
-        
-        points = getLocalPoints(v, v->getXForFrame(frame));
+        EventVector points = getLocalPoints(v, v->getXForFrame(frame));
         if (points.empty()) return false;
         frame = points.begin()->getFrame();
         return true;
     }    
 
-    //!!! I think this is not quite right - we want to be able to snap
-    //!!! to events that are nearby but not covering the given frame,
-    //!!! and I think that worked with the old code. Compare and
-    //!!! revise.
+    // Normally we snap to the start frame of whichever event we
+    // find. However here, for SnapRight only, if the end frame of
+    // whichever event we would have snapped to had we been snapping
+    // left is closer than the start frame of the next event to the
+    // right, then we snap to that frame instead. Clear?
     
-    points = m_model->getEventsCovering(frame);
-    sv_frame_t snapped = frame;
-    bool found = false;
+    Event left;
+    bool haveLeft = false;
+    if (m_model->getNearestEventMatching
+        (frame, [](Event) { return true; }, EventSeries::Backward, left)) {
+        haveLeft = true;
+    }
 
-    for (EventVector::const_iterator i = points.begin();
-         i != points.end(); ++i) {
+    if (snap == SnapLeft) {
+        frame = left.getFrame();
+        return haveLeft;
+    }
 
-        if (snap == SnapRight) {
+    Event right;
+    bool haveRight = false;
+    if (m_model->getNearestEventMatching
+        (frame, [](Event) { return true; }, EventSeries::Forward, right)) {
+        haveRight = true;
+    }
 
-            // The best frame to snap to is the end frame of whichever
-            // feature we would have snapped to the start frame of if
-            // we had been snapping left.
-
-            if (i->getFrame() <= frame) {
-                if (i->getFrame() + i->getDuration() > frame) {
-                    snapped = i->getFrame() + i->getDuration();
-                    found = true; // don't break, as the next may be better
+    if (haveLeft) {
+        sv_frame_t leftEnd = left.getFrame() + left.getDuration();
+        if (leftEnd > frame) {
+            if (haveRight) {
+                if (leftEnd - frame < right.getFrame() - frame) {
+                    frame = leftEnd;
+                } else {
+                    frame = right.getFrame();
                 }
             } else {
-                if (!found) {
-                    snapped = i->getFrame();
-                    found = true;
-                }
-                break;
+                frame = leftEnd;
             }
-
-        } else if (snap == SnapLeft) {
-
-            if (i->getFrame() <= frame) {
-                snapped = i->getFrame();
-                found = true; // don't break, as the next may be better
-            } else {
-                break;
-            }
-
-        } else { // nearest
-
-            EventVector::const_iterator j = i;
-            ++j;
-
-            if (j == points.end()) {
-
-                snapped = i->getFrame();
-                found = true;
-                break;
-
-            } else if (j->getFrame() >= frame) {
-
-                if (j->getFrame() - frame < frame - i->getFrame()) {
-                    snapped = j->getFrame();
-                } else {
-                    snapped = i->getFrame();
-                }
-                found = true;
-                break;
-            }
+            return true;
         }
     }
 
-    frame = snapped;
-    return found;
+    if (haveRight) {
+        frame = right.getFrame();
+        return true;
+    }
+
+    return false;
 }
 
 bool
@@ -533,82 +519,41 @@
         return Layer::snapToSimilarFeature(v, frame, resolution, snap);
     }
 
+    // snap is only permitted to be SnapLeft or SnapRight here.  We
+    // don't do the same trick as in snapToFeatureFrame, of snapping
+    // to the end of a feature sometimes.
+    
     resolution = m_model->getResolution();
 
-    /*!!! todo: overhaul the logic of this function (and supporting
-          apis in EventSeries / RegionModel)
+    Event ref;
+    Event e;
+    float matchvalue;
+    bool found;
 
-    const EventVector &points = m_model->getPoints();
-    EventVector close = m_model->getPoints(frame, frame);
-    */
-    EventVector points = m_model->getAllEvents();
-    EventVector close = {};
-    
-    EventVector::const_iterator i;
+    found = m_model->getNearestEventMatching
+        (frame, [](Event) { return true; }, EventSeries::Backward, ref);
 
-    sv_frame_t matchframe = frame;
-    double matchvalue = 0.f;
-
-    for (i = close.begin(); i != close.end(); ++i) {
-        if (i->getFrame() > frame) break;
-        matchvalue = i->getValue();
-        matchframe = i->getFrame();
+    if (!found) {
+        return false;
     }
 
-    sv_frame_t snapped = frame;
-    bool found = false;
-    bool distant = false;
-    double epsilon = 0.0001;
+    matchvalue = ref.getValue();
+    
+    found = m_model->getNearestEventMatching
+        (frame,
+         [matchvalue](Event e) {
+             double epsilon = 0.0001;
+             return fabs(e.getValue() - matchvalue) < epsilon;
+         },
+         snap == SnapLeft ? EventSeries::Backward : EventSeries::Forward,
+         e);
 
-    i = close.begin();
-
-    // Scan through the close points first, then the more distant ones
-    // if no suitable close one is found. So the while-termination
-    // condition here can only happen once i has passed through the
-    // whole of the close container and then the whole of the separate
-    // points container. The two iterators are totally distinct, but
-    // have the same type so we cheekily use the same variable and a
-    // single loop for both.
-
-    while (i != points.end()) {
-
-        if (!distant) {
-            if (i == close.end()) {
-                // switch from the close container to the points container
-                i = points.begin();
-                distant = true;
-            }
-        }
-
-        if (snap == SnapRight) {
-
-            if (i->getFrame() > matchframe &&
-                fabs(i->getValue() - matchvalue) < epsilon) {
-                snapped = i->getFrame();
-                found = true;
-                break;
-            }
-
-        } else if (snap == SnapLeft) {
-
-            if (i->getFrame() < matchframe) {
-                if (fabs(i->getValue() - matchvalue) < epsilon) {
-                    snapped = i->getFrame();
-                    found = true; // don't break, as the next may be better
-                }
-            } else if (found || distant) {
-                break;
-            }
-
-        } else { 
-            // no other snap types supported
-        }
-
-        ++i;
+    if (!found) {
+        return false;
     }
 
-    frame = snapped;
-    return found;
+    frame = e.getFrame();
+    return true;
 }
 
 QString
--- a/layer/TimeValueLayer.cpp	Wed Mar 20 14:59:34 2019 +0000
+++ b/layer/TimeValueLayer.cpp	Wed Mar 20 15:46:17 2019 +0000
@@ -662,10 +662,6 @@
 
     // SnapLeft / SnapRight: return frame of nearest feature in that
     // direction no matter how far away
-    // 
-    // SnapNearest: return frame of nearest feature in either
-    // direction no matter how far away - I'm fairly sure this is
-    // never actually used
     //
     // SnapNeighbouring: return frame of feature that would be used in
     // an editing operation, i.e. closest feature in either direction
@@ -703,6 +699,8 @@
         return Layer::snapToSimilarFeature(v, frame, resolution, snap);
     }
 
+    // snap is only permitted to be SnapLeft or SnapRight here.
+    
     resolution = m_model->getResolution();
 
     Event ref;