annotate data/model/SparseModel.h @ 1455:ec9e65fcf749

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