annotate data/model/SparseModel.h @ 1459:3a128665fa6f horizontal-scale

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