comparison data/model/RegionModel.h @ 1649:1cc9a0d4b1b6 single-point

Update RegionModel following NoteModel, er, model. They have quite a bit in common that we should now pull out some of
author Chris Cannam
date Fri, 15 Mar 2019 14:23:50 +0000
parents ad5f892c0c4d
children 7a56bb85030f
comparison
equal deleted inserted replaced
1648:86bbccb79c9b 1649:1cc9a0d4b1b6
14 */ 14 */
15 15
16 #ifndef SV_REGION_MODEL_H 16 #ifndef SV_REGION_MODEL_H
17 #define SV_REGION_MODEL_H 17 #define SV_REGION_MODEL_H
18 18
19 #include "IntervalModel.h" 19 #include "EventCommands.h"
20 #include "TabularModel.h"
21 #include "Model.h"
22
20 #include "base/RealTime.h" 23 #include "base/RealTime.h"
24 #include "base/EventSeries.h"
25 #include "base/UnitDatabase.h"
26
27 #include "system/System.h"
28
29 #include <QMutex>
21 30
22 /** 31 /**
23 * RegionModel -- a concrete IntervalModel for intervals associated 32 * RegionModel -- a model for intervals associated with a value, which
24 * with a value, which we call regions for no very compelling reason. 33 * we call regions for no very compelling reason.
25 */ 34 */
26 35 class RegionModel : public Model,
27 /** 36 public TabularModel,
28 * Region "point" type. A region is something that has an onset time, 37 public EventEditable
29 * a single value, and a duration. Like other points, it can also
30 * have a label.
31 *
32 * This is called RegionRec instead of Region to avoid name collisions
33 * with the X11 Region struct. Bah.
34 */
35
36 struct RegionRec
37 {
38 public:
39 RegionRec() : frame(0), value(0.f), duration(0) { }
40 RegionRec(sv_frame_t _frame) : frame(_frame), value(0.0f), duration(0) { }
41 RegionRec(sv_frame_t _frame, float _value, sv_frame_t _duration, QString _label) :
42 frame(_frame), value(_value), duration(_duration), label(_label) { }
43
44 int getDimensions() const { return 3; }
45
46 sv_frame_t frame;
47 float value;
48 sv_frame_t duration;
49 QString label;
50
51 QString getLabel() const { return label; }
52
53 void toXml(QTextStream &stream,
54 QString indent = "",
55 QString extraAttributes = "") const
56 {
57 stream <<
58 QString("%1<point frame=\"%2\" value=\"%3\" duration=\"%4\" label=\"%5\" %6/>\n")
59 .arg(indent).arg(frame).arg(value).arg(duration)
60 .arg(XmlExportable::encodeEntities(label)).arg(extraAttributes);
61 }
62
63 QString toDelimitedDataString(QString delimiter, DataExportOptions, sv_samplerate_t sampleRate) const
64 {
65 QStringList list;
66 list << RealTime::frame2RealTime(frame, sampleRate).toString().c_str();
67 list << QString("%1").arg(value);
68 list << RealTime::frame2RealTime(duration, sampleRate).toString().c_str();
69 if (label != "") list << label;
70 return list.join(delimiter);
71 }
72
73 struct Comparator {
74 bool operator()(const RegionRec &p1,
75 const RegionRec &p2) const {
76 if (p1.frame != p2.frame) return p1.frame < p2.frame;
77 if (p1.value != p2.value) return p1.value < p2.value;
78 if (p1.duration != p2.duration) return p1.duration < p2.duration;
79 return p1.label < p2.label;
80 }
81 };
82
83 struct OrderComparator {
84 bool operator()(const RegionRec &p1,
85 const RegionRec &p2) const {
86 return p1.frame < p2.frame;
87 }
88 };
89 };
90
91
92 class RegionModel : public IntervalModel<RegionRec>
93 { 38 {
94 Q_OBJECT 39 Q_OBJECT
95 40
96 public: 41 public:
97 RegionModel(sv_samplerate_t sampleRate, int resolution, 42 RegionModel(sv_samplerate_t sampleRate,
43 int resolution,
98 bool notifyOnAdd = true) : 44 bool notifyOnAdd = true) :
99 IntervalModel<RegionRec>(sampleRate, resolution, notifyOnAdd), 45 m_sampleRate(sampleRate),
46 m_resolution(resolution),
47 m_valueMinimum(0.f),
48 m_valueMaximum(0.f),
49 m_haveExtents(false),
100 m_valueQuantization(0), 50 m_valueQuantization(0),
101 m_haveDistinctValues(false) 51 m_haveDistinctValues(false),
102 { 52 m_notifyOnAdd(notifyOnAdd),
53 m_sinceLastNotifyMin(-1),
54 m_sinceLastNotifyMax(-1),
55 m_completion(0) {
103 } 56 }
104 57
105 RegionModel(sv_samplerate_t sampleRate, int resolution, 58 RegionModel(sv_samplerate_t sampleRate, int resolution,
106 float valueMinimum, float valueMaximum, 59 float valueMinimum, float valueMaximum,
107 bool notifyOnAdd = true) : 60 bool notifyOnAdd = true) :
108 IntervalModel<RegionRec>(sampleRate, resolution, 61 m_sampleRate(sampleRate),
109 valueMinimum, valueMaximum, 62 m_resolution(resolution),
110 notifyOnAdd), 63 m_valueMinimum(valueMinimum),
64 m_valueMaximum(valueMaximum),
65 m_haveExtents(false),
111 m_valueQuantization(0), 66 m_valueQuantization(0),
112 m_haveDistinctValues(false) 67 m_haveDistinctValues(false),
113 { 68 m_notifyOnAdd(notifyOnAdd),
114 } 69 m_sinceLastNotifyMin(-1),
115 70 m_sinceLastNotifyMax(-1),
116 virtual ~RegionModel() 71 m_completion(0) {
117 { 72 }
73
74 virtual ~RegionModel() {
75 }
76
77 QString getTypeName() const override { return tr("Region"); }
78
79 bool isOK() const override { return true; }
80 sv_frame_t getStartFrame() const override { return m_events.getStartFrame(); }
81 sv_frame_t getEndFrame() const override { return m_events.getEndFrame(); }
82 sv_samplerate_t getSampleRate() const override { return m_sampleRate; }
83 int getResolution() const { return m_resolution; }
84
85 QString getScaleUnits() const { return m_units; }
86 void setScaleUnits(QString units) {
87 m_units = units;
88 UnitDatabase::getInstance()->registerUnit(units);
118 } 89 }
119 90
120 float getValueQuantization() const { return m_valueQuantization; } 91 float getValueQuantization() const { return m_valueQuantization; }
121 void setValueQuantization(float q) { m_valueQuantization = q; } 92 void setValueQuantization(float q) { m_valueQuantization = q; }
122 93
123 bool haveDistinctValues() const { return m_haveDistinctValues; } 94 bool haveDistinctValues() const { return m_haveDistinctValues; }
124 95
125 QString getTypeName() const override { return tr("Region"); } 96 float getValueMinimum() const { return m_valueMinimum; }
126 97 float getValueMaximum() const { return m_valueMaximum; }
127 void toXml(QTextStream &out, 98
128 QString indent = "", 99 int getCompletion() const { return m_completion; }
129 QString extraAttributes = "") const override 100
130 { 101 void setCompletion(int completion, bool update = true) {
131 std::cerr << "RegionModel::toXml: extraAttributes = \"" 102
132 << extraAttributes.toStdString() << std::endl; 103 bool emitCompletionChanged = true;
133 104 bool emitGeneralModelChanged = false;
134 IntervalModel<RegionRec>::toXml 105 bool emitRegionChanged = false;
135 (out, 106
136 indent, 107 {
137 QString("%1 subtype=\"region\" valueQuantization=\"%2\"") 108 QMutexLocker locker(&m_mutex);
138 .arg(extraAttributes).arg(m_valueQuantization)); 109
139 } 110 if (m_completion != completion) {
140 111 m_completion = completion;
112
113 if (completion == 100) {
114
115 if (m_notifyOnAdd) {
116 emitCompletionChanged = false;
117 }
118
119 m_notifyOnAdd = true; // henceforth
120 emitGeneralModelChanged = true;
121
122 } else if (!m_notifyOnAdd) {
123
124 if (update &&
125 m_sinceLastNotifyMin >= 0 &&
126 m_sinceLastNotifyMax >= 0) {
127 emitRegionChanged = true;
128 }
129 }
130 }
131 }
132
133 if (emitCompletionChanged) {
134 emit completionChanged();
135 }
136 if (emitGeneralModelChanged) {
137 emit modelChanged();
138 }
139 if (emitRegionChanged) {
140 emit modelChangedWithin(m_sinceLastNotifyMin, m_sinceLastNotifyMax);
141 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
142 }
143 }
144
145 /**
146 * Query methods.
147 */
148
149 int getEventCount() const {
150 return m_events.count();
151 }
152 bool isEmpty() const {
153 return m_events.isEmpty();
154 }
155 bool containsEvent(const Event &e) const {
156 return m_events.contains(e);
157 }
158 EventVector getAllEvents() const {
159 return m_events.getAllEvents();
160 }
161 EventVector getEventsSpanning(sv_frame_t f, sv_frame_t duration) const {
162 return m_events.getEventsSpanning(f, duration);
163 }
164 EventVector getEventsWithin(sv_frame_t f, sv_frame_t duration) const {
165 return m_events.getEventsWithin(f, duration);
166 }
167 EventVector getEventsStartingWithin(sv_frame_t f, sv_frame_t duration) const {
168 return m_events.getEventsStartingWithin(f, duration);
169 }
170 EventVector getEventsCovering(sv_frame_t f) const {
171 return m_events.getEventsCovering(f);
172 }
173
174 /**
175 * Editing methods.
176 */
177 void add(Event e) override {
178
179 bool allChange = false;
180
181 {
182 QMutexLocker locker(&m_mutex);
183 m_events.add(e);
184 //!!!??? if (point.getLabel() != "") m_hasTextLabels = true;
185
186 float v = e.getValue();
187 if (!ISNAN(v) && !ISINF(v)) {
188 if (!m_haveExtents || v < m_valueMinimum) {
189 m_valueMinimum = v; allChange = true;
190 }
191 if (!m_haveExtents || v > m_valueMaximum) {
192 m_valueMaximum = v; allChange = true;
193 }
194 m_haveExtents = true;
195 }
196
197 if (e.hasValue() && e.getValue() != 0.f) {
198 m_haveDistinctValues = true;
199 }
200
201 sv_frame_t f = e.getFrame();
202
203 if (!m_notifyOnAdd) {
204 if (m_sinceLastNotifyMin == -1 || f < m_sinceLastNotifyMin) {
205 m_sinceLastNotifyMin = f;
206 }
207 if (m_sinceLastNotifyMax == -1 || f > m_sinceLastNotifyMax) {
208 m_sinceLastNotifyMax = f;
209 }
210 }
211 }
212
213 if (m_notifyOnAdd) {
214 emit modelChangedWithin(e.getFrame(),
215 e.getFrame() + e.getDuration() + m_resolution);
216 }
217 if (allChange) {
218 emit modelChanged();
219 }
220 }
221
222 void remove(Event e) override {
223 {
224 QMutexLocker locker(&m_mutex);
225 m_events.remove(e);
226 }
227 emit modelChangedWithin(e.getFrame(),
228 e.getFrame() + e.getDuration() + m_resolution);
229 }
230
141 /** 231 /**
142 * TabularModel methods. 232 * TabularModel methods.
143 */ 233 */
144 234
145 int getColumnCount() const override 235 int getRowCount() const override {
146 { 236 return m_events.count();
237 }
238
239 int getColumnCount() const override {
147 return 5; 240 return 5;
148 } 241 }
149 242
150 QString getHeading(int column) const override 243 bool isColumnTimeValue(int column) const override {
151 { 244 // NB duration is not a "time value" -- that's for columns
245 // whose sort ordering is exactly that of the frame time
246 return (column < 2);
247 }
248
249 sv_frame_t getFrameForRow(int row) const override {
250 if (row < 0 || row >= m_events.count()) {
251 return 0;
252 }
253 Event e = m_events.getEventByIndex(row);
254 return e.getFrame();
255 }
256
257 int getRowForFrame(sv_frame_t frame) const override {
258 return m_events.getIndexForEvent(Event(frame));
259 }
260
261 QString getHeading(int column) const override {
152 switch (column) { 262 switch (column) {
153 case 0: return tr("Time"); 263 case 0: return tr("Time");
154 case 1: return tr("Frame"); 264 case 1: return tr("Frame");
155 case 2: return tr("Value"); 265 case 2: return tr("Value");
156 case 3: return tr("Duration"); 266 case 3: return tr("Duration");
157 case 4: return tr("Label"); 267 case 4: return tr("Label");
158 default: return tr("Unknown"); 268 default: return tr("Unknown");
159 } 269 }
160 } 270 }
161 271
162 QVariant getData(int row, int column, int role) const override 272 QVariant getData(int row, int column, int role) const override {
163 { 273
164 if (column < 4) { 274 if (row < 0 || row >= m_events.count()) {
165 return IntervalModel<RegionRec>::getData(row, column, role); 275 return QVariant();
166 } 276 }
167 277
168 PointListConstIterator i = getPointListIteratorForRow(row); 278 Event e = m_events.getEventByIndex(row);
169 if (i == m_points.end()) return QVariant();
170 279
171 switch (column) { 280 switch (column) {
172 case 4: return i->label; 281 case 0: return adaptFrameForRole(e.getFrame(), getSampleRate(), role);
282 case 1: return int(e.getFrame());
283 case 2: return adaptValueForRole(e.getValue(), getScaleUnits(), role);
284 case 3: return int(e.getDuration());
285 case 4: return e.getLabel();
173 default: return QVariant(); 286 default: return QVariant();
174 } 287 }
175 } 288 }
176 289
177 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override 290 Command *getSetDataCommand(int row, int column, const QVariant &value, int role) override {
178 { 291
179 if (column < 4) { 292 if (row < 0 || row >= m_events.count()) return nullptr;
180 return IntervalModel<RegionRec>::getSetDataCommand 293 if (role != Qt::EditRole) return nullptr;
181 (row, column, value, role); 294
182 } 295 Event e0 = m_events.getEventByIndex(row);
183 296 Event e1;
184 if (role != Qt::EditRole) return 0;
185 PointListIterator i = getPointListIteratorForRow(row);
186 if (i == m_points.end()) return 0;
187 EditCommand *command = new EditCommand(this, tr("Edit Data"));
188
189 Point point(*i);
190 command->deletePoint(point);
191 297
192 switch (column) { 298 switch (column) {
193 case 4: point.label = value.toString(); break; 299 case 0: e1 = e0.withFrame(sv_frame_t(round(value.toDouble() *
194 } 300 getSampleRate()))); break;
195 301 case 1: e1 = e0.withFrame(value.toInt()); break;
196 command->addPoint(point); 302 case 2: e1 = e0.withValue(float(value.toDouble())); break;
303 case 3: e1 = e0.withDuration(value.toInt()); break;
304 case 4: e1 = e0.withLabel(value.toString()); break;
305 }
306
307 ChangeEventsCommand *command =
308 new ChangeEventsCommand(this, tr("Edit Data"));
309 command->remove(e0);
310 command->add(e1);
197 return command->finish(); 311 return command->finish();
198 } 312 }
199 313
200 SortType getSortType(int column) const override 314 SortType getSortType(int column) const override
201 { 315 {
202 if (column == 4) return SortAlphabetical; 316 if (column == 4) return SortAlphabetical;
203 return SortNumeric; 317 return SortNumeric;
204 } 318 }
205 319
206 void addPoint(const Point &point) override 320
207 { 321 /**
208 if (point.value != 0.f) m_haveDistinctValues = true; 322 * XmlExportable methods.
209 IntervalModel<RegionRec>::addPoint(point); 323 */
324 void toXml(QTextStream &out,
325 QString indent = "",
326 QString extraAttributes = "") const override {
327
328 Model::toXml
329 (out,
330 indent,
331 QString("type=\"sparse\" dimensions=\"3\" resolution=\"%1\" "
332 "notifyOnAdd=\"%2\" dataset=\"%3\" subtype=\"%4\" "
333 "valueQuantization=\"%5\" minimum=\"%6\" maximum=\"%7\" "
334 "%8")
335 .arg(m_resolution)
336 .arg(m_notifyOnAdd ? "true" : "false")
337 .arg(getObjectExportId(&m_events))
338 .arg("region")
339 .arg(m_valueQuantization)
340 .arg(m_valueMinimum)
341 .arg(m_valueMaximum)
342 .arg(extraAttributes));
343
344 m_events.toXml(out, indent, QString("dimensions=\"3\""));
210 } 345 }
211 346
212 protected: 347 protected:
348 sv_samplerate_t m_sampleRate;
349 int m_resolution;
350
351 float m_valueMinimum;
352 float m_valueMaximum;
353 bool m_haveExtents;
213 float m_valueQuantization; 354 float m_valueQuantization;
214 bool m_haveDistinctValues; 355 bool m_haveDistinctValues;
356 QString m_units;
357
358 bool m_notifyOnAdd;
359 sv_frame_t m_sinceLastNotifyMin;
360 sv_frame_t m_sinceLastNotifyMax;
361
362 EventSeries m_events;
363
364 int m_completion;
365
366 mutable QMutex m_mutex;
215 }; 367 };
216 368
217 #endif 369 #endif