annotate data/model/SparseModel.h @ 1223:c2207877689d piper

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