diff base/PointSeries.h @ 1614:2e14a7876945 single-point

Fixes and tests for PointSeries
author Chris Cannam
date Thu, 07 Mar 2019 14:35:57 +0000
parents 23a29e5dc0e9
children
line wrap: on
line diff
--- a/base/PointSeries.h	Wed Mar 06 16:37:10 2019 +0000
+++ b/base/PointSeries.h	Thu Mar 07 14:35:57 2019 +0000
@@ -19,6 +19,8 @@
 
 #include <set>
 
+//#define DEBUG_POINT_SERIES 1
+
 class PointSeries
 {
 public:
@@ -33,26 +35,28 @@
             sv_frame_t frame = p.getFrame();
             sv_frame_t endFrame = p.getFrame() + p.getDuration();
 
-            std::set<Point> active;
-            auto itr = m_seams.lower_bound(frame);
-            if (itr == m_seams.end() || itr->first > frame) {
-                if (itr != m_seams.begin()) {
-                    --itr;
+            createSeam(frame);
+            createSeam(endFrame);
+            
+            auto i0 = m_seams.find(frame); // must succeed after createSeam
+            auto i1 = m_seams.find(endFrame); // likewise
+
+            for (auto i = i0; i != i1; ++i) {
+                if (i == m_seams.end()) {
+                    SVCERR << "ERROR: PointSeries::add: "
+                           << "reached end of seam map"
+                           << endl;
+                    break;
                 }
+                i->second.insert(p);
             }
-            if (itr != m_seams.end()) {
-                active = itr->second;
-            }
-            active.insert(p);
-            m_seams[frame] = active;
+        }
 
-            for (itr = m_seams.find(frame); itr->first < endFrame; ++itr) {
-                active = itr->second;
-                itr->second.insert(p);
-            }
-
-            m_seams[endFrame] = active;
-        }
+#ifdef DEBUG_POINT_SERIES
+        std::cerr << "after add:" << std::endl;
+        dumpPoints();
+        dumpSeams();
+#endif
     }
 
     void remove(const Point &p) {
@@ -72,17 +76,32 @@
             sv_frame_t frame = p.getFrame();
             sv_frame_t endFrame = p.getFrame() + p.getDuration();
 
-            auto itr = m_seams.find(frame);
-            if (itr == m_seams.end()) {
-                SVCERR << "WARNING: PointSeries::remove: frame " << frame
+            auto i0 = m_seams.find(frame);
+            auto i1 = m_seams.find(endFrame);
+
+#ifdef DEBUG_POINT_SERIES
+            // This should be impossible if we found p in m_points above
+            if (i0 == m_seams.end() || i1 == m_seams.end()) {
+                SVCERR << "ERROR: PointSeries::remove: either frame " << frame
+                       << " or endFrame " << endFrame
                        << " for point not found in seam map: point is "
                        << p.toXmlString() << endl;
-                return;
             }
+#endif
 
-            while (itr != m_seams.end() && itr->first <= endFrame) {
-                itr->second.erase(p);
-                ++itr;
+            for (auto i = i0; i != i1; ++i) {
+                if (i == m_seams.end()) {
+                    SVCERR << "ERROR: PointSeries::remove: "
+                           << "reached end of seam map"
+                           << endl;
+                    break;
+                }
+                // Can't just erase(p) as that would erase all of
+                // them, if there are several identical ones
+                auto si = i->second.find(p);
+                if (si != i->second.end()) {
+                    i->second.erase(si);
+                }
             }
 
             // Shall we "garbage-collect" here? We could be leaving
@@ -91,6 +110,12 @@
             // slow us down. But a lot depends on whether callers ever
             // really delete anything much.
         }
+
+#ifdef DEBUG_POINT_SERIES
+        std::cerr << "after remove:" << std::endl;
+        dumpPoints();
+        dumpSeams();
+#endif
     }
 
     bool contains(const Point &p) {
@@ -118,14 +143,86 @@
      * equal to it and its end frame (start + duration) is greater
      * than it.
      */
-    PointVector getPointsSpanning(sv_frame_t frame) {
-        return {};
+    PointVector getPointsSpanning(sv_frame_t frame) const {
+        PointVector span;
+
+        // first find any zero-duration points
+        auto pitr = m_points.lower_bound(Point(frame, QString()));
+        if (pitr != m_points.end()) {
+            while (pitr->getFrame() == frame) {
+                if (!pitr->haveDuration()) {
+                    span.push_back(*pitr);
+                }
+                ++pitr;
+            }
+        }
+
+        // now any non-zero-duration ones from the seam map
+        auto sitr = m_seams.lower_bound(frame);
+        if (sitr == m_seams.end() || sitr->first > frame) {
+            if (sitr != m_seams.begin()) {
+                --sitr;
+            }                
+        }
+        if (sitr != m_seams.end() && sitr->first <= frame) {
+            for (auto p: sitr->second) {
+                span.push_back(p);
+            }
+        }
+        
+        return span;
     }
 
 private:
     int m_count;
-    std::multiset<Point> m_points;
-    std::map<sv_frame_t, std::set<Point>> m_seams;
+
+    typedef std::multiset<Point> PointMultiset;
+    PointMultiset m_points;
+
+    typedef std::map<sv_frame_t, std::multiset<Point>> FramePointsMap;
+    FramePointsMap m_seams;
+
+    /** Create a seam at the given frame, copying from the prior seam
+     *  if there is one. If a seam already exists at the given frame,
+     *  leave it untouched.
+     */
+    void createSeam(sv_frame_t frame) {
+        auto itr = m_seams.lower_bound(frame);
+        if (itr == m_seams.end() || itr->first > frame) {
+            if (itr != m_seams.begin()) {
+                --itr;
+            }
+        }
+        if (itr == m_seams.end()) {
+            m_seams[frame] = {};
+        } else if (itr->first < frame) {
+            m_seams[frame] = itr->second;
+        } else if (itr->first > frame) { // itr must be begin()
+            m_seams[frame] = {};
+        }
+    }
+
+#ifdef DEBUG_POINT_SERIES
+    void dumpPoints() const {
+        std::cerr << "POINTS [" << std::endl;
+        for (const auto &p: m_points) {
+            std::cerr << p.toXmlString("  ");
+        }
+        std::cerr << "]" << std::endl;
+    }
+    
+    void dumpSeams() const {
+        std::cerr << "SEAMS [" << std::endl;
+        for (const auto &s: m_seams) {
+            std::cerr << "  " << s.first << " -> {" << std::endl;
+            for (const auto &p: s.second) {
+                std::cerr << p.toXmlString("    ");
+            }
+            std::cerr << "  }" << std::endl;
+        }
+        std::cerr << "]" << std::endl;
+    }
+#endif
 };
 
 #endif