annotate data/model/SparseModel.h @ 300:5877d68815c7

* Change WaveFileModel API from getValues(start,end) to getData(start,count). It's much less error-prone to pass in frame counts instead of start/end locations. Should have done this ages ago. This closes #1794563. * Add option to apply a transform to only the selection region, instead of the whole audio. * (to make the above work properly) Add start frame offset to wave models
author Chris Cannam
date Mon, 01 Oct 2007 13:48:38 +0000
parents c022976d18e8
children 73537d900d4b
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@147 20 #include "base/Command.h"
Chris@147 21 #include "base/CommandHistory.h"
Chris@147 22
Chris@147 23 #include <iostream>
Chris@147 24
Chris@147 25 #include <set>
Chris@147 26 #include <QMutex>
Chris@147 27 #include <QTextStream>
Chris@147 28
Chris@147 29
Chris@147 30 /**
Chris@147 31 * Model containing sparse data (points with some properties). The
Chris@147 32 * properties depend on the point type.
Chris@147 33 */
Chris@147 34
Chris@147 35 template <typename PointType>
Chris@147 36 class SparseModel : public Model
Chris@147 37 {
Chris@147 38 public:
Chris@147 39 SparseModel(size_t sampleRate, size_t resolution,
Chris@147 40 bool notifyOnAdd = true);
Chris@147 41 virtual ~SparseModel() { }
Chris@147 42
Chris@147 43 virtual bool isOK() const { return true; }
Chris@147 44 virtual size_t getStartFrame() const;
Chris@147 45 virtual size_t getEndFrame() const;
Chris@147 46 virtual size_t getSampleRate() const { return m_sampleRate; }
Chris@147 47
Chris@147 48 virtual Model *clone() const;
Chris@147 49
Chris@147 50 // Number of frames of the underlying sample rate that this model
Chris@147 51 // is capable of resolving to. For example, if m_resolution == 10
Chris@147 52 // then every point in this model will be at a multiple of 10
Chris@147 53 // sample frames and should be considered to cover a window ending
Chris@147 54 // 10 sample frames later.
Chris@147 55 virtual size_t getResolution() const {
Chris@147 56 return m_resolution ? m_resolution : 1;
Chris@147 57 }
Chris@147 58 virtual void setResolution(size_t resolution);
Chris@147 59
Chris@147 60 typedef PointType Point;
Chris@147 61 typedef std::multiset<PointType,
Chris@147 62 typename PointType::OrderComparator> PointList;
Chris@147 63 typedef typename PointList::iterator PointListIterator;
Chris@147 64
Chris@147 65 /**
Chris@147 66 * Return whether the model is empty or not.
Chris@147 67 */
Chris@147 68 virtual bool isEmpty() const;
Chris@147 69
Chris@147 70 /**
Chris@147 71 * Get the total number of points in the model.
Chris@147 72 */
Chris@147 73 virtual size_t getPointCount() const;
Chris@147 74
Chris@147 75 /**
Chris@147 76 * Get all of the points in this model between the given
Chris@147 77 * boundaries (in frames), as well as up to two points before and
Chris@147 78 * after the boundaries. If you need exact boundaries, check the
Chris@147 79 * point coordinates in the returned list.
Chris@147 80 */
Chris@147 81 virtual PointList getPoints(long start, long end) const;
Chris@147 82
Chris@147 83 /**
Chris@147 84 * Get all points that cover the given frame number, taking the
Chris@147 85 * resolution of the model into account.
Chris@147 86 */
Chris@147 87 virtual PointList getPoints(long frame) const;
Chris@147 88
Chris@147 89 /**
Chris@297 90 * Get all points.
Chris@297 91 */
Chris@297 92 virtual const PointList &getPoints() const { return m_points; }
Chris@297 93
Chris@297 94 /**
Chris@147 95 * Return all points that share the nearest frame number prior to
Chris@147 96 * the given one at which there are any points.
Chris@147 97 */
Chris@147 98 virtual PointList getPreviousPoints(long frame) const;
Chris@147 99
Chris@147 100 /**
Chris@147 101 * Return all points that share the nearest frame number
Chris@147 102 * subsequent to the given one at which there are any points.
Chris@147 103 */
Chris@147 104 virtual PointList getNextPoints(long frame) const;
Chris@147 105
Chris@147 106 /**
Chris@147 107 * Remove all points.
Chris@147 108 */
Chris@147 109 virtual void clear();
Chris@147 110
Chris@147 111 /**
Chris@147 112 * Add a point.
Chris@147 113 */
Chris@147 114 virtual void addPoint(const PointType &point);
Chris@147 115
Chris@147 116 /**
Chris@147 117 * Remove a point. Points are not necessarily unique, so this
Chris@147 118 * function will remove the first point that compares equal to the
Chris@147 119 * supplied one using Point::Comparator. Other identical points
Chris@147 120 * may remain in the model.
Chris@147 121 */
Chris@147 122 virtual void deletePoint(const PointType &point);
Chris@147 123
Chris@297 124 virtual bool isReady(int *completion = 0) const {
Chris@297 125 bool ready = isOK() && (m_completion == 100);
Chris@297 126 if (completion) *completion = m_completion;
Chris@297 127 return ready;
Chris@297 128 }
Chris@297 129
Chris@147 130 virtual void setCompletion(int completion);
Chris@147 131 virtual int getCompletion() const { return m_completion; }
Chris@147 132
Chris@147 133 virtual bool hasTextLabels() const { return m_hasTextLabels; }
Chris@147 134
Chris@147 135 virtual void toXml(QTextStream &out,
Chris@147 136 QString indent = "",
Chris@147 137 QString extraAttributes = "") const;
Chris@147 138
Chris@147 139 virtual QString toXmlString(QString indent = "",
Chris@147 140 QString extraAttributes = "") const;
Chris@147 141
Chris@147 142 virtual QString toDelimitedDataString(QString delimiter) const
Chris@147 143 {
Chris@147 144 QString s;
Chris@147 145 for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@147 146 s += i->toDelimitedDataString(delimiter, m_sampleRate) + "\n";
Chris@147 147 }
Chris@147 148 return s;
Chris@147 149 }
Chris@147 150
Chris@147 151 /**
Chris@147 152 * Command to add a point, with undo.
Chris@147 153 */
Chris@147 154 class AddPointCommand : public Command
Chris@147 155 {
Chris@147 156 public:
Chris@147 157 AddPointCommand(SparseModel<PointType> *model,
Chris@147 158 const PointType &point,
Chris@147 159 QString name = "") :
Chris@147 160 m_model(model), m_point(point), m_name(name) { }
Chris@147 161
Chris@147 162 virtual QString getName() const {
Chris@147 163 return (m_name == "" ? tr("Add Point") : m_name);
Chris@147 164 }
Chris@147 165
Chris@147 166 virtual void execute() { m_model->addPoint(m_point); }
Chris@147 167 virtual void unexecute() { m_model->deletePoint(m_point); }
Chris@147 168
Chris@147 169 const PointType &getPoint() const { return m_point; }
Chris@147 170
Chris@147 171 private:
Chris@147 172 SparseModel<PointType> *m_model;
Chris@147 173 PointType m_point;
Chris@147 174 QString m_name;
Chris@147 175 };
Chris@147 176
Chris@147 177
Chris@147 178 /**
Chris@147 179 * Command to remove a point, with undo.
Chris@147 180 */
Chris@147 181 class DeletePointCommand : public Command
Chris@147 182 {
Chris@147 183 public:
Chris@147 184 DeletePointCommand(SparseModel<PointType> *model,
Chris@147 185 const PointType &point) :
Chris@147 186 m_model(model), m_point(point) { }
Chris@147 187
Chris@147 188 virtual QString getName() const { return tr("Delete Point"); }
Chris@147 189
Chris@147 190 virtual void execute() { m_model->deletePoint(m_point); }
Chris@147 191 virtual void unexecute() { m_model->addPoint(m_point); }
Chris@147 192
Chris@147 193 const PointType &getPoint() const { return m_point; }
Chris@147 194
Chris@147 195 private:
Chris@147 196 SparseModel<PointType> *m_model;
Chris@147 197 PointType m_point;
Chris@147 198 };
Chris@147 199
Chris@147 200
Chris@147 201 /**
Chris@147 202 * Command to add or remove a series of points, with undo.
Chris@147 203 * Consecutive add/remove pairs for the same point are collapsed.
Chris@147 204 */
Chris@147 205 class EditCommand : public MacroCommand
Chris@147 206 {
Chris@147 207 public:
Chris@147 208 EditCommand(SparseModel<PointType> *model, QString commandName);
Chris@147 209
Chris@147 210 virtual void addPoint(const PointType &point);
Chris@147 211 virtual void deletePoint(const PointType &point);
Chris@147 212
Chris@147 213 /**
Chris@147 214 * Stack an arbitrary other command in the same sequence.
Chris@147 215 */
Chris@147 216 virtual void addCommand(Command *command) { addCommand(command, true); }
Chris@147 217
Chris@147 218 /**
Chris@147 219 * If any points have been added or deleted, add this command
Chris@147 220 * to the command history. Otherwise delete the command.
Chris@147 221 */
Chris@147 222 virtual void finish();
Chris@147 223
Chris@147 224 protected:
Chris@147 225 virtual void addCommand(Command *command, bool executeFirst);
Chris@147 226
Chris@147 227 SparseModel<PointType> *m_model;
Chris@147 228 };
Chris@147 229
Chris@147 230
Chris@147 231 /**
Chris@147 232 * Command to relabel a point.
Chris@147 233 */
Chris@147 234 class RelabelCommand : public Command
Chris@147 235 {
Chris@147 236 public:
Chris@147 237 RelabelCommand(SparseModel<PointType> *model,
Chris@147 238 const PointType &point,
Chris@147 239 QString newLabel) :
Chris@147 240 m_model(model), m_oldPoint(point), m_newPoint(point) {
Chris@147 241 m_newPoint.label = newLabel;
Chris@147 242 }
Chris@147 243
Chris@147 244 virtual QString getName() const { return tr("Re-Label Point"); }
Chris@147 245
Chris@147 246 virtual void execute() {
Chris@147 247 m_model->deletePoint(m_oldPoint);
Chris@147 248 m_model->addPoint(m_newPoint);
Chris@147 249 std::swap(m_oldPoint, m_newPoint);
Chris@147 250 }
Chris@147 251
Chris@147 252 virtual void unexecute() { execute(); }
Chris@147 253
Chris@147 254 private:
Chris@147 255 SparseModel<PointType> *m_model;
Chris@147 256 PointType m_oldPoint;
Chris@147 257 PointType m_newPoint;
Chris@147 258 };
Chris@147 259
Chris@147 260
Chris@147 261
Chris@147 262 protected:
Chris@147 263 size_t m_sampleRate;
Chris@147 264 size_t m_resolution;
Chris@147 265 bool m_notifyOnAdd;
Chris@147 266 long m_sinceLastNotifyMin;
Chris@147 267 long m_sinceLastNotifyMax;
Chris@147 268 bool m_hasTextLabels;
Chris@147 269
Chris@147 270 PointList m_points;
Chris@147 271 size_t m_pointCount;
Chris@147 272 mutable QMutex m_mutex;
Chris@147 273 int m_completion;
Chris@147 274 };
Chris@147 275
Chris@147 276
Chris@147 277 template <typename PointType>
Chris@147 278 SparseModel<PointType>::SparseModel(size_t sampleRate,
Chris@147 279 size_t resolution,
Chris@147 280 bool notifyOnAdd) :
Chris@147 281 m_sampleRate(sampleRate),
Chris@147 282 m_resolution(resolution),
Chris@147 283 m_notifyOnAdd(notifyOnAdd),
Chris@147 284 m_sinceLastNotifyMin(-1),
Chris@147 285 m_sinceLastNotifyMax(-1),
Chris@147 286 m_hasTextLabels(false),
Chris@147 287 m_pointCount(0),
Chris@147 288 m_completion(100)
Chris@147 289 {
Chris@147 290 }
Chris@147 291
Chris@147 292 template <typename PointType>
Chris@147 293 size_t
Chris@147 294 SparseModel<PointType>::getStartFrame() const
Chris@147 295 {
Chris@147 296 QMutexLocker locker(&m_mutex);
Chris@147 297 size_t f = 0;
Chris@147 298 if (!m_points.empty()) {
Chris@147 299 f = m_points.begin()->frame;
Chris@147 300 }
Chris@147 301 return f;
Chris@147 302 }
Chris@147 303
Chris@147 304 template <typename PointType>
Chris@147 305 size_t
Chris@147 306 SparseModel<PointType>::getEndFrame() const
Chris@147 307 {
Chris@147 308 QMutexLocker locker(&m_mutex);
Chris@147 309 size_t f = 0;
Chris@147 310 if (!m_points.empty()) {
Chris@147 311 PointListIterator i(m_points.end());
Chris@147 312 f = (--i)->frame;
Chris@147 313 }
Chris@147 314 return f;
Chris@147 315 }
Chris@147 316
Chris@147 317 template <typename PointType>
Chris@147 318 Model *
Chris@147 319 SparseModel<PointType>::clone() const
Chris@147 320 {
Chris@147 321 SparseModel<PointType> *model =
Chris@147 322 new SparseModel<PointType>(m_sampleRate, m_resolution, m_notifyOnAdd);
Chris@147 323 model->m_points = m_points;
Chris@147 324 model->m_pointCount = m_pointCount;
Chris@147 325 return model;
Chris@147 326 }
Chris@147 327
Chris@147 328 template <typename PointType>
Chris@147 329 bool
Chris@147 330 SparseModel<PointType>::isEmpty() const
Chris@147 331 {
Chris@147 332 return m_pointCount == 0;
Chris@147 333 }
Chris@147 334
Chris@147 335 template <typename PointType>
Chris@147 336 size_t
Chris@147 337 SparseModel<PointType>::getPointCount() const
Chris@147 338 {
Chris@147 339 return m_pointCount;
Chris@147 340 }
Chris@147 341
Chris@147 342 template <typename PointType>
Chris@147 343 typename SparseModel<PointType>::PointList
Chris@147 344 SparseModel<PointType>::getPoints(long start, long end) const
Chris@147 345 {
Chris@147 346 if (start > end) return PointList();
Chris@147 347 QMutexLocker locker(&m_mutex);
Chris@147 348
Chris@147 349 PointType startPoint(start), endPoint(end);
Chris@147 350
Chris@147 351 PointListIterator startItr = m_points.lower_bound(startPoint);
Chris@147 352 PointListIterator endItr = m_points.upper_bound(endPoint);
Chris@147 353
Chris@147 354 if (startItr != m_points.begin()) --startItr;
Chris@147 355 if (startItr != m_points.begin()) --startItr;
Chris@147 356 if (endItr != m_points.end()) ++endItr;
Chris@147 357 if (endItr != m_points.end()) ++endItr;
Chris@147 358
Chris@147 359 PointList rv;
Chris@147 360
Chris@147 361 for (PointListIterator i = startItr; i != endItr; ++i) {
Chris@147 362 rv.insert(*i);
Chris@147 363 }
Chris@147 364
Chris@147 365 return rv;
Chris@147 366 }
Chris@147 367
Chris@147 368 template <typename PointType>
Chris@147 369 typename SparseModel<PointType>::PointList
Chris@147 370 SparseModel<PointType>::getPoints(long frame) const
Chris@147 371 {
Chris@147 372 QMutexLocker locker(&m_mutex);
Chris@147 373
Chris@147 374 if (m_resolution == 0) return PointList();
Chris@147 375
Chris@147 376 long start = (frame / m_resolution) * m_resolution;
Chris@147 377 long end = start + m_resolution;
Chris@147 378
Chris@147 379 PointType startPoint(start), endPoint(end);
Chris@147 380
Chris@147 381 PointListIterator startItr = m_points.lower_bound(startPoint);
Chris@147 382 PointListIterator endItr = m_points.upper_bound(endPoint);
Chris@147 383
Chris@147 384 PointList rv;
Chris@147 385
Chris@147 386 for (PointListIterator i = startItr; i != endItr; ++i) {
Chris@147 387 rv.insert(*i);
Chris@147 388 }
Chris@147 389
Chris@147 390 return rv;
Chris@147 391 }
Chris@147 392
Chris@147 393 template <typename PointType>
Chris@147 394 typename SparseModel<PointType>::PointList
Chris@147 395 SparseModel<PointType>::getPreviousPoints(long originFrame) const
Chris@147 396 {
Chris@147 397 QMutexLocker locker(&m_mutex);
Chris@147 398
Chris@147 399 PointType lookupPoint(originFrame);
Chris@147 400 PointList rv;
Chris@147 401
Chris@147 402 PointListIterator i = m_points.lower_bound(lookupPoint);
Chris@147 403 if (i == m_points.begin()) return rv;
Chris@147 404
Chris@147 405 --i;
Chris@147 406 long frame = i->frame;
Chris@147 407 while (i->frame == frame) {
Chris@147 408 rv.insert(*i);
Chris@147 409 if (i == m_points.begin()) break;
Chris@147 410 --i;
Chris@147 411 }
Chris@147 412
Chris@147 413 return rv;
Chris@147 414 }
Chris@147 415
Chris@147 416 template <typename PointType>
Chris@147 417 typename SparseModel<PointType>::PointList
Chris@147 418 SparseModel<PointType>::getNextPoints(long originFrame) const
Chris@147 419 {
Chris@147 420 QMutexLocker locker(&m_mutex);
Chris@147 421
Chris@147 422 PointType lookupPoint(originFrame);
Chris@147 423 PointList rv;
Chris@147 424
Chris@147 425 PointListIterator i = m_points.upper_bound(lookupPoint);
Chris@147 426 if (i == m_points.end()) return rv;
Chris@147 427
Chris@147 428 long frame = i->frame;
Chris@147 429 while (i != m_points.end() && i->frame == frame) {
Chris@147 430 rv.insert(*i);
Chris@147 431 ++i;
Chris@147 432 }
Chris@147 433
Chris@147 434 return rv;
Chris@147 435 }
Chris@147 436
Chris@147 437 template <typename PointType>
Chris@147 438 void
Chris@147 439 SparseModel<PointType>::setResolution(size_t resolution)
Chris@147 440 {
Chris@147 441 {
Chris@147 442 QMutexLocker locker(&m_mutex);
Chris@147 443 m_resolution = resolution;
Chris@147 444 }
Chris@147 445 emit modelChanged();
Chris@147 446 }
Chris@147 447
Chris@147 448 template <typename PointType>
Chris@147 449 void
Chris@147 450 SparseModel<PointType>::clear()
Chris@147 451 {
Chris@147 452 {
Chris@147 453 QMutexLocker locker(&m_mutex);
Chris@147 454 m_points.clear();
Chris@147 455 m_pointCount = 0;
Chris@147 456 }
Chris@147 457 emit modelChanged();
Chris@147 458 }
Chris@147 459
Chris@147 460 template <typename PointType>
Chris@147 461 void
Chris@147 462 SparseModel<PointType>::addPoint(const PointType &point)
Chris@147 463 {
Chris@147 464 {
Chris@147 465 QMutexLocker locker(&m_mutex);
Chris@147 466 m_points.insert(point);
Chris@147 467 m_pointCount++;
Chris@147 468 if (point.label != "") m_hasTextLabels = true;
Chris@147 469 }
Chris@147 470
Chris@147 471 // Even though this model is nominally sparse, there may still be
Chris@147 472 // too many signals going on here (especially as they'll probably
Chris@147 473 // be queued from one thread to another), which is why we need the
Chris@147 474 // notifyOnAdd as an option rather than a necessity (the
Chris@147 475 // alternative is to notify on setCompletion).
Chris@147 476
Chris@147 477 if (m_notifyOnAdd) {
Chris@147 478 emit modelChanged(point.frame, point.frame + m_resolution);
Chris@147 479 } else {
Chris@147 480 if (m_sinceLastNotifyMin == -1 ||
Chris@147 481 point.frame < m_sinceLastNotifyMin) {
Chris@147 482 m_sinceLastNotifyMin = point.frame;
Chris@147 483 }
Chris@147 484 if (m_sinceLastNotifyMax == -1 ||
Chris@147 485 point.frame > m_sinceLastNotifyMax) {
Chris@147 486 m_sinceLastNotifyMax = point.frame;
Chris@147 487 }
Chris@147 488 }
Chris@147 489 }
Chris@147 490
Chris@147 491 template <typename PointType>
Chris@147 492 void
Chris@147 493 SparseModel<PointType>::deletePoint(const PointType &point)
Chris@147 494 {
Chris@147 495 {
Chris@147 496 QMutexLocker locker(&m_mutex);
Chris@147 497
Chris@147 498 PointListIterator i = m_points.lower_bound(point);
Chris@147 499 typename PointType::Comparator comparator;
Chris@147 500 while (i != m_points.end()) {
Chris@147 501 if (i->frame > point.frame) break;
Chris@147 502 if (!comparator(*i, point) && !comparator(point, *i)) {
Chris@147 503 m_points.erase(i);
Chris@147 504 m_pointCount--;
Chris@147 505 break;
Chris@147 506 }
Chris@147 507 ++i;
Chris@147 508 }
Chris@147 509 }
Chris@147 510 // std::cout << "SparseOneDimensionalModel: emit modelChanged("
Chris@147 511 // << point.frame << ")" << std::endl;
Chris@147 512 emit modelChanged(point.frame, point.frame + m_resolution);
Chris@147 513 }
Chris@147 514
Chris@147 515 template <typename PointType>
Chris@147 516 void
Chris@147 517 SparseModel<PointType>::setCompletion(int completion)
Chris@147 518 {
Chris@297 519 std::cerr << "SparseModel::setCompletion(" << completion << ")" << std::endl;
Chris@191 520
Chris@147 521 if (m_completion != completion) {
Chris@147 522 m_completion = completion;
Chris@147 523
Chris@147 524 if (completion == 100) {
Chris@147 525
Chris@297 526 if (!m_notifyOnAdd) {
Chris@297 527 emit completionChanged();
Chris@297 528 }
Chris@297 529
Chris@147 530 m_notifyOnAdd = true; // henceforth
Chris@147 531 emit modelChanged();
Chris@147 532
Chris@147 533 } else if (!m_notifyOnAdd) {
Chris@147 534
Chris@147 535 if (m_sinceLastNotifyMin >= 0 &&
Chris@147 536 m_sinceLastNotifyMax >= 0) {
Chris@147 537 emit modelChanged(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
Chris@147 538 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@147 539 } else {
Chris@147 540 emit completionChanged();
Chris@147 541 }
Chris@147 542 } else {
Chris@147 543 emit completionChanged();
Chris@147 544 }
Chris@147 545 }
Chris@147 546 }
Chris@147 547
Chris@147 548 template <typename PointType>
Chris@147 549 void
Chris@147 550 SparseModel<PointType>::toXml(QTextStream &out,
Chris@147 551 QString indent,
Chris@147 552 QString extraAttributes) const
Chris@147 553 {
Chris@147 554 Model::toXml
Chris@147 555 (out,
Chris@147 556 indent,
Chris@147 557 QString("type=\"sparse\" dimensions=\"%1\" resolution=\"%2\" notifyOnAdd=\"%3\" dataset=\"%4\" %5")
Chris@147 558 .arg(PointType(0).getDimensions())
Chris@147 559 .arg(m_resolution)
Chris@147 560 .arg(m_notifyOnAdd ? "true" : "false")
Chris@147 561 .arg(getObjectExportId(&m_points))
Chris@147 562 .arg(extraAttributes));
Chris@147 563
Chris@147 564 out << indent;
Chris@147 565 out << QString("<dataset id=\"%1\" dimensions=\"%2\">\n")
Chris@147 566 .arg(getObjectExportId(&m_points))
Chris@147 567 .arg(PointType(0).getDimensions());
Chris@147 568
Chris@147 569 for (PointListIterator i = m_points.begin(); i != m_points.end(); ++i) {
Chris@147 570 out << i->toXmlString(indent + " ");
Chris@147 571 }
Chris@147 572
Chris@147 573 out << indent;
Chris@147 574 out << "</dataset>\n";
Chris@147 575 }
Chris@147 576
Chris@147 577 template <typename PointType>
Chris@147 578 QString
Chris@147 579 SparseModel<PointType>::toXmlString(QString indent,
Chris@147 580 QString extraAttributes) const
Chris@147 581 {
Chris@147 582 QString s;
Chris@147 583
Chris@147 584 {
Chris@147 585 QTextStream out(&s);
Chris@147 586 toXml(out, indent, extraAttributes);
Chris@147 587 }
Chris@147 588
Chris@147 589 return s;
Chris@147 590 }
Chris@147 591
Chris@147 592 template <typename PointType>
Chris@147 593 SparseModel<PointType>::EditCommand::EditCommand(SparseModel *model,
Chris@147 594 QString commandName) :
Chris@147 595 MacroCommand(commandName),
Chris@147 596 m_model(model)
Chris@147 597 {
Chris@147 598 }
Chris@147 599
Chris@147 600 template <typename PointType>
Chris@147 601 void
Chris@147 602 SparseModel<PointType>::EditCommand::addPoint(const PointType &point)
Chris@147 603 {
Chris@147 604 addCommand(new AddPointCommand(m_model, point), true);
Chris@147 605 }
Chris@147 606
Chris@147 607 template <typename PointType>
Chris@147 608 void
Chris@147 609 SparseModel<PointType>::EditCommand::deletePoint(const PointType &point)
Chris@147 610 {
Chris@147 611 addCommand(new DeletePointCommand(m_model, point), true);
Chris@147 612 }
Chris@147 613
Chris@147 614 template <typename PointType>
Chris@147 615 void
Chris@147 616 SparseModel<PointType>::EditCommand::finish()
Chris@147 617 {
Chris@147 618 if (!m_commands.empty()) {
Chris@147 619 CommandHistory::getInstance()->addCommand(this, false);
Chris@147 620 } else {
Chris@147 621 delete this;
Chris@147 622 }
Chris@147 623 }
Chris@147 624
Chris@147 625 template <typename PointType>
Chris@147 626 void
Chris@147 627 SparseModel<PointType>::EditCommand::addCommand(Command *command,
Chris@147 628 bool executeFirst)
Chris@147 629 {
Chris@147 630 if (executeFirst) command->execute();
Chris@147 631
Chris@147 632 if (!m_commands.empty()) {
Chris@147 633 DeletePointCommand *dpc = dynamic_cast<DeletePointCommand *>(command);
Chris@147 634 if (dpc) {
Chris@147 635 AddPointCommand *apc = dynamic_cast<AddPointCommand *>
Chris@147 636 (m_commands[m_commands.size() - 1]);
Chris@147 637 typename PointType::Comparator comparator;
Chris@147 638 if (apc) {
Chris@147 639 if (!comparator(apc->getPoint(), dpc->getPoint()) &&
Chris@147 640 !comparator(dpc->getPoint(), apc->getPoint())) {
Chris@147 641 deleteCommand(apc);
Chris@147 642 return;
Chris@147 643 }
Chris@147 644 }
Chris@147 645 }
Chris@147 646 }
Chris@147 647
Chris@147 648 MacroCommand::addCommand(command);
Chris@147 649 }
Chris@147 650
Chris@147 651
Chris@147 652 #endif
Chris@147 653
Chris@147 654
Chris@147 655