annotate data/model/SparseModel.h @ 631:3a5ee4b6c9ad

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