annotate data/model/SparseModel.h @ 1456:904e031c9c76

Fix hangs due to nested mutex lockers (as a result of emitting signals from within a locked section)
author Chris Cannam
date Tue, 24 Apr 2018 10:01:34 +0100
parents 6e9615bde1f9
children 0fb5d4e6edeb
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@1456 758 {
Chris@1456 759 QMutexLocker locker(&m_mutex);
Chris@1212 760
Chris@1456 761 m_points.insert(point);
Chris@1456 762 m_pointCount++;
Chris@1456 763 if (point.getLabel() != "") m_hasTextLabels = true;
Chris@147 764
Chris@1456 765 // Even though this model is nominally sparse, there may still
Chris@1456 766 // be too many signals going on here (especially as they'll
Chris@1456 767 // probably be queued from one thread to another), which is
Chris@1456 768 // why we need the notifyOnAdd as an option rather than a
Chris@1456 769 // necessity (the alternative is to notify on setCompletion).
Chris@1456 770
Chris@1456 771 if (m_notifyOnAdd) {
Chris@1456 772 m_rows.clear(); //!!! inefficient
Chris@1456 773 } else {
Chris@1456 774 if (m_sinceLastNotifyMin == -1 ||
Chris@1456 775 point.frame < m_sinceLastNotifyMin) {
Chris@1456 776 m_sinceLastNotifyMin = point.frame;
Chris@1456 777 }
Chris@1456 778 if (m_sinceLastNotifyMax == -1 ||
Chris@1456 779 point.frame > m_sinceLastNotifyMax) {
Chris@1456 780 m_sinceLastNotifyMax = point.frame;
Chris@1456 781 }
Chris@1456 782 }
Chris@1456 783 }
Chris@147 784
Chris@147 785 if (m_notifyOnAdd) {
Chris@1429 786 emit modelChangedWithin(point.frame, point.frame + m_resolution);
Chris@147 787 }
Chris@147 788 }
Chris@147 789
Chris@147 790 template <typename PointType>
Chris@1113 791 bool
Chris@1113 792 SparseModel<PointType>::containsPoint(const PointType &point)
Chris@1113 793 {
Chris@1212 794 QMutexLocker locker(&m_mutex);
Chris@1113 795
Chris@1212 796 PointListIterator i = m_points.lower_bound(point);
Chris@1212 797 typename PointType::Comparator comparator;
Chris@1212 798 while (i != m_points.end()) {
Chris@1212 799 if (i->frame > point.frame) break;
Chris@1212 800 if (!comparator(*i, point) && !comparator(point, *i)) {
Chris@1212 801 return true;
Chris@1212 802 }
Chris@1212 803 ++i;
Chris@1113 804 }
Chris@1113 805
Chris@1113 806 return false;
Chris@1113 807 }
Chris@1113 808
Chris@1113 809 template <typename PointType>
Chris@147 810 void
Chris@147 811 SparseModel<PointType>::deletePoint(const PointType &point)
Chris@147 812 {
Chris@1456 813 {
Chris@1456 814 QMutexLocker locker(&m_mutex);
Chris@147 815
Chris@1456 816 PointListIterator i = m_points.lower_bound(point);
Chris@1456 817 typename PointType::Comparator comparator;
Chris@1456 818 while (i != m_points.end()) {
Chris@1456 819 if (i->frame > point.frame) break;
Chris@1456 820 if (!comparator(*i, point) && !comparator(point, *i)) {
Chris@1456 821 m_points.erase(i);
Chris@1456 822 m_pointCount--;
Chris@1456 823 break;
Chris@1429 824 }
Chris@1456 825 ++i;
Chris@1456 826 }
Chris@1212 827
Chris@147 828 // std::cout << "SparseOneDimensionalModel: emit modelChanged("
Chris@1429 829 // << point.frame << ")" << std::endl;
Chris@1456 830 m_rows.clear(); //!!! inefficient
Chris@1456 831 }
Chris@1456 832
Chris@931 833 emit modelChangedWithin(point.frame, point.frame + m_resolution);
Chris@147 834 }
Chris@147 835
Chris@147 836 template <typename PointType>
Chris@147 837 void
Chris@333 838 SparseModel<PointType>::setCompletion(int completion, bool update)
Chris@147 839 {
Chris@301 840 // std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl;
Chris@1456 841 bool emitCompletionChanged = true;
Chris@1456 842 bool emitGeneralModelChanged = false;
Chris@1456 843 bool emitRegionChanged = false;
Chris@191 844
Chris@1456 845 {
Chris@1456 846 QMutexLocker locker(&m_mutex);
Chris@1212 847
Chris@1456 848 if (m_completion != completion) {
Chris@1456 849 m_completion = completion;
Chris@147 850
Chris@1456 851 if (completion == 100) {
Chris@147 852
Chris@1456 853 if (m_notifyOnAdd) {
Chris@1456 854 emitCompletionChanged = false;
Chris@1456 855 }
Chris@1456 856
Chris@1456 857 m_notifyOnAdd = true; // henceforth
Chris@1456 858 m_rows.clear(); //!!! inefficient
Chris@1456 859 emitGeneralModelChanged = true;
Chris@1456 860
Chris@1456 861 } else if (!m_notifyOnAdd) {
Chris@1456 862
Chris@1456 863 if (update &&
Chris@1456 864 m_sinceLastNotifyMin >= 0 &&
Chris@1456 865 m_sinceLastNotifyMax >= 0) {
Chris@1456 866 m_rows.clear(); //!!! inefficient
Chris@1456 867 emitRegionChanged = true;
Chris@1456 868 }
Chris@297 869 }
Chris@1456 870 }
Chris@1456 871 }
Chris@297 872
Chris@1456 873 if (emitCompletionChanged) {
Chris@1456 874 emit completionChanged();
Chris@1456 875 }
Chris@1456 876 if (emitGeneralModelChanged) {
Chris@1456 877 emit modelChanged();
Chris@1456 878 }
Chris@1456 879 if (emitRegionChanged) {
Chris@1456 880 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
Chris@1456 881 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@147 882 }
Chris@147 883 }
Chris@147 884
Chris@147 885 template <typename PointType>
Chris@147 886 void
Chris@147 887 SparseModel<PointType>::toXml(QTextStream &out,
Chris@147 888 QString indent,
Chris@147 889 QString extraAttributes) const
Chris@147 890 {
Chris@777 891 // std::cerr << "SparseModel::toXml: extraAttributes = \""
Chris@777 892 // << extraAttributes.toStdString() << std::endl;
Chris@318 893
Chris@407 894 QString type = getXmlOutputType();
Chris@407 895
Chris@147 896 Model::toXml
Chris@1429 897 (out,
Chris@147 898 indent,
Chris@1429 899 QString("type=\"%1\" dimensions=\"%2\" resolution=\"%3\" notifyOnAdd=\"%4\" dataset=\"%5\" %6")
Chris@407 900 .arg(type)
Chris@1429 901 .arg(PointType(0).getDimensions())
Chris@1429 902 .arg(m_resolution)
Chris@1429 903 .arg(m_notifyOnAdd ? "true" : "false")
Chris@1429 904 .arg(getObjectExportId(&m_points))
Chris@1429 905 .arg(extraAttributes));
Chris@147 906
Chris@147 907 out << indent;
Chris@147 908 out << QString("<dataset id=\"%1\" dimensions=\"%2\">\n")
Chris@1429 909 .arg(getObjectExportId(&m_points))
Chris@1429 910 .arg(PointType(0).getDimensions());
Chris@147 911
Chris@608 912 for (PointListConstIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@314 913 i->toXml(out, indent + " ");
Chris@147 914 }
Chris@147 915
Chris@147 916 out << indent;
Chris@147 917 out << "</dataset>\n";
Chris@147 918 }
Chris@147 919
Chris@147 920 template <typename PointType>
Chris@147 921 SparseModel<PointType>::EditCommand::EditCommand(SparseModel *model,
Chris@147 922 QString commandName) :
Chris@147 923 MacroCommand(commandName),
Chris@147 924 m_model(model)
Chris@147 925 {
Chris@147 926 }
Chris@147 927
Chris@147 928 template <typename PointType>
Chris@147 929 void
Chris@147 930 SparseModel<PointType>::EditCommand::addPoint(const PointType &point)
Chris@147 931 {
Chris@147 932 addCommand(new AddPointCommand(m_model, point), true);
Chris@147 933 }
Chris@147 934
Chris@147 935 template <typename PointType>
Chris@147 936 void
Chris@147 937 SparseModel<PointType>::EditCommand::deletePoint(const PointType &point)
Chris@147 938 {
Chris@147 939 addCommand(new DeletePointCommand(m_model, point), true);
Chris@147 940 }
Chris@147 941
Chris@147 942 template <typename PointType>
Chris@416 943 typename SparseModel<PointType>::EditCommand *
Chris@147 944 SparseModel<PointType>::EditCommand::finish()
Chris@147 945 {
Chris@147 946 if (!m_commands.empty()) {
Chris@387 947 return this;
Chris@147 948 } else {
Chris@147 949 delete this;
Chris@389 950 return 0;
Chris@147 951 }
Chris@147 952 }
Chris@147 953
Chris@147 954 template <typename PointType>
Chris@147 955 void
Chris@147 956 SparseModel<PointType>::EditCommand::addCommand(Command *command,
Chris@1429 957 bool executeFirst)
Chris@147 958 {
Chris@147 959 if (executeFirst) command->execute();
Chris@147 960
Chris@147 961 if (!m_commands.empty()) {
Chris@1429 962 DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command);
Chris@1429 963 if (dpc) {
Chris@1429 964 AddPointCommand *apc = dynamic_cast<AddPointCommand *>
Chris@1429 965 (m_commands[m_commands.size() - 1]);
Chris@1429 966 typename PointType::Comparator comparator;
Chris@1429 967 if (apc) {
Chris@1429 968 if (!comparator(apc->getPoint(), dpc->getPoint()) &&
Chris@1429 969 !comparator(dpc->getPoint(), apc->getPoint())) {
Chris@1429 970 deleteCommand(apc);
Chris@1429 971 return;
Chris@1429 972 }
Chris@1429 973 }
Chris@1429 974 }
Chris@147 975 }
Chris@147 976
Chris@147 977 MacroCommand::addCommand(command);
Chris@147 978 }
Chris@147 979
Chris@147 980
Chris@147 981 #endif
Chris@147 982
Chris@147 983
Chris@147 984