annotate data/model/SparseModel.h @ 569:9773aadbae0c

* Handle zero-velocity note ons as well as note offs (can't believe I fell for that one) * Add Peek Left / Peek Right (alt+left/right) and change peek-drag (i.e. dragging without moving playback pointer or other panes) from ctrl+drag to alt+drag for symmetry
author Chris Cannam
date Thu, 26 Feb 2009 10:49:08 +0000
parents 6441b31b37ac
children 1415e35881f6
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@420 29
Chris@425 30 #include <cmath>
Chris@425 31
Chris@147 32 #include <QMutex>
Chris@147 33 #include <QTextStream>
Chris@147 34
Chris@147 35 /**
Chris@147 36 * Model containing sparse data (points with some properties). The
Chris@147 37 * properties depend on the point type.
Chris@147 38 */
Chris@147 39
Chris@147 40 template <typename PointType>
Chris@420 41 class SparseModel : public Model,
Chris@420 42 public TabularModel
Chris@147 43 {
Chris@147 44 public:
Chris@147 45 SparseModel(size_t sampleRate, size_t resolution,
Chris@147 46 bool notifyOnAdd = true);
Chris@147 47 virtual ~SparseModel() { }
Chris@147 48
Chris@147 49 virtual bool isOK() const { return true; }
Chris@147 50 virtual size_t getStartFrame() const;
Chris@147 51 virtual size_t getEndFrame() const;
Chris@147 52 virtual size_t getSampleRate() const { return m_sampleRate; }
Chris@147 53
Chris@147 54 virtual Model *clone() const;
Chris@147 55
Chris@147 56 // Number of frames of the underlying sample rate that this model
Chris@147 57 // is capable of resolving to. For example, if m_resolution == 10
Chris@147 58 // then every point in this model will be at a multiple of 10
Chris@147 59 // sample frames and should be considered to cover a window ending
Chris@147 60 // 10 sample frames later.
Chris@147 61 virtual size_t getResolution() const {
Chris@147 62 return m_resolution ? m_resolution : 1;
Chris@147 63 }
Chris@147 64 virtual void setResolution(size_t resolution);
Chris@147 65
Chris@147 66 typedef PointType Point;
Chris@147 67 typedef std::multiset<PointType,
Chris@147 68 typename PointType::OrderComparator> PointList;
Chris@147 69 typedef typename PointList::iterator PointListIterator;
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@147 79 virtual size_t 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@147 92 virtual PointList getPoints(long start, long 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@147 98 virtual PointList getPoints(long 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@147 104 virtual PointList getPreviousPoints(long 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@147 110 virtual PointList getNextPoints(long 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@147 149 virtual QString toDelimitedDataString(QString delimiter) const
Chris@147 150 {
Chris@147 151 QString s;
Chris@147 152 for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@147 153 s += i->toDelimitedDataString(delimiter, m_sampleRate) + "\n";
Chris@147 154 }
Chris@147 155 return s;
Chris@147 156 }
Chris@147 157
Chris@147 158 /**
Chris@147 159 * Command to add a point, with undo.
Chris@147 160 */
Chris@147 161 class AddPointCommand : public Command
Chris@147 162 {
Chris@147 163 public:
Chris@147 164 AddPointCommand(SparseModel<PointType> *model,
Chris@147 165 const PointType &point,
Chris@147 166 QString name = "") :
Chris@147 167 m_model(model), m_point(point), m_name(name) { }
Chris@147 168
Chris@147 169 virtual QString getName() const {
Chris@147 170 return (m_name == "" ? tr("Add Point") : m_name);
Chris@147 171 }
Chris@147 172
Chris@147 173 virtual void execute() { m_model->addPoint(m_point); }
Chris@147 174 virtual void unexecute() { m_model->deletePoint(m_point); }
Chris@147 175
Chris@147 176 const PointType &getPoint() const { return m_point; }
Chris@147 177
Chris@147 178 private:
Chris@147 179 SparseModel<PointType> *m_model;
Chris@147 180 PointType m_point;
Chris@147 181 QString m_name;
Chris@147 182 };
Chris@147 183
Chris@147 184
Chris@147 185 /**
Chris@147 186 * Command to remove a point, with undo.
Chris@147 187 */
Chris@147 188 class DeletePointCommand : public Command
Chris@147 189 {
Chris@147 190 public:
Chris@147 191 DeletePointCommand(SparseModel<PointType> *model,
Chris@147 192 const PointType &point) :
Chris@147 193 m_model(model), m_point(point) { }
Chris@147 194
Chris@147 195 virtual QString getName() const { return tr("Delete Point"); }
Chris@147 196
Chris@147 197 virtual void execute() { m_model->deletePoint(m_point); }
Chris@147 198 virtual void unexecute() { m_model->addPoint(m_point); }
Chris@147 199
Chris@147 200 const PointType &getPoint() const { return m_point; }
Chris@147 201
Chris@147 202 private:
Chris@147 203 SparseModel<PointType> *m_model;
Chris@147 204 PointType m_point;
Chris@147 205 };
Chris@147 206
Chris@147 207
Chris@147 208 /**
Chris@147 209 * Command to add or remove a series of points, with undo.
Chris@147 210 * Consecutive add/remove pairs for the same point are collapsed.
Chris@147 211 */
Chris@147 212 class EditCommand : public MacroCommand
Chris@147 213 {
Chris@147 214 public:
Chris@147 215 EditCommand(SparseModel<PointType> *model, QString commandName);
Chris@147 216
Chris@147 217 virtual void addPoint(const PointType &point);
Chris@147 218 virtual void deletePoint(const PointType &point);
Chris@147 219
Chris@147 220 /**
Chris@147 221 * Stack an arbitrary other command in the same sequence.
Chris@147 222 */
Chris@147 223 virtual void addCommand(Command *command) { addCommand(command, true); }
Chris@147 224
Chris@147 225 /**
Chris@387 226 * If any points have been added or deleted, return this
Chris@387 227 * command (so the caller can add it to the command history).
Chris@416 228 * Otherwise delete the command and return NULL.
Chris@147 229 */
Chris@416 230 virtual EditCommand *finish();
Chris@147 231
Chris@147 232 protected:
Chris@147 233 virtual void addCommand(Command *command, bool executeFirst);
Chris@147 234
Chris@147 235 SparseModel<PointType> *m_model;
Chris@147 236 };
Chris@147 237
Chris@147 238
Chris@147 239 /**
Chris@147 240 * Command to relabel a point.
Chris@147 241 */
Chris@147 242 class RelabelCommand : public Command
Chris@147 243 {
Chris@147 244 public:
Chris@147 245 RelabelCommand(SparseModel<PointType> *model,
Chris@147 246 const PointType &point,
Chris@147 247 QString newLabel) :
Chris@147 248 m_model(model), m_oldPoint(point), m_newPoint(point) {
Chris@147 249 m_newPoint.label = newLabel;
Chris@147 250 }
Chris@147 251
Chris@147 252 virtual QString getName() const { return tr("Re-Label Point"); }
Chris@147 253
Chris@147 254 virtual void execute() {
Chris@147 255 m_model->deletePoint(m_oldPoint);
Chris@147 256 m_model->addPoint(m_newPoint);
Chris@147 257 std::swap(m_oldPoint, m_newPoint);
Chris@147 258 }
Chris@147 259
Chris@147 260 virtual void unexecute() { execute(); }
Chris@147 261
Chris@147 262 private:
Chris@147 263 SparseModel<PointType> *m_model;
Chris@147 264 PointType m_oldPoint;
Chris@147 265 PointType m_newPoint;
Chris@147 266 };
Chris@147 267
Chris@420 268 /**
Chris@420 269 * TabularModel methods.
Chris@420 270 */
Chris@420 271
Chris@420 272 virtual int getRowCount() const
Chris@420 273 {
Chris@420 274 return m_points.size();
Chris@420 275 }
Chris@420 276
Chris@420 277 virtual long getFrameForRow(int row) const
Chris@420 278 {
Chris@420 279 PointListIterator i = getPointListIteratorForRow(row);
Chris@420 280 if (i == m_points.end()) return 0;
Chris@420 281 return i->frame;
Chris@420 282 }
Chris@420 283
Chris@420 284 virtual int getRowForFrame(long frame) const
Chris@420 285 {
Chris@420 286 if (m_rows.empty()) rebuildRowVector();
Chris@420 287 std::vector<long>::iterator i =
Chris@420 288 std::lower_bound(m_rows.begin(), m_rows.end(), frame);
Chris@432 289 int row = std::distance(m_rows.begin(), i);
Chris@432 290 if (i != m_rows.begin() && (i == m_rows.end() || *i != frame)) {
Chris@432 291 --row;
Chris@432 292 }
Chris@432 293 return row;
Chris@420 294 }
Chris@420 295
Chris@420 296 virtual int getColumnCount() const { return 1; }
Chris@425 297 virtual QVariant getData(int row, int column, int role) const
Chris@425 298 {
Chris@425 299 PointListIterator i = getPointListIteratorForRow(row);
Chris@425 300 if (i == m_points.end()) return QVariant();
Chris@425 301
Chris@425 302 switch (column) {
Chris@425 303 case 0: {
Chris@425 304 if (role == SortRole) return int(i->frame);
Chris@425 305 RealTime rt = RealTime::frame2RealTime(i->frame, getSampleRate());
Chris@425 306 if (role == Qt::EditRole) return rt.toString().c_str();
Chris@425 307 else return rt.toText().c_str();
Chris@425 308 }
Chris@425 309 case 1: return int(i->frame);
Chris@425 310 }
Chris@425 311
Chris@420 312 return QVariant();
Chris@420 313 }
Chris@427 314
Chris@425 315 virtual Command *getSetDataCommand(int row, int column,
Chris@425 316 const QVariant &value, int role)
Chris@425 317 {
Chris@425 318 if (role != Qt::EditRole) return false;
Chris@425 319 PointListIterator i = getPointListIteratorForRow(row);
Chris@425 320 if (i == m_points.end()) return false;
Chris@425 321 EditCommand *command = new EditCommand(this, tr("Edit Data"));
Chris@425 322
Chris@425 323 Point point(*i);
Chris@425 324 command->deletePoint(point);
Chris@425 325
Chris@425 326 switch (column) {
Chris@425 327 case 0: point.frame = lrint(value.toDouble() * getSampleRate()); break;
Chris@425 328 case 1: point.frame = value.toInt(); break;
Chris@425 329 }
Chris@425 330
Chris@425 331 command->addPoint(point);
Chris@425 332 return command->finish();
Chris@425 333 }
Chris@425 334
Chris@427 335 virtual Command *getInsertRowCommand(int row)
Chris@427 336 {
Chris@427 337 EditCommand *command = new EditCommand(this, tr("Insert Data Point"));
Chris@427 338 Point point(0);
Chris@427 339 PointListIterator i = getPointListIteratorForRow(row);
Chris@427 340 if (i == m_points.end() && i != m_points.begin()) --i;
Chris@427 341 if (i != m_points.end()) point = *i;
Chris@427 342 command->addPoint(point);
Chris@427 343 return command->finish();
Chris@427 344 }
Chris@427 345
Chris@427 346 virtual Command *getRemoveRowCommand(int row)
Chris@427 347 {
Chris@427 348 EditCommand *command = new EditCommand(this, tr("Delete Data Point"));
Chris@427 349 PointListIterator i = getPointListIteratorForRow(row);
Chris@427 350 if (i == m_points.end()) return 0;
Chris@427 351 command->deletePoint(*i);
Chris@427 352 return command->finish();
Chris@427 353 }
Chris@427 354
Chris@147 355 protected:
Chris@147 356 size_t m_sampleRate;
Chris@147 357 size_t m_resolution;
Chris@147 358 bool m_notifyOnAdd;
Chris@147 359 long m_sinceLastNotifyMin;
Chris@147 360 long m_sinceLastNotifyMax;
Chris@147 361 bool m_hasTextLabels;
Chris@147 362
Chris@147 363 PointList m_points;
Chris@147 364 size_t m_pointCount;
Chris@147 365 mutable QMutex m_mutex;
Chris@147 366 int m_completion;
Chris@420 367
Chris@420 368 void getPointIterators(long frame,
Chris@420 369 PointListIterator &startItr,
Chris@420 370 PointListIterator &endItr) const;
Chris@420 371
Chris@420 372 // This is only used if the model is called on to act in
Chris@420 373 // TabularModel mode
Chris@420 374 mutable std::vector<long> m_rows; // map from row number to frame
Chris@420 375 void rebuildRowVector() const
Chris@420 376 {
Chris@420 377 m_rows.clear();
Chris@420 378 for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@420 379 m_rows.push_back(i->frame);
Chris@420 380 }
Chris@420 381 }
Chris@420 382
Chris@420 383 PointListIterator getPointListIteratorForRow(int row) const
Chris@420 384 {
Chris@420 385 if (m_rows.empty()) rebuildRowVector();
Chris@425 386 if (row < 0 || row + 1 > int(m_rows.size())) return m_points.end();
Chris@420 387
Chris@420 388 size_t frame = m_rows[row];
Chris@420 389 int indexAtFrame = 0;
Chris@420 390 int ri = row;
Chris@420 391 while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
Chris@420 392 int initialIndexAtFrame = indexAtFrame;
Chris@420 393
Chris@420 394 PointListIterator i0, i1;
Chris@420 395 getPointIterators(frame, i0, i1);
Chris@420 396 PointListIterator i = i0;
Chris@420 397
Chris@420 398 for (i = i0; i != i1; ++i) {
Chris@420 399 if (indexAtFrame > 0) { --indexAtFrame; continue; }
Chris@420 400 return i;
Chris@420 401 }
Chris@420 402
Chris@420 403 if (indexAtFrame > 0) {
Chris@420 404 std::cerr << "WARNING: SparseModel::getPointListIteratorForRow: No iterator available for row " << row << " (frame = " << frame << ", index at frame = " << initialIndexAtFrame << ", leftover index " << indexAtFrame << ")" << std::endl;
Chris@420 405 }
Chris@420 406 return i;
Chris@420 407 }
Chris@147 408 };
Chris@147 409
Chris@147 410
Chris@147 411 template <typename PointType>
Chris@147 412 SparseModel<PointType>::SparseModel(size_t sampleRate,
Chris@147 413 size_t resolution,
Chris@147 414 bool notifyOnAdd) :
Chris@147 415 m_sampleRate(sampleRate),
Chris@147 416 m_resolution(resolution),
Chris@147 417 m_notifyOnAdd(notifyOnAdd),
Chris@147 418 m_sinceLastNotifyMin(-1),
Chris@147 419 m_sinceLastNotifyMax(-1),
Chris@147 420 m_hasTextLabels(false),
Chris@147 421 m_pointCount(0),
Chris@147 422 m_completion(100)
Chris@147 423 {
Chris@147 424 }
Chris@147 425
Chris@147 426 template <typename PointType>
Chris@147 427 size_t
Chris@147 428 SparseModel<PointType>::getStartFrame() const
Chris@147 429 {
Chris@147 430 QMutexLocker locker(&m_mutex);
Chris@147 431 size_t f = 0;
Chris@147 432 if (!m_points.empty()) {
Chris@147 433 f = m_points.begin()->frame;
Chris@147 434 }
Chris@147 435 return f;
Chris@147 436 }
Chris@147 437
Chris@147 438 template <typename PointType>
Chris@147 439 size_t
Chris@147 440 SparseModel<PointType>::getEndFrame() const
Chris@147 441 {
Chris@147 442 QMutexLocker locker(&m_mutex);
Chris@147 443 size_t f = 0;
Chris@147 444 if (!m_points.empty()) {
Chris@147 445 PointListIterator i(m_points.end());
Chris@147 446 f = (--i)->frame;
Chris@147 447 }
Chris@147 448 return f;
Chris@147 449 }
Chris@147 450
Chris@147 451 template <typename PointType>
Chris@147 452 Model *
Chris@147 453 SparseModel<PointType>::clone() const
Chris@147 454 {
Chris@425 455 return 0; //!!! is this ever used?
Chris@425 456 /*
Chris@147 457 SparseModel<PointType> *model =
Chris@147 458 new SparseModel<PointType>(m_sampleRate, m_resolution, m_notifyOnAdd);
Chris@147 459 model->m_points = m_points;
Chris@147 460 model->m_pointCount = m_pointCount;
Chris@147 461 return model;
Chris@425 462 */
Chris@147 463 }
Chris@147 464
Chris@147 465 template <typename PointType>
Chris@147 466 bool
Chris@147 467 SparseModel<PointType>::isEmpty() const
Chris@147 468 {
Chris@147 469 return m_pointCount == 0;
Chris@147 470 }
Chris@147 471
Chris@147 472 template <typename PointType>
Chris@147 473 size_t
Chris@147 474 SparseModel<PointType>::getPointCount() const
Chris@147 475 {
Chris@147 476 return m_pointCount;
Chris@147 477 }
Chris@147 478
Chris@147 479 template <typename PointType>
Chris@459 480 const typename SparseModel<PointType>::PointList &
Chris@459 481 SparseModel<PointType>::getPoints() const
Chris@459 482 {
Chris@459 483 return m_points;
Chris@459 484 }
Chris@459 485
Chris@459 486 template <typename PointType>
Chris@147 487 typename SparseModel<PointType>::PointList
Chris@147 488 SparseModel<PointType>::getPoints(long start, long end) const
Chris@147 489 {
Chris@147 490 if (start > end) return PointList();
Chris@147 491 QMutexLocker locker(&m_mutex);
Chris@147 492
Chris@147 493 PointType startPoint(start), endPoint(end);
Chris@147 494
Chris@147 495 PointListIterator startItr = m_points.lower_bound(startPoint);
Chris@147 496 PointListIterator endItr = m_points.upper_bound(endPoint);
Chris@147 497
Chris@147 498 if (startItr != m_points.begin()) --startItr;
Chris@147 499 if (startItr != m_points.begin()) --startItr;
Chris@147 500 if (endItr != m_points.end()) ++endItr;
Chris@147 501 if (endItr != m_points.end()) ++endItr;
Chris@147 502
Chris@147 503 PointList rv;
Chris@147 504
Chris@147 505 for (PointListIterator i = startItr; i != endItr; ++i) {
Chris@147 506 rv.insert(*i);
Chris@147 507 }
Chris@147 508
Chris@147 509 return rv;
Chris@147 510 }
Chris@147 511
Chris@147 512 template <typename PointType>
Chris@147 513 typename SparseModel<PointType>::PointList
Chris@147 514 SparseModel<PointType>::getPoints(long frame) const
Chris@147 515 {
Chris@420 516 PointListIterator startItr, endItr;
Chris@420 517 getPointIterators(frame, startItr, endItr);
Chris@147 518
Chris@147 519 PointList rv;
Chris@147 520
Chris@147 521 for (PointListIterator i = startItr; i != endItr; ++i) {
Chris@147 522 rv.insert(*i);
Chris@147 523 }
Chris@147 524
Chris@147 525 return rv;
Chris@147 526 }
Chris@147 527
Chris@147 528 template <typename PointType>
Chris@420 529 void
Chris@420 530 SparseModel<PointType>::getPointIterators(long frame,
Chris@420 531 PointListIterator &startItr,
Chris@420 532 PointListIterator &endItr) const
Chris@420 533 {
Chris@420 534 QMutexLocker locker(&m_mutex);
Chris@420 535
Chris@420 536 if (m_resolution == 0) {
Chris@420 537 startItr = m_points.end();
Chris@420 538 endItr = m_points.end();
Chris@420 539 return;
Chris@420 540 }
Chris@420 541
Chris@420 542 long start = (frame / m_resolution) * m_resolution;
Chris@420 543 long end = start + m_resolution;
Chris@420 544
Chris@420 545 PointType startPoint(start), endPoint(end);
Chris@420 546
Chris@420 547 startItr = m_points.lower_bound(startPoint);
Chris@420 548 endItr = m_points.upper_bound(endPoint);
Chris@420 549 }
Chris@420 550
Chris@420 551 template <typename PointType>
Chris@147 552 typename SparseModel<PointType>::PointList
Chris@147 553 SparseModel<PointType>::getPreviousPoints(long originFrame) const
Chris@147 554 {
Chris@147 555 QMutexLocker locker(&m_mutex);
Chris@147 556
Chris@147 557 PointType lookupPoint(originFrame);
Chris@147 558 PointList rv;
Chris@147 559
Chris@147 560 PointListIterator i = m_points.lower_bound(lookupPoint);
Chris@147 561 if (i == m_points.begin()) return rv;
Chris@147 562
Chris@147 563 --i;
Chris@147 564 long frame = i->frame;
Chris@147 565 while (i->frame == frame) {
Chris@147 566 rv.insert(*i);
Chris@147 567 if (i == m_points.begin()) break;
Chris@147 568 --i;
Chris@147 569 }
Chris@147 570
Chris@147 571 return rv;
Chris@147 572 }
Chris@147 573
Chris@147 574 template <typename PointType>
Chris@147 575 typename SparseModel<PointType>::PointList
Chris@147 576 SparseModel<PointType>::getNextPoints(long originFrame) const
Chris@147 577 {
Chris@147 578 QMutexLocker locker(&m_mutex);
Chris@147 579
Chris@147 580 PointType lookupPoint(originFrame);
Chris@147 581 PointList rv;
Chris@147 582
Chris@147 583 PointListIterator i = m_points.upper_bound(lookupPoint);
Chris@147 584 if (i == m_points.end()) return rv;
Chris@147 585
Chris@147 586 long frame = i->frame;
Chris@147 587 while (i != m_points.end() && i->frame == frame) {
Chris@147 588 rv.insert(*i);
Chris@147 589 ++i;
Chris@147 590 }
Chris@147 591
Chris@147 592 return rv;
Chris@147 593 }
Chris@147 594
Chris@147 595 template <typename PointType>
Chris@147 596 void
Chris@147 597 SparseModel<PointType>::setResolution(size_t resolution)
Chris@147 598 {
Chris@147 599 {
Chris@147 600 QMutexLocker locker(&m_mutex);
Chris@147 601 m_resolution = resolution;
Chris@147 602 }
Chris@420 603 m_rows.clear();
Chris@147 604 emit modelChanged();
Chris@147 605 }
Chris@147 606
Chris@147 607 template <typename PointType>
Chris@147 608 void
Chris@147 609 SparseModel<PointType>::clear()
Chris@147 610 {
Chris@147 611 {
Chris@147 612 QMutexLocker locker(&m_mutex);
Chris@147 613 m_points.clear();
Chris@147 614 m_pointCount = 0;
Chris@147 615 }
Chris@420 616 m_rows.clear();
Chris@147 617 emit modelChanged();
Chris@147 618 }
Chris@147 619
Chris@147 620 template <typename PointType>
Chris@147 621 void
Chris@147 622 SparseModel<PointType>::addPoint(const PointType &point)
Chris@147 623 {
Chris@147 624 {
Chris@147 625 QMutexLocker locker(&m_mutex);
Chris@147 626 m_points.insert(point);
Chris@147 627 m_pointCount++;
Chris@338 628 if (point.getLabel() != "") m_hasTextLabels = true;
Chris@147 629 }
Chris@147 630
Chris@147 631 // Even though this model is nominally sparse, there may still be
Chris@147 632 // too many signals going on here (especially as they'll probably
Chris@147 633 // be queued from one thread to another), which is why we need the
Chris@147 634 // notifyOnAdd as an option rather than a necessity (the
Chris@147 635 // alternative is to notify on setCompletion).
Chris@147 636
Chris@147 637 if (m_notifyOnAdd) {
Chris@420 638 m_rows.clear(); //!!! inefficient
Chris@147 639 emit modelChanged(point.frame, point.frame + m_resolution);
Chris@147 640 } else {
Chris@147 641 if (m_sinceLastNotifyMin == -1 ||
Chris@147 642 point.frame < m_sinceLastNotifyMin) {
Chris@147 643 m_sinceLastNotifyMin = point.frame;
Chris@147 644 }
Chris@147 645 if (m_sinceLastNotifyMax == -1 ||
Chris@147 646 point.frame > m_sinceLastNotifyMax) {
Chris@147 647 m_sinceLastNotifyMax = point.frame;
Chris@147 648 }
Chris@147 649 }
Chris@147 650 }
Chris@147 651
Chris@147 652 template <typename PointType>
Chris@147 653 void
Chris@147 654 SparseModel<PointType>::deletePoint(const PointType &point)
Chris@147 655 {
Chris@147 656 {
Chris@147 657 QMutexLocker locker(&m_mutex);
Chris@147 658
Chris@147 659 PointListIterator i = m_points.lower_bound(point);
Chris@147 660 typename PointType::Comparator comparator;
Chris@147 661 while (i != m_points.end()) {
Chris@147 662 if (i->frame > point.frame) break;
Chris@147 663 if (!comparator(*i, point) && !comparator(point, *i)) {
Chris@147 664 m_points.erase(i);
Chris@147 665 m_pointCount--;
Chris@147 666 break;
Chris@147 667 }
Chris@147 668 ++i;
Chris@147 669 }
Chris@147 670 }
Chris@147 671 // std::cout << "SparseOneDimensionalModel: emit modelChanged("
Chris@147 672 // << point.frame << ")" << std::endl;
Chris@420 673 m_rows.clear(); //!!! inefficient
Chris@147 674 emit modelChanged(point.frame, point.frame + m_resolution);
Chris@147 675 }
Chris@147 676
Chris@147 677 template <typename PointType>
Chris@147 678 void
Chris@333 679 SparseModel<PointType>::setCompletion(int completion, bool update)
Chris@147 680 {
Chris@301 681 // std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl;
Chris@191 682
Chris@147 683 if (m_completion != completion) {
Chris@147 684 m_completion = completion;
Chris@147 685
Chris@147 686 if (completion == 100) {
Chris@147 687
Chris@297 688 if (!m_notifyOnAdd) {
Chris@297 689 emit completionChanged();
Chris@297 690 }
Chris@297 691
Chris@147 692 m_notifyOnAdd = true; // henceforth
Chris@420 693 m_rows.clear(); //!!! inefficient
Chris@147 694 emit modelChanged();
Chris@147 695
Chris@147 696 } else if (!m_notifyOnAdd) {
Chris@147 697
Chris@333 698 if (update &&
Chris@333 699 m_sinceLastNotifyMin >= 0 &&
Chris@147 700 m_sinceLastNotifyMax >= 0) {
Chris@420 701 m_rows.clear(); //!!! inefficient
Chris@147 702 emit modelChanged(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
Chris@147 703 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@147 704 } else {
Chris@147 705 emit completionChanged();
Chris@147 706 }
Chris@147 707 } else {
Chris@147 708 emit completionChanged();
Chris@147 709 }
Chris@147 710 }
Chris@147 711 }
Chris@147 712
Chris@147 713 template <typename PointType>
Chris@147 714 void
Chris@147 715 SparseModel<PointType>::toXml(QTextStream &out,
Chris@147 716 QString indent,
Chris@147 717 QString extraAttributes) const
Chris@147 718 {
Chris@318 719 std::cerr << "SparseModel::toXml: extraAttributes = \""
Chris@318 720 << extraAttributes.toStdString() << std::endl;
Chris@318 721
Chris@407 722 QString type = getXmlOutputType();
Chris@407 723
Chris@147 724 Model::toXml
Chris@147 725 (out,
Chris@147 726 indent,
Chris@407 727 QString("type=\"%1\" dimensions=\"%2\" resolution=\"%3\" notifyOnAdd=\"%4\" dataset=\"%5\" %6")
Chris@407 728 .arg(type)
Chris@147 729 .arg(PointType(0).getDimensions())
Chris@147 730 .arg(m_resolution)
Chris@147 731 .arg(m_notifyOnAdd ? "true" : "false")
Chris@147 732 .arg(getObjectExportId(&m_points))
Chris@147 733 .arg(extraAttributes));
Chris@147 734
Chris@147 735 out << indent;
Chris@147 736 out << QString("<dataset id=\"%1\" dimensions=\"%2\">\n")
Chris@147 737 .arg(getObjectExportId(&m_points))
Chris@147 738 .arg(PointType(0).getDimensions());
Chris@147 739
Chris@147 740 for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@314 741 i->toXml(out, indent + " ");
Chris@147 742 }
Chris@147 743
Chris@147 744 out << indent;
Chris@147 745 out << "</dataset>\n";
Chris@147 746 }
Chris@147 747
Chris@147 748 template <typename PointType>
Chris@147 749 SparseModel<PointType>::EditCommand::EditCommand(SparseModel *model,
Chris@147 750 QString commandName) :
Chris@147 751 MacroCommand(commandName),
Chris@147 752 m_model(model)
Chris@147 753 {
Chris@147 754 }
Chris@147 755
Chris@147 756 template <typename PointType>
Chris@147 757 void
Chris@147 758 SparseModel<PointType>::EditCommand::addPoint(const PointType &point)
Chris@147 759 {
Chris@147 760 addCommand(new AddPointCommand(m_model, point), true);
Chris@147 761 }
Chris@147 762
Chris@147 763 template <typename PointType>
Chris@147 764 void
Chris@147 765 SparseModel<PointType>::EditCommand::deletePoint(const PointType &point)
Chris@147 766 {
Chris@147 767 addCommand(new DeletePointCommand(m_model, point), true);
Chris@147 768 }
Chris@147 769
Chris@147 770 template <typename PointType>
Chris@416 771 typename SparseModel<PointType>::EditCommand *
Chris@147 772 SparseModel<PointType>::EditCommand::finish()
Chris@147 773 {
Chris@147 774 if (!m_commands.empty()) {
Chris@387 775 return this;
Chris@147 776 } else {
Chris@147 777 delete this;
Chris@389 778 return 0;
Chris@147 779 }
Chris@147 780 }
Chris@147 781
Chris@147 782 template <typename PointType>
Chris@147 783 void
Chris@147 784 SparseModel<PointType>::EditCommand::addCommand(Command *command,
Chris@147 785 bool executeFirst)
Chris@147 786 {
Chris@147 787 if (executeFirst) command->execute();
Chris@147 788
Chris@147 789 if (!m_commands.empty()) {
Chris@147 790 DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command);
Chris@147 791 if (dpc) {
Chris@147 792 AddPointCommand *apc = dynamic_cast<AddPointCommand *>
Chris@147 793 (m_commands[m_commands.size() - 1]);
Chris@147 794 typename PointType::Comparator comparator;
Chris@147 795 if (apc) {
Chris@147 796 if (!comparator(apc->getPoint(), dpc->getPoint()) &&
Chris@147 797 !comparator(dpc->getPoint(), apc->getPoint())) {
Chris@147 798 deleteCommand(apc);
Chris@147 799 return;
Chris@147 800 }
Chris@147 801 }
Chris@147 802 }
Chris@147 803 }
Chris@147 804
Chris@147 805 MacroCommand::addCommand(command);
Chris@147 806 }
Chris@147 807
Chris@147 808
Chris@147 809 #endif
Chris@147 810
Chris@147 811
Chris@147 812