annotate data/model/SparseModel.h @ 968:6b931eeba385

clarify
author Chris Cannam
date Wed, 03 Sep 2014 11:04:41 +0100
parents df82da55e86a
children 9f526ddc6165
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 EditCommand *command = new EditCommand(this, tr("Delete Data Point"));
Chris@427 367 PointListIterator i = getPointListIteratorForRow(row);
Chris@427 368 if (i == m_points.end()) return 0;
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