annotate data/model/SparseModel.h @ 1152:d73b4bac2dc4 3.0-integration

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