annotate data/model/SparseModel.h @ 1065:0fd3661bcfff tonioni

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