comparison data/model/SparseModel.h @ 147:3a13b0d4934e

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