annotate data/model/SparseModel.h @ 1612:23a29e5dc0e9 single-point

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