Mercurial > hg > svcore
view 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 source
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /* Sonic Visualiser An audio file viewer and annotation editor. Centre for Digital Music, Queen Mary, University of London. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. See the file COPYING included with this distribution for more information. */ #ifndef SV_POINT_SERIES_H #define SV_POINT_SERIES_H #include "Point.h" #include <set> //#define DEBUG_POINT_SERIES 1 class PointSeries { public: PointSeries() : m_count(0) { } void add(const Point &p) { m_points.insert(p); ++m_count; if (p.haveDuration()) { sv_frame_t frame = p.getFrame(); sv_frame_t endFrame = p.getFrame() + p.getDuration(); 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); } } #ifdef DEBUG_POINT_SERIES std::cerr << "after add:" << std::endl; dumpPoints(); dumpSeams(); #endif } void remove(const Point &p) { // erase first itr that matches p; if there is more than one // p, erase(p) would remove all of them, but we only want to // remove (any) one auto pitr = m_points.find(p); if (pitr == m_points.end()) { return; // we don't know this point } else { m_points.erase(pitr); --m_count; } if (p.haveDuration()) { sv_frame_t frame = p.getFrame(); sv_frame_t endFrame = p.getFrame() + p.getDuration(); 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; } #endif 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 // lots of empty point-sets, or consecutive identical // ones, which are a pure irrelevance that take space and // 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) { return m_points.find(p) != m_points.end(); } int count() const { return m_count; } bool isEmpty() const { return m_count == 0; } void clear() { m_points.clear(); m_seams.clear(); m_count = 0; } /** * Retrieve all points that span the given frame. A point without * duration spans a frame if its own frame is equal to it. A point * with duration spans a frame if its start frame is less than or * equal to it and its end frame (start + duration) is greater * than it. */ 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; 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