annotate data/model/SparseModel.h @ 1008:d9e0e59a1581

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