annotate data/model/SparseModel.h @ 299:576be0d0d218

* Merge transform directory from sv-match-alignment branch (the previous comment included notes for this stuff, but I missed it in the actual merge) * Fix crash when a transform fails to create an output model and the thread that created the transform then deletes its input model thinking it's no longer needed, even though the transform run thread is still using it -- fix is to wait() on the transform before returning the null output model
author Chris Cannam
date Fri, 28 Sep 2007 16:15:06 +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