annotate data/model/SparseModel.h @ 1622:172bd3374adf single-point

Add (disabled by default) stress test for EventSeries
author Chris Cannam
date Mon, 11 Mar 2019 09:53:28 +0000
parents 2e14a7876945
children 628ffbb05856
rev   line source
Chris@147 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@147 2
Chris@147 3 /*
Chris@147 4 Sonic Visualiser
Chris@147 5 An audio file viewer and annotation editor.
Chris@147 6 Centre for Digital Music, Queen Mary, University of London.
Chris@147 7 This file copyright 2006 Chris Cannam.
Chris@147 8
Chris@147 9 This program is free software; you can redistribute it and/or
Chris@147 10 modify it under the terms of the GNU General Public License as
Chris@147 11 published by the Free Software Foundation; either version 2 of the
Chris@147 12 License, or (at your option) any later version. See the file
Chris@147 13 COPYING included with this distribution for more information.
Chris@147 14 */
Chris@147 15
cannam@1452 16 #ifndef SV_SPARSE_MODEL_H
cannam@1452 17 #define SV_SPARSE_MODEL_H
Chris@147 18
Chris@150 19 #include "Model.h"
Chris@420 20 #include "TabularModel.h"
Chris@147 21 #include "base/Command.h"
Chris@425 22 #include "base/RealTime.h"
Chris@1218 23 #include "system/System.h"
Chris@147 24
Chris@147 25 #include <iostream>
Chris@147 26
Chris@147 27 #include <set>
Chris@420 28 #include <vector>
Chris@420 29 #include <algorithm>
Chris@608 30 #include <iterator>
Chris@420 31
Chris@425 32 #include <cmath>
Chris@425 33
Chris@147 34 #include <QMutex>
Chris@147 35 #include <QTextStream>
Chris@147 36
Chris@147 37 /**
Chris@147 38 * Model containing sparse data (points with some properties). The
Chris@147 39 * properties depend on the point type.
Chris@147 40 */
Chris@147 41
Chris@147 42 template <typename PointType>
Chris@420 43 class SparseModel : public Model,
Chris@420 44 public TabularModel
Chris@147 45 {
Chris@1458 46 // If we omit the Q_OBJECT macro, lupdate complains.
Chris@1458 47
Chris@1458 48 // If we include it, moc fails (can't handle template classes).
Chris@1458 49
Chris@1458 50 // If we omit it, lupdate still seems to emit translatable
Chris@1458 51 // messages for the tr() strings in here. So I guess we omit it.
Chris@1458 52
Chris@147 53 public:
Chris@1040 54 SparseModel(sv_samplerate_t sampleRate, int resolution,
Chris@1429 55 bool notifyOnAdd = true);
Chris@147 56 virtual ~SparseModel() { }
Chris@147 57
Chris@1580 58 bool isOK() const override { return true; }
Chris@1608 59 sv_frame_t getStartFrame() const override;
Chris@1608 60 sv_frame_t getEndFrame() const override;
Chris@1580 61 sv_samplerate_t getSampleRate() const override { return m_sampleRate; }
Chris@147 62
Chris@147 63 // Number of frames of the underlying sample rate that this model
Chris@147 64 // is capable of resolving to. For example, if m_resolution == 10
Chris@147 65 // then every point in this model will be at a multiple of 10
Chris@147 66 // sample frames and should be considered to cover a window ending
Chris@147 67 // 10 sample frames later.
Chris@929 68 virtual int getResolution() const {
Chris@147 69 return m_resolution ? m_resolution : 1;
Chris@147 70 }
Chris@929 71 virtual void setResolution(int resolution);
Chris@1466 72
Chris@1466 73 // Extend the end of the model. If this is set to something beyond
Chris@1466 74 // the end of the final point in the model, then getEndFrame()
Chris@1466 75 // will return this value. Otherwise getEndFrame() will return the
Chris@1466 76 // end of the final point. (This is used by the Tony application)
Chris@1466 77 virtual void extendEndFrame(sv_frame_t to) { m_extendTo = to; }
Chris@1466 78
Chris@147 79 typedef PointType Point;
Chris@147 80 typedef std::multiset<PointType,
Chris@1429 81 typename PointType::OrderComparator> PointList;
Chris@147 82 typedef typename PointList::iterator PointListIterator;
Chris@606 83 typedef typename PointList::const_iterator PointListConstIterator;
Chris@147 84
Chris@147 85 /**
Chris@147 86 * Return whether the model is empty or not.
Chris@147 87 */
Chris@147 88 virtual bool isEmpty() const;
Chris@147 89
Chris@147 90 /**
Chris@147 91 * Get the total number of points in the model.
Chris@147 92 */
Chris@929 93 virtual int getPointCount() const;
Chris@147 94
Chris@147 95 /**
Chris@459 96 * Get all points.
Chris@459 97 */
Chris@459 98 virtual const PointList &getPoints() const;
Chris@459 99
Chris@459 100 /**
Chris@147 101 * Get all of the points in this model between the given
Chris@147 102 * boundaries (in frames), as well as up to two points before and
Chris@147 103 * after the boundaries. If you need exact boundaries, check the
Chris@147 104 * point coordinates in the returned list.
Chris@147 105 */
Chris@1038 106 virtual PointList getPoints(sv_frame_t start, sv_frame_t end) const;
Chris@147 107
Chris@147 108 /**
Chris@147 109 * Get all points that cover the given frame number, taking the
Chris@147 110 * resolution of the model into account.
Chris@147 111 */
Chris@1038 112 virtual PointList getPoints(sv_frame_t frame) const;
Chris@147 113
Chris@147 114 /**
Chris@147 115 * Return all points that share the nearest frame number prior to
Chris@147 116 * the given one at which there are any points.
Chris@147 117 */
Chris@1038 118 virtual PointList getPreviousPoints(sv_frame_t frame) const;
Chris@147 119
Chris@147 120 /**
Chris@147 121 * Return all points that share the nearest frame number
Chris@147 122 * subsequent to the given one at which there are any points.
Chris@147 123 */
Chris@1038 124 virtual PointList getNextPoints(sv_frame_t frame) const;
Chris@147 125
Chris@147 126 /**
Chris@147 127 * Remove all points.
Chris@147 128 */
Chris@147 129 virtual void clear();
Chris@147 130
Chris@147 131 /**
Chris@147 132 * Add a point.
Chris@147 133 */
Chris@147 134 virtual void addPoint(const PointType &point);
Chris@147 135
Chris@147 136 /**
Chris@147 137 * Remove a point. Points are not necessarily unique, so this
Chris@147 138 * function will remove the first point that compares equal to the
Chris@147 139 * supplied one using Point::Comparator. Other identical points
Chris@147 140 * may remain in the model.
Chris@147 141 */
Chris@147 142 virtual void deletePoint(const PointType &point);
Chris@147 143
Chris@1113 144 /**
Chris@1113 145 * Return true if the given point is found in this model, false
Chris@1113 146 * otherwise.
Chris@1113 147 */
Chris@1113 148 virtual bool containsPoint(const PointType &point);
Chris@1113 149
Chris@1580 150 bool isReady(int *completion = 0) const override {
Chris@297 151 bool ready = isOK() && (m_completion == 100);
Chris@297 152 if (completion) *completion = m_completion;
Chris@297 153 return ready;
Chris@297 154 }
Chris@297 155
Chris@333 156 virtual void setCompletion(int completion, bool update = true);
Chris@147 157 virtual int getCompletion() const { return m_completion; }
Chris@147 158
Chris@147 159 virtual bool hasTextLabels() const { return m_hasTextLabels; }
Chris@147 160
Chris@1580 161 bool isSparse() const override { return true; }
cannam@1452 162
Chris@1580 163 QString getTypeName() const override { return tr("Sparse"); }
Chris@345 164
Chris@407 165 virtual QString getXmlOutputType() const { return "sparse"; }
Chris@407 166
Chris@1608 167 void toXml(QTextStream &out,
Chris@1608 168 QString indent = "",
Chris@1608 169 QString extraAttributes = "") const override;
Chris@147 170
Chris@1580 171 QString toDelimitedDataString(QString delimiter) const override {
Chris@1072 172 return toDelimitedDataStringWithOptions
Chris@1072 173 (delimiter, DataExportDefaults);
Chris@1060 174 }
Chris@1060 175
Chris@1580 176 QString toDelimitedDataStringWithOptions(QString delimiter,
Chris@1580 177 DataExportOptions opts) const override {
Chris@1064 178 return toDelimitedDataStringSubsetWithOptions
Chris@1064 179 (delimiter, opts,
Chris@1451 180 std::min(getStartFrame(), sv_frame_t(0)), getEndFrame());
Chris@147 181 }
Chris@147 182
Chris@1580 183 QString toDelimitedDataStringSubset(QString delimiter, sv_frame_t f0, sv_frame_t f1) const override {
Chris@1072 184 return toDelimitedDataStringSubsetWithOptions
Chris@1072 185 (delimiter, DataExportDefaults, f0, f1);
Chris@1060 186 }
Chris@1060 187
Chris@1580 188 QString toDelimitedDataStringSubsetWithOptions(QString delimiter, DataExportOptions opts, sv_frame_t f0, sv_frame_t f1) const override {
Chris@1064 189 if (opts & DataExportFillGaps) {
Chris@1064 190 return toDelimitedDataStringSubsetFilled(delimiter, opts, f0, f1);
Chris@1064 191 } else {
Chris@1064 192 QString s;
Chris@1064 193 for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@1127 194 if (i->frame >= f0 && i->frame < f1) {
Chris@1064 195 s += i->toDelimitedDataString(delimiter, opts, m_sampleRate) + "\n";
Chris@1064 196 }
Chris@838 197 }
Chris@1064 198 return s;
Chris@838 199 }
Chris@838 200 }
Chris@838 201
Chris@147 202 /**
Chris@147 203 * Command to add a point, with undo.
Chris@147 204 */
Chris@147 205 class AddPointCommand : public Command
Chris@147 206 {
Chris@147 207 public:
Chris@1429 208 AddPointCommand(SparseModel<PointType> *model,
Chris@1429 209 const PointType &point,
Chris@147 210 QString name = "") :
Chris@1429 211 m_model(model), m_point(point), m_name(name) { }
Chris@147 212
Chris@1580 213 QString getName() const override {
Chris@147 214 return (m_name == "" ? tr("Add Point") : m_name);
Chris@147 215 }
Chris@147 216
Chris@1580 217 void execute() override { m_model->addPoint(m_point); }
Chris@1580 218 void unexecute() override { m_model->deletePoint(m_point); }
Chris@147 219
Chris@1429 220 const PointType &getPoint() const { return m_point; }
Chris@147 221
Chris@147 222 private:
Chris@1429 223 SparseModel<PointType> *m_model;
Chris@1429 224 PointType m_point;
Chris@147 225 QString m_name;
Chris@147 226 };
Chris@147 227
Chris@147 228
Chris@147 229 /**
Chris@147 230 * Command to remove a point, with undo.
Chris@147 231 */
Chris@147 232 class DeletePointCommand : public Command
Chris@147 233 {
Chris@147 234 public:
Chris@1429 235 DeletePointCommand(SparseModel<PointType> *model,
Chris@1429 236 const PointType &point) :
Chris@1429 237 m_model(model), m_point(point) { }
Chris@147 238
Chris@1580 239 QString getName() const override { return tr("Delete Point"); }
Chris@147 240
Chris@1580 241 void execute() override { m_model->deletePoint(m_point); }
Chris@1580 242 void unexecute() override { m_model->addPoint(m_point); }
Chris@147 243
Chris@1429 244 const PointType &getPoint() const { return m_point; }
Chris@147 245
Chris@147 246 private:
Chris@1429 247 SparseModel<PointType> *m_model;
Chris@1429 248 PointType m_point;
Chris@147 249 };
Chris@147 250
Chris@147 251
Chris@147 252 /**
Chris@147 253 * Command to add or remove a series of points, with undo.
Chris@147 254 * Consecutive add/remove pairs for the same point are collapsed.
Chris@147 255 */
Chris@147 256 class EditCommand : public MacroCommand
Chris@147 257 {
Chris@147 258 public:
Chris@1429 259 EditCommand(SparseModel<PointType> *model, QString commandName);
Chris@147 260
Chris@1429 261 virtual void addPoint(const PointType &point);
Chris@1429 262 virtual void deletePoint(const PointType &point);
Chris@147 263
Chris@1429 264 /**
Chris@1429 265 * Stack an arbitrary other command in the same sequence.
Chris@1429 266 */
Chris@1580 267 void addCommand(Command *command) override { addCommand(command, true); }
Chris@147 268
Chris@1429 269 /**
Chris@1429 270 * If any points have been added or deleted, return this
Chris@1429 271 * command (so the caller can add it to the command history).
Chris@1429 272 * Otherwise delete the command and return NULL.
Chris@1429 273 */
Chris@1429 274 virtual EditCommand *finish();
Chris@147 275
Chris@147 276 protected:
Chris@1429 277 virtual void addCommand(Command *command, bool executeFirst);
Chris@147 278
Chris@1429 279 SparseModel<PointType> *m_model;
Chris@147 280 };
Chris@147 281
Chris@147 282
Chris@147 283 /**
Chris@147 284 * Command to relabel a point.
Chris@147 285 */
Chris@147 286 class RelabelCommand : public Command
Chris@147 287 {
Chris@147 288 public:
Chris@1429 289 RelabelCommand(SparseModel<PointType> *model,
Chris@1429 290 const PointType &point,
Chris@1429 291 QString newLabel) :
Chris@1429 292 m_model(model), m_oldPoint(point), m_newPoint(point) {
Chris@1429 293 m_newPoint.label = newLabel;
Chris@1429 294 }
Chris@147 295
Chris@1580 296 QString getName() const override { return tr("Re-Label Point"); }
Chris@147 297
Chris@1580 298 void execute() override {
Chris@1429 299 m_model->deletePoint(m_oldPoint);
Chris@1429 300 m_model->addPoint(m_newPoint);
Chris@1429 301 std::swap(m_oldPoint, m_newPoint);
Chris@1429 302 }
Chris@147 303
Chris@1580 304 void unexecute() override { execute(); }
Chris@147 305
Chris@147 306 private:
Chris@1429 307 SparseModel<PointType> *m_model;
Chris@1429 308 PointType m_oldPoint;
Chris@1429 309 PointType m_newPoint;
Chris@147 310 };
Chris@147 311
Chris@420 312 /**
Chris@420 313 * TabularModel methods.
Chris@420 314 */
Chris@420 315
Chris@1580 316 int getRowCount() const override
Chris@420 317 {
Chris@1038 318 return int(m_points.size());
Chris@420 319 }
Chris@420 320
Chris@1580 321 sv_frame_t getFrameForRow(int row) const override
Chris@420 322 {
Chris@606 323 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@420 324 if (i == m_points.end()) return 0;
Chris@420 325 return i->frame;
Chris@420 326 }
Chris@420 327
Chris@1580 328 int getRowForFrame(sv_frame_t frame) const override
Chris@420 329 {
Chris@420 330 if (m_rows.empty()) rebuildRowVector();
Chris@1038 331 std::vector<sv_frame_t>::iterator i =
Chris@420 332 std::lower_bound(m_rows.begin(), m_rows.end(), frame);
Chris@1038 333 ssize_t row = std::distance(m_rows.begin(), i);
Chris@432 334 if (i != m_rows.begin() && (i == m_rows.end() || *i != frame)) {
Chris@432 335 --row;
Chris@432 336 }
Chris@1038 337 return int(row);
Chris@420 338 }
Chris@420 339
Chris@1580 340 int getColumnCount() const override { return 1; }
Chris@1580 341 QVariant getData(int row, int column, int role) const override
Chris@425 342 {
Chris@606 343 PointListConstIterator i = getPointListIteratorForRow(row);
Chris@1254 344 if (i == m_points.end()) {
Chris@1254 345 // cerr << "no iterator for row " << row << " (have " << getRowCount() << " rows)" << endl;
Chris@1254 346 return QVariant();
Chris@1254 347 }
Chris@425 348
Chris@1254 349 // cerr << "returning data for row " << row << " col " << column << endl;
Chris@1254 350
Chris@425 351 switch (column) {
Chris@425 352 case 0: {
Chris@425 353 if (role == SortRole) return int(i->frame);
Chris@425 354 RealTime rt = RealTime::frame2RealTime(i->frame, getSampleRate());
Chris@425 355 if (role == Qt::EditRole) return rt.toString().c_str();
Chris@425 356 else return rt.toText().c_str();
Chris@425 357 }
Chris@425 358 case 1: return int(i->frame);
Chris@425 359 }
Chris@425 360
Chris@420 361 return QVariant();
Chris@420 362 }
Chris@427 363
Chris@1580 364 Command *getSetDataCommand(int row, int column,
Chris@1580 365 const QVariant &value, int role) override
Chris@425 366 {
Chris@740 367 if (role != Qt::EditRole) return 0;
Chris@425 368 PointListIterator i = getPointListIteratorForRow(row);
Chris@740 369 if (i == m_points.end()) return 0;
Chris@425 370 EditCommand *command = new EditCommand(this, tr("Edit Data"));
Chris@425 371
Chris@425 372 Point point(*i);
Chris@425 373 command->deletePoint(point);
Chris@425 374
Chris@425 375 switch (column) {
Chris@425 376 case 0: point.frame = lrint(value.toDouble() * getSampleRate()); break;
Chris@425 377 case 1: point.frame = value.toInt(); break;
Chris@425 378 }
Chris@425 379
Chris@425 380 command->addPoint(point);
Chris@425 381 return command->finish();
Chris@425 382 }
Chris@425 383
Chris@1580 384 Command *getInsertRowCommand(int row) override
Chris@427 385 {
Chris@427 386 EditCommand *command = new EditCommand(this, tr("Insert Data Point"));
Chris@427 387 Point point(0);
Chris@427 388 PointListIterator i = getPointListIteratorForRow(row);
Chris@427 389 if (i == m_points.end() && i != m_points.begin()) --i;
Chris@427 390 if (i != m_points.end()) point = *i;
Chris@427 391 command->addPoint(point);
Chris@427 392 return command->finish();
Chris@427 393 }
Chris@427 394
Chris@1580 395 Command *getRemoveRowCommand(int row) override
Chris@427 396 {
Chris@427 397 PointListIterator i = getPointListIteratorForRow(row);
Chris@427 398 if (i == m_points.end()) return 0;
Chris@978 399 EditCommand *command = new EditCommand(this, tr("Delete Data Point"));
Chris@427 400 command->deletePoint(*i);
Chris@427 401 return command->finish();
Chris@427 402 }
Chris@427 403
Chris@147 404 protected:
Chris@1040 405 sv_samplerate_t m_sampleRate;
Chris@929 406 int m_resolution;
Chris@1466 407 sv_frame_t m_extendTo;
Chris@147 408 bool m_notifyOnAdd;
Chris@1038 409 sv_frame_t m_sinceLastNotifyMin;
Chris@1038 410 sv_frame_t m_sinceLastNotifyMax;
Chris@147 411 bool m_hasTextLabels;
Chris@147 412
Chris@147 413 PointList m_points;
Chris@929 414 int m_pointCount;
Chris@147 415 mutable QMutex m_mutex;
Chris@147 416 int m_completion;
Chris@420 417
Chris@1038 418 void getPointIterators(sv_frame_t frame,
Chris@420 419 PointListIterator &startItr,
Chris@608 420 PointListIterator &endItr);
Chris@1038 421 void getPointIterators(sv_frame_t frame,
Chris@608 422 PointListConstIterator &startItr,
Chris@608 423 PointListConstIterator &endItr) const;
Chris@420 424
Chris@420 425 // This is only used if the model is called on to act in
Chris@420 426 // TabularModel mode
Chris@1038 427 mutable std::vector<sv_frame_t> m_rows; // map from row number to frame
Chris@1254 428
Chris@420 429 void rebuildRowVector() const
Chris@420 430 {
Chris@420 431 m_rows.clear();
Chris@608 432 for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@777 433 // std::cerr << "rebuildRowVector: row " << m_rows.size() << " -> " << i->frame << std::endl;
Chris@420 434 m_rows.push_back(i->frame);
Chris@420 435 }
Chris@420 436 }
Chris@420 437
Chris@608 438 PointListIterator getPointListIteratorForRow(int row)
Chris@420 439 {
Chris@420 440 if (m_rows.empty()) rebuildRowVector();
Chris@425 441 if (row < 0 || row + 1 > int(m_rows.size())) return m_points.end();
Chris@420 442
Chris@1038 443 sv_frame_t frame = m_rows[row];
Chris@420 444 int indexAtFrame = 0;
Chris@420 445 int ri = row;
Chris@420 446 while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
Chris@420 447 int initialIndexAtFrame = indexAtFrame;
Chris@420 448
Chris@420 449 PointListIterator i0, i1;
Chris@420 450 getPointIterators(frame, i0, i1);
Chris@420 451 PointListIterator i = i0;
Chris@420 452
Chris@420 453 for (i = i0; i != i1; ++i) {
Chris@778 454 if (i->frame < (int)frame) { continue; }
Chris@420 455 if (indexAtFrame > 0) { --indexAtFrame; continue; }
Chris@420 456 return i;
Chris@420 457 }
Chris@420 458
Chris@420 459 if (indexAtFrame > 0) {
Chris@420 460 std::cerr << "WARNING: SparseModel::getPointListIteratorForRow: No iterator available for row " << row << " (frame = " << frame << ", index at frame = " << initialIndexAtFrame << ", leftover index " << indexAtFrame << ")" << std::endl;
Chris@420 461 }
Chris@420 462 return i;
Chris@420 463 }
Chris@608 464
Chris@608 465 PointListConstIterator getPointListIteratorForRow(int row) const
Chris@608 466 {
Chris@608 467 if (m_rows.empty()) rebuildRowVector();
Chris@608 468 if (row < 0 || row + 1 > int(m_rows.size())) return m_points.end();
Chris@608 469
Chris@1038 470 sv_frame_t frame = m_rows[row];
Chris@608 471 int indexAtFrame = 0;
Chris@608 472 int ri = row;
Chris@608 473 while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
Chris@608 474 int initialIndexAtFrame = indexAtFrame;
Chris@608 475
Chris@1254 476 // std::cerr << "getPointListIteratorForRow " << row << ": initialIndexAtFrame = " << initialIndexAtFrame << " for frame " << frame << std::endl;
Chris@777 477
Chris@608 478 PointListConstIterator i0, i1;
Chris@608 479 getPointIterators(frame, i0, i1);
Chris@608 480 PointListConstIterator i = i0;
Chris@608 481
Chris@608 482 for (i = i0; i != i1; ++i) {
Chris@785 483 // std::cerr << "i->frame is " << i->frame << ", wanting " << frame << std::endl;
Chris@785 484
Chris@778 485 if (i->frame < (int)frame) { continue; }
Chris@608 486 if (indexAtFrame > 0) { --indexAtFrame; continue; }
Chris@608 487 return i;
Chris@608 488 }
Chris@1254 489 /*
Chris@1254 490 if (i == m_points.end()) {
Chris@1254 491 std::cerr << "returning i at end" << std::endl;
Chris@1254 492 } else {
Chris@1254 493 std::cerr << "returning i with i->frame = " << i->frame << std::endl;
Chris@1254 494 }
Chris@1254 495 */
Chris@608 496 if (indexAtFrame > 0) {
Chris@608 497 std::cerr << "WARNING: SparseModel::getPointListIteratorForRow: No iterator available for row " << row << " (frame = " << frame << ", index at frame = " << initialIndexAtFrame << ", leftover index " << indexAtFrame << ")" << std::endl;
Chris@608 498 }
Chris@608 499 return i;
Chris@608 500 }
Chris@1064 501
Chris@1064 502 QString toDelimitedDataStringSubsetFilled(QString delimiter,
Chris@1064 503 DataExportOptions opts,
Chris@1064 504 sv_frame_t f0,
Chris@1064 505 sv_frame_t f1) const {
Chris@1064 506
Chris@1064 507 QString s;
Chris@1064 508 opts &= ~DataExportFillGaps;
Chris@1064 509
Chris@1064 510 // find frame time of first point in range (if any)
Chris@1064 511 sv_frame_t first = f0;
Chris@1064 512 for (auto &p: m_points) {
Chris@1064 513 if (p.frame >= f0) {
Chris@1064 514 first = p.frame;
Chris@1064 515 break;
Chris@1064 516 }
Chris@1064 517 }
Chris@1064 518
Chris@1064 519 // project back to first frame time in range according to
Chris@1064 520 // resolution. e.g. if f0 = 2, first = 9, resolution = 4 then
Chris@1064 521 // we start at 5 (because 1 is too early and we need to arrive
Chris@1064 522 // at 9 to match the first actual point). This method is
Chris@1064 523 // stupid but easy to understand:
Chris@1064 524 sv_frame_t f = first;
Chris@1064 525 while (f >= f0 + m_resolution) f -= m_resolution;
Chris@1064 526
Chris@1064 527 // now progress, either writing the next point (if within
Chris@1064 528 // distance) or a default point
Chris@1064 529 PointListConstIterator itr = m_points.begin();
Chris@1064 530
Chris@1064 531 while (f < f1) {
Chris@1064 532 if (itr != m_points.end() && itr->frame <= f) {
Chris@1064 533 s += itr->toDelimitedDataString(delimiter, opts, m_sampleRate);
Chris@1064 534 ++itr;
Chris@1064 535 } else {
Chris@1064 536 s += Point(f).toDelimitedDataString(delimiter, opts, m_sampleRate);
Chris@1064 537 }
Chris@1064 538 s += "\n";
Chris@1064 539 f += m_resolution;
Chris@1064 540 }
Chris@1064 541
Chris@1064 542 return s;
Chris@1064 543 }
Chris@147 544 };
Chris@147 545
Chris@147 546
Chris@147 547 template <typename PointType>
Chris@1040 548 SparseModel<PointType>::SparseModel(sv_samplerate_t sampleRate,
Chris@929 549 int resolution,
Chris@147 550 bool notifyOnAdd) :
Chris@147 551 m_sampleRate(sampleRate),
Chris@147 552 m_resolution(resolution),
Chris@1466 553 m_extendTo(0),
Chris@147 554 m_notifyOnAdd(notifyOnAdd),
Chris@147 555 m_sinceLastNotifyMin(-1),
Chris@147 556 m_sinceLastNotifyMax(-1),
Chris@147 557 m_hasTextLabels(false),
Chris@147 558 m_pointCount(0),
Chris@147 559 m_completion(100)
Chris@147 560 {
Chris@147 561 }
Chris@147 562
Chris@147 563 template <typename PointType>
Chris@1038 564 sv_frame_t
Chris@147 565 SparseModel<PointType>::getStartFrame() const
Chris@147 566 {
Chris@147 567 QMutexLocker locker(&m_mutex);
Chris@1038 568 sv_frame_t f = 0;
Chris@147 569 if (!m_points.empty()) {
Chris@1429 570 f = m_points.begin()->frame;
Chris@147 571 }
Chris@147 572 return f;
Chris@147 573 }
Chris@147 574
Chris@147 575 template <typename PointType>
Chris@1038 576 sv_frame_t
Chris@147 577 SparseModel<PointType>::getEndFrame() const
Chris@147 578 {
Chris@147 579 QMutexLocker locker(&m_mutex);
Chris@1038 580 sv_frame_t f = 0;
Chris@147 581 if (!m_points.empty()) {
Chris@1429 582 PointListConstIterator i(m_points.end());
Chris@1609 583 f = (--i)->frame + getResolution();
Chris@147 584 }
Chris@1466 585 if (m_extendTo > f) {
Chris@1466 586 return m_extendTo;
Chris@1466 587 } else {
Chris@1466 588 return f;
Chris@1466 589 }
Chris@147 590 }
Chris@147 591
Chris@147 592 template <typename PointType>
Chris@147 593 bool
Chris@147 594 SparseModel<PointType>::isEmpty() const
Chris@147 595 {
Chris@147 596 return m_pointCount == 0;
Chris@147 597 }
Chris@147 598
Chris@147 599 template <typename PointType>
Chris@929 600 int
Chris@147 601 SparseModel<PointType>::getPointCount() const
Chris@147 602 {
Chris@147 603 return m_pointCount;
Chris@147 604 }
Chris@147 605
Chris@147 606 template <typename PointType>
Chris@459 607 const typename SparseModel<PointType>::PointList &
Chris@459 608 SparseModel<PointType>::getPoints() const
Chris@459 609 {
Chris@459 610 return m_points;
Chris@459 611 }
Chris@459 612
Chris@459 613 template <typename PointType>
Chris@147 614 typename SparseModel<PointType>::PointList
Chris@1038 615 SparseModel<PointType>::getPoints(sv_frame_t start, sv_frame_t end) const
Chris@147 616 {
Chris@147 617 if (start > end) return PointList();
Chris@147 618 QMutexLocker locker(&m_mutex);
Chris@147 619
Chris@147 620 PointType startPoint(start), endPoint(end);
Chris@147 621
Chris@608 622 PointListConstIterator startItr = m_points.lower_bound(startPoint);
Chris@608 623 PointListConstIterator endItr = m_points.upper_bound(endPoint);
Chris@147 624
Chris@147 625 if (startItr != m_points.begin()) --startItr;
Chris@147 626 if (startItr != m_points.begin()) --startItr;
Chris@147 627 if (endItr != m_points.end()) ++endItr;
Chris@147 628 if (endItr != m_points.end()) ++endItr;
Chris@147 629
Chris@147 630 PointList rv;
Chris@147 631
Chris@608 632 for (PointListConstIterator i = startItr; i != endItr; ++i) {
Chris@1429 633 rv.insert(*i);
Chris@147 634 }
Chris@147 635
Chris@147 636 return rv;
Chris@147 637 }
Chris@147 638
Chris@147 639 template <typename PointType>
Chris@147 640 typename SparseModel<PointType>::PointList
Chris@1038 641 SparseModel<PointType>::getPoints(sv_frame_t frame) const
Chris@147 642 {
Chris@608 643 PointListConstIterator startItr, endItr;
Chris@420 644 getPointIterators(frame, startItr, endItr);
Chris@147 645
Chris@147 646 PointList rv;
Chris@147 647
Chris@608 648 for (PointListConstIterator i = startItr; i != endItr; ++i) {
Chris@1429 649 rv.insert(*i);
Chris@147 650 }
Chris@147 651
Chris@147 652 return rv;
Chris@147 653 }
Chris@147 654
Chris@147 655 template <typename PointType>
Chris@420 656 void
Chris@1038 657 SparseModel<PointType>::getPointIterators(sv_frame_t frame,
Chris@420 658 PointListIterator &startItr,
Chris@608 659 PointListIterator &endItr)
Chris@608 660 {
Chris@608 661 QMutexLocker locker(&m_mutex);
Chris@608 662
Chris@608 663 if (m_resolution == 0) {
Chris@608 664 startItr = m_points.end();
Chris@608 665 endItr = m_points.end();
Chris@608 666 return;
Chris@608 667 }
Chris@608 668
Chris@1038 669 sv_frame_t start = (frame / m_resolution) * m_resolution;
Chris@1038 670 sv_frame_t end = start + m_resolution;
Chris@608 671
Chris@608 672 PointType startPoint(start), endPoint(end);
Chris@777 673
Chris@608 674 startItr = m_points.lower_bound(startPoint);
Chris@608 675 endItr = m_points.upper_bound(endPoint);
Chris@608 676 }
Chris@608 677
Chris@608 678 template <typename PointType>
Chris@608 679 void
Chris@1038 680 SparseModel<PointType>::getPointIterators(sv_frame_t frame,
Chris@608 681 PointListConstIterator &startItr,
Chris@608 682 PointListConstIterator &endItr) const
Chris@420 683 {
Chris@420 684 QMutexLocker locker(&m_mutex);
Chris@420 685
Chris@420 686 if (m_resolution == 0) {
Chris@785 687 // std::cerr << "getPointIterators: resolution == 0, returning end()" << std::endl;
Chris@420 688 startItr = m_points.end();
Chris@420 689 endItr = m_points.end();
Chris@420 690 return;
Chris@420 691 }
Chris@420 692
Chris@1038 693 sv_frame_t start = (frame / m_resolution) * m_resolution;
Chris@1038 694 sv_frame_t end = start + m_resolution;
Chris@420 695
Chris@420 696 PointType startPoint(start), endPoint(end);
Chris@420 697
Chris@777 698 // std::cerr << "getPointIterators: start frame " << start << ", end frame " << end << ", m_resolution " << m_resolution << std::endl;
Chris@785 699
Chris@420 700 startItr = m_points.lower_bound(startPoint);
Chris@420 701 endItr = m_points.upper_bound(endPoint);
Chris@420 702 }
Chris@420 703
Chris@420 704 template <typename PointType>
Chris@147 705 typename SparseModel<PointType>::PointList
Chris@1038 706 SparseModel<PointType>::getPreviousPoints(sv_frame_t originFrame) const
Chris@147 707 {
Chris@147 708 QMutexLocker locker(&m_mutex);
Chris@147 709
Chris@147 710 PointType lookupPoint(originFrame);
Chris@147 711 PointList rv;
Chris@147 712
Chris@608 713 PointListConstIterator i = m_points.lower_bound(lookupPoint);
Chris@147 714 if (i == m_points.begin()) return rv;
Chris@147 715
Chris@147 716 --i;
Chris@1038 717 sv_frame_t frame = i->frame;
Chris@147 718 while (i->frame == frame) {
Chris@1429 719 rv.insert(*i);
Chris@1429 720 if (i == m_points.begin()) break;
Chris@1429 721 --i;
Chris@147 722 }
Chris@147 723
Chris@147 724 return rv;
Chris@147 725 }
Chris@147 726
Chris@147 727 template <typename PointType>
Chris@147 728 typename SparseModel<PointType>::PointList
Chris@1038 729 SparseModel<PointType>::getNextPoints(sv_frame_t originFrame) const
Chris@147 730 {
Chris@147 731 QMutexLocker locker(&m_mutex);
Chris@147 732
Chris@147 733 PointType lookupPoint(originFrame);
Chris@147 734 PointList rv;
Chris@147 735
Chris@608 736 PointListConstIterator i = m_points.upper_bound(lookupPoint);
Chris@147 737 if (i == m_points.end()) return rv;
Chris@147 738
Chris@1038 739 sv_frame_t frame = i->frame;
Chris@147 740 while (i != m_points.end() && i->frame == frame) {
Chris@1429 741 rv.insert(*i);
Chris@1429 742 ++i;
Chris@147 743 }
Chris@147 744
Chris@147 745 return rv;
Chris@147 746 }
Chris@147 747
Chris@147 748 template <typename PointType>
Chris@147 749 void
Chris@929 750 SparseModel<PointType>::setResolution(int resolution)
Chris@147 751 {
Chris@147 752 {
Chris@1429 753 QMutexLocker locker(&m_mutex);
Chris@1429 754 m_resolution = resolution;
Chris@1212 755 m_rows.clear();
Chris@147 756 }
Chris@147 757 emit modelChanged();
Chris@147 758 }
Chris@147 759
Chris@147 760 template <typename PointType>
Chris@147 761 void
Chris@147 762 SparseModel<PointType>::clear()
Chris@147 763 {
Chris@147 764 {
Chris@1429 765 QMutexLocker locker(&m_mutex);
Chris@1429 766 m_points.clear();
Chris@147 767 m_pointCount = 0;
Chris@1212 768 m_rows.clear();
Chris@147 769 }
Chris@147 770 emit modelChanged();
Chris@147 771 }
Chris@147 772
Chris@147 773 template <typename PointType>
Chris@147 774 void
Chris@147 775 SparseModel<PointType>::addPoint(const PointType &point)
Chris@147 776 {
Chris@1456 777 {
Chris@1456 778 QMutexLocker locker(&m_mutex);
Chris@1212 779
Chris@1456 780 m_points.insert(point);
Chris@1456 781 m_pointCount++;
Chris@1456 782 if (point.getLabel() != "") m_hasTextLabels = true;
Chris@147 783
Chris@1456 784 // Even though this model is nominally sparse, there may still
Chris@1456 785 // be too many signals going on here (especially as they'll
Chris@1456 786 // probably be queued from one thread to another), which is
Chris@1456 787 // why we need the notifyOnAdd as an option rather than a
Chris@1456 788 // necessity (the alternative is to notify on setCompletion).
Chris@1456 789
Chris@1456 790 if (m_notifyOnAdd) {
Chris@1456 791 m_rows.clear(); //!!! inefficient
Chris@1456 792 } else {
Chris@1456 793 if (m_sinceLastNotifyMin == -1 ||
Chris@1456 794 point.frame < m_sinceLastNotifyMin) {
Chris@1456 795 m_sinceLastNotifyMin = point.frame;
Chris@1456 796 }
Chris@1456 797 if (m_sinceLastNotifyMax == -1 ||
Chris@1456 798 point.frame > m_sinceLastNotifyMax) {
Chris@1456 799 m_sinceLastNotifyMax = point.frame;
Chris@1456 800 }
Chris@1456 801 }
Chris@1456 802 }
Chris@147 803
Chris@147 804 if (m_notifyOnAdd) {
Chris@1429 805 emit modelChangedWithin(point.frame, point.frame + m_resolution);
Chris@147 806 }
Chris@147 807 }
Chris@147 808
Chris@147 809 template <typename PointType>
Chris@1113 810 bool
Chris@1113 811 SparseModel<PointType>::containsPoint(const PointType &point)
Chris@1113 812 {
Chris@1212 813 QMutexLocker locker(&m_mutex);
Chris@1113 814
Chris@1212 815 PointListIterator i = m_points.lower_bound(point);
Chris@1212 816 typename PointType::Comparator comparator;
Chris@1212 817 while (i != m_points.end()) {
Chris@1212 818 if (i->frame > point.frame) break;
Chris@1212 819 if (!comparator(*i, point) && !comparator(point, *i)) {
Chris@1212 820 return true;
Chris@1212 821 }
Chris@1212 822 ++i;
Chris@1113 823 }
Chris@1113 824
Chris@1113 825 return false;
Chris@1113 826 }
Chris@1113 827
Chris@1113 828 template <typename PointType>
Chris@147 829 void
Chris@147 830 SparseModel<PointType>::deletePoint(const PointType &point)
Chris@147 831 {
Chris@1456 832 {
Chris@1456 833 QMutexLocker locker(&m_mutex);
Chris@147 834
Chris@1456 835 PointListIterator i = m_points.lower_bound(point);
Chris@1456 836 typename PointType::Comparator comparator;
Chris@1456 837 while (i != m_points.end()) {
Chris@1456 838 if (i->frame > point.frame) break;
Chris@1456 839 if (!comparator(*i, point) && !comparator(point, *i)) {
Chris@1456 840 m_points.erase(i);
Chris@1456 841 m_pointCount--;
Chris@1456 842 break;
Chris@1429 843 }
Chris@1456 844 ++i;
Chris@1456 845 }
Chris@1212 846
Chris@147 847 // std::cout << "SparseOneDimensionalModel: emit modelChanged("
Chris@1429 848 // << point.frame << ")" << std::endl;
Chris@1456 849 m_rows.clear(); //!!! inefficient
Chris@1456 850 }
Chris@1456 851
Chris@931 852 emit modelChangedWithin(point.frame, point.frame + m_resolution);
Chris@147 853 }
Chris@147 854
Chris@147 855 template <typename PointType>
Chris@147 856 void
Chris@333 857 SparseModel<PointType>::setCompletion(int completion, bool update)
Chris@147 858 {
Chris@301 859 // std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl;
Chris@1456 860 bool emitCompletionChanged = true;
Chris@1456 861 bool emitGeneralModelChanged = false;
Chris@1456 862 bool emitRegionChanged = false;
Chris@191 863
Chris@1456 864 {
Chris@1456 865 QMutexLocker locker(&m_mutex);
Chris@1212 866
Chris@1456 867 if (m_completion != completion) {
Chris@1456 868 m_completion = completion;
Chris@147 869
Chris@1456 870 if (completion == 100) {
Chris@147 871
Chris@1456 872 if (m_notifyOnAdd) {
Chris@1456 873 emitCompletionChanged = false;
Chris@1456 874 }
Chris@1456 875
Chris@1456 876 m_notifyOnAdd = true; // henceforth
Chris@1456 877 m_rows.clear(); //!!! inefficient
Chris@1456 878 emitGeneralModelChanged = true;
Chris@1456 879
Chris@1456 880 } else if (!m_notifyOnAdd) {
Chris@1456 881
Chris@1456 882 if (update &&
Chris@1456 883 m_sinceLastNotifyMin >= 0 &&
Chris@1456 884 m_sinceLastNotifyMax >= 0) {
Chris@1456 885 m_rows.clear(); //!!! inefficient
Chris@1456 886 emitRegionChanged = true;
Chris@1456 887 }
Chris@297 888 }
Chris@1456 889 }
Chris@1456 890 }
Chris@297 891
Chris@1456 892 if (emitCompletionChanged) {
Chris@1456 893 emit completionChanged();
Chris@1456 894 }
Chris@1456 895 if (emitGeneralModelChanged) {
Chris@1456 896 emit modelChanged();
Chris@1456 897 }
Chris@1456 898 if (emitRegionChanged) {
Chris@1456 899 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
Chris@1456 900 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@147 901 }
Chris@147 902 }
Chris@147 903
Chris@147 904 template <typename PointType>
Chris@147 905 void
Chris@147 906 SparseModel<PointType>::toXml(QTextStream &out,
Chris@147 907 QString indent,
Chris@147 908 QString extraAttributes) const
Chris@147 909 {
Chris@777 910 // std::cerr << "SparseModel::toXml: extraAttributes = \""
Chris@777 911 // << extraAttributes.toStdString() << std::endl;
Chris@318 912
Chris@407 913 QString type = getXmlOutputType();
Chris@407 914
Chris@147 915 Model::toXml
Chris@1429 916 (out,
Chris@147 917 indent,
Chris@1429 918 QString("type=\"%1\" dimensions=\"%2\" resolution=\"%3\" notifyOnAdd=\"%4\" dataset=\"%5\" %6")
Chris@407 919 .arg(type)
Chris@1429 920 .arg(PointType(0).getDimensions())
Chris@1429 921 .arg(m_resolution)
Chris@1429 922 .arg(m_notifyOnAdd ? "true" : "false")
Chris@1429 923 .arg(getObjectExportId(&m_points))
Chris@1429 924 .arg(extraAttributes));
Chris@147 925
Chris@147 926 out << indent;
Chris@147 927 out << QString("<dataset id=\"%1\" dimensions=\"%2\">\n")
Chris@1429 928 .arg(getObjectExportId(&m_points))
Chris@1429 929 .arg(PointType(0).getDimensions());
Chris@147 930
Chris@608 931 for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@314 932 i->toXml(out, indent + " ");
Chris@147 933 }
Chris@147 934
Chris@147 935 out << indent;
Chris@147 936 out << "</dataset>\n";
Chris@147 937 }
Chris@147 938
Chris@147 939 template <typename PointType>
Chris@147 940 SparseModel<PointType>::EditCommand::EditCommand(SparseModel *model,
Chris@147 941 QString commandName) :
Chris@147 942 MacroCommand(commandName),
Chris@147 943 m_model(model)
Chris@147 944 {
Chris@147 945 }
Chris@147 946
Chris@147 947 template <typename PointType>
Chris@147 948 void
Chris@147 949 SparseModel<PointType>::EditCommand::addPoint(const PointType &point)
Chris@147 950 {
Chris@147 951 addCommand(new AddPointCommand(m_model, point), true);
Chris@147 952 }
Chris@147 953
Chris@147 954 template <typename PointType>
Chris@147 955 void
Chris@147 956 SparseModel<PointType>::EditCommand::deletePoint(const PointType &point)
Chris@147 957 {
Chris@147 958 addCommand(new DeletePointCommand(m_model, point), true);
Chris@147 959 }
Chris@147 960
Chris@147 961 template <typename PointType>
Chris@416 962 typename SparseModel<PointType>::EditCommand *
Chris@147 963 SparseModel<PointType>::EditCommand::finish()
Chris@147 964 {
Chris@147 965 if (!m_commands.empty()) {
Chris@387 966 return this;
Chris@147 967 } else {
Chris@147 968 delete this;
Chris@389 969 return 0;
Chris@147 970 }
Chris@147 971 }
Chris@147 972
Chris@147 973 template <typename PointType>
Chris@147 974 void
Chris@147 975 SparseModel<PointType>::EditCommand::addCommand(Command *command,
Chris@1429 976 bool executeFirst)
Chris@147 977 {
Chris@147 978 if (executeFirst) command->execute();
Chris@147 979
Chris@147 980 if (!m_commands.empty()) {
Chris@1429 981 DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command);
Chris@1429 982 if (dpc) {
Chris@1429 983 AddPointCommand *apc = dynamic_cast<AddPointCommand *>
Chris@1429 984 (m_commands[m_commands.size() - 1]);
Chris@1429 985 typename PointType::Comparator comparator;
Chris@1429 986 if (apc) {
Chris@1429 987 if (!comparator(apc->getPoint(), dpc->getPoint()) &&
Chris@1429 988 !comparator(dpc->getPoint(), apc->getPoint())) {
Chris@1429 989 deleteCommand(apc);
Chris@1429 990 return;
Chris@1429 991 }
Chris@1429 992 }
Chris@1429 993 }
Chris@147 994 }
Chris@147 995
Chris@147 996 MacroCommand::addCommand(command);
Chris@147 997 }
Chris@147 998
Chris@147 999
Chris@147 1000 #endif
Chris@147 1001
Chris@147 1002
Chris@147 1003