Mercurial > hg > svgui
comparison layer/SliceLayer.cpp @ 1238:4d0ca1ab4cd0
Some work to make spectrum layers (and slice layers generally) zoomable in the frequency axis. Also fixes a number of view id mixups in SliceLayer which broke offset calculations for the x axis scale.
author | Chris Cannam |
---|---|
date | Tue, 07 Feb 2017 14:55:19 +0000 |
parents | 1a7c2ca31579 |
children | 9e1559b08f0d |
comparison
equal
deleted
inserted
replaced
1237:2cc9e0e5df51 | 1238:4d0ca1ab4cd0 |
---|---|
38 m_binScale(LinearBins), | 38 m_binScale(LinearBins), |
39 m_normalize(false), | 39 m_normalize(false), |
40 m_threshold(0.0), | 40 m_threshold(0.0), |
41 m_initialThreshold(0.0), | 41 m_initialThreshold(0.0), |
42 m_gain(1.0), | 42 m_gain(1.0), |
43 m_minbin(0), | |
44 m_maxbin(0), | |
43 m_currentf0(0), | 45 m_currentf0(0), |
44 m_currentf1(0) | 46 m_currentf1(0) |
45 { | 47 { |
46 } | 48 } |
47 | 49 |
65 | 67 |
66 m_sliceableModel = sliceable; | 68 m_sliceableModel = sliceable; |
67 | 69 |
68 connectSignals(m_sliceableModel); | 70 connectSignals(m_sliceableModel); |
69 | 71 |
72 m_minbin = 0; | |
73 m_maxbin = m_sliceableModel->getHeight(); | |
74 | |
70 emit modelReplaced(); | 75 emit modelReplaced(); |
76 emit layerParametersChanged(); | |
71 } | 77 } |
72 | 78 |
73 void | 79 void |
74 SliceLayer::sliceableModelReplaced(const Model *orig, const Model *replacement) | 80 SliceLayer::sliceableModelReplaced(const Model *orig, const Model *replacement) |
75 { | 81 { |
105 { | 111 { |
106 minbin = 0; | 112 minbin = 0; |
107 maxbin = 0; | 113 maxbin = 0; |
108 if (!m_sliceableModel) return ""; | 114 if (!m_sliceableModel) return ""; |
109 | 115 |
110 int xorigin = m_xorigins[v]; | 116 minbin = int(round(getBinForX(v, p.x()))); |
111 int w = v->getPaintWidth() - xorigin - 1; | 117 maxbin = int(round(getBinForX(v, p.x() + 1))); |
112 | 118 |
113 int mh = m_sliceableModel->getHeight(); | 119 int mh = m_sliceableModel->getHeight(); |
114 minbin = getBinForX(p.x() - xorigin, mh, w); | |
115 maxbin = getBinForX(p.x() - xorigin + 1, mh, w); | |
116 | |
117 if (minbin >= mh) minbin = mh - 1; | 120 if (minbin >= mh) minbin = mh - 1; |
118 if (maxbin >= mh) maxbin = mh - 1; | 121 if (maxbin >= mh) maxbin = mh - 1; |
119 if (minbin < 0) minbin = 0; | 122 if (minbin < 0) minbin = 0; |
120 if (maxbin < 0) maxbin = 0; | 123 if (maxbin < 0) maxbin = 0; |
121 | 124 |
131 | 134 |
132 QString rtrangestr = QString("%1 s").arg((rt1 - rt0).toText().c_str()); | 135 QString rtrangestr = QString("%1 s").arg((rt1 - rt0).toText().c_str()); |
133 | 136 |
134 if (includeBinDescription) { | 137 if (includeBinDescription) { |
135 | 138 |
139 int i0 = minbin - m_minbin; | |
140 int i1 = maxbin - m_minbin; | |
141 | |
136 float minvalue = 0.0; | 142 float minvalue = 0.0; |
137 if (minbin < int(m_values.size())) minvalue = m_values[minbin]; | 143 if (in_range_for(m_values, i0)) minvalue = m_values[i0]; |
138 | 144 |
139 float maxvalue = minvalue; | 145 float maxvalue = minvalue; |
140 if (maxbin < int(m_values.size())) maxvalue = m_values[maxbin]; | 146 if (in_range_for(m_values, i1)) maxvalue = m_values[i1]; |
141 | 147 |
142 if (minvalue > maxvalue) std::swap(minvalue, maxvalue); | 148 if (minvalue > maxvalue) std::swap(minvalue, maxvalue); |
143 | 149 |
144 QString binstr; | 150 QString binstr; |
145 if (maxbin != minbin) { | 151 if (maxbin != minbin) { |
146 binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1); | 152 binstr = tr("%1 - %2").arg(minbin+1).arg(maxbin+1); |
178 return description; | 184 return description; |
179 } | 185 } |
180 } | 186 } |
181 | 187 |
182 double | 188 double |
183 SliceLayer::getXForBin(int bin, int count, double w) const | 189 SliceLayer::getXForBin(const LayerGeometryProvider *v, double bin) const |
184 { | 190 { |
185 double x = 0; | 191 double x = 0; |
192 | |
193 bin -= m_minbin; | |
194 if (bin < 0) bin = 0; | |
195 | |
196 double count = m_maxbin - m_minbin; | |
197 if (count < 0) count = 0; | |
198 | |
199 int pw = v->getPaintWidth(); | |
200 int origin = m_xorigins[v->getId()]; | |
201 int w = pw - origin; | |
202 if (w < 1) w = 1; | |
186 | 203 |
187 switch (m_binScale) { | 204 switch (m_binScale) { |
188 | 205 |
189 case LinearBins: | 206 case LinearBins: |
190 x = (w * bin) / count; | 207 x = (w * bin) / count; |
196 | 213 |
197 case InvertedLogBins: | 214 case InvertedLogBins: |
198 x = w - (w * log10(count - bin - 1)) / log10(count); | 215 x = w - (w * log10(count - bin - 1)) / log10(count); |
199 break; | 216 break; |
200 } | 217 } |
201 | 218 |
202 return x; | 219 return x + origin; |
203 } | 220 } |
204 | 221 |
205 int | 222 double |
206 SliceLayer::getBinForX(double x, int count, double w) const | 223 SliceLayer::getBinForX(const LayerGeometryProvider *v, double x) const |
207 { | 224 { |
208 int bin = 0; | 225 double bin = 0; |
209 | 226 |
227 double count = m_maxbin - m_minbin; | |
228 if (count < 0) count = 0; | |
229 | |
230 int pw = v->getPaintWidth(); | |
231 int origin = m_xorigins[v->getId()]; | |
232 | |
233 int w = pw - origin; | |
234 if (w < 1) w = 1; | |
235 | |
236 x = x - origin; | |
237 if (x < 0) x = 0; | |
238 | |
210 switch (m_binScale) { | 239 switch (m_binScale) { |
211 | 240 |
212 case LinearBins: | 241 case LinearBins: |
213 bin = int((x * count) / w + 0.0001); | 242 bin = int((x * count) / w + 0.0001); |
214 break; | 243 break; |
220 case InvertedLogBins: | 249 case InvertedLogBins: |
221 bin = count + 1 - int(pow(10.0, (log10(count) * (w - x)) / double(w)) + 0.0001); | 250 bin = count + 1 - int(pow(10.0, (log10(count) * (w - x)) / double(w)) + 0.0001); |
222 break; | 251 break; |
223 } | 252 } |
224 | 253 |
225 return bin; | 254 return bin + m_minbin; |
226 } | 255 } |
227 | 256 |
228 double | 257 double |
229 SliceLayer::getYForValue(double value, const LayerGeometryProvider *v, double &norm) const | 258 SliceLayer::getYForValue(const LayerGeometryProvider *v, double value, double &norm) const |
230 { | 259 { |
231 norm = 0.0; | 260 norm = 0.0; |
232 | 261 |
233 if (m_yorigins.find(v) == m_yorigins.end()) return 0; | 262 if (m_yorigins.find(v->getId()) == m_yorigins.end()) return 0; |
234 | 263 |
235 value *= m_gain; | 264 value *= m_gain; |
236 | 265 |
237 int yorigin = m_yorigins[v]; | 266 int yorigin = m_yorigins[v->getId()]; |
238 int h = m_heights[v]; | 267 int h = m_heights[v->getId()]; |
239 double thresh = getThresholdDb(); | 268 double thresh = getThresholdDb(); |
240 | 269 |
241 double y = 0.0; | 270 double y = 0.0; |
242 | 271 |
243 if (h <= 0) return y; | 272 if (h <= 0) return y; |
274 | 303 |
275 return y; | 304 return y; |
276 } | 305 } |
277 | 306 |
278 double | 307 double |
279 SliceLayer::getValueForY(double y, const LayerGeometryProvider *v) const | 308 SliceLayer::getValueForY(const LayerGeometryProvider *v, double y) const |
280 { | 309 { |
281 double value = 0.0; | 310 double value = 0.0; |
282 | 311 |
283 if (m_yorigins.find(v) == m_yorigins.end()) return value; | 312 if (m_yorigins.find(v->getId()) == m_yorigins.end()) return value; |
284 | 313 |
285 int yorigin = m_yorigins[v]; | 314 int yorigin = m_yorigins[v->getId()]; |
286 int h = m_heights[v]; | 315 int h = m_heights[v->getId()]; |
287 double thresh = getThresholdDb(); | 316 double thresh = getThresholdDb(); |
288 | 317 |
289 if (h <= 0) return value; | 318 if (h <= 0) return value; |
290 | 319 |
291 y = yorigin - y; | 320 y = yorigin - y; |
339 } | 368 } |
340 | 369 |
341 int xorigin = getVerticalScaleWidth(v, true, paint) + 1; | 370 int xorigin = getVerticalScaleWidth(v, true, paint) + 1; |
342 int w = v->getPaintWidth() - xorigin - 1; | 371 int w = v->getPaintWidth() - xorigin - 1; |
343 | 372 |
344 m_xorigins[v] = xorigin; // for use in getFeatureDescription | 373 m_xorigins[v->getId()] = xorigin; // for use in getFeatureDescription |
345 | 374 |
346 int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 7; | 375 int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 7; |
347 int h = yorigin - paint.fontMetrics().height() - 8; | 376 int h = yorigin - paint.fontMetrics().height() - 8; |
348 | 377 |
349 m_yorigins[v] = yorigin; // for getYForValue etc | 378 m_yorigins[v->getId()] = yorigin; // for getYForValue etc |
350 m_heights[v] = h; | 379 m_heights[v->getId()] = h; |
351 | 380 |
352 if (h <= 0) return; | 381 if (h <= 0) return; |
353 | 382 |
354 QPainterPath path; | 383 QPainterPath path; |
355 | 384 |
356 int mh = m_sliceableModel->getHeight(); | 385 int mh = m_sliceableModel->getHeight(); |
357 | 386 int bin0 = 0; |
387 | |
388 if (m_maxbin > m_minbin) { | |
389 mh = m_maxbin - m_minbin; | |
390 bin0 = m_minbin; | |
391 } | |
392 | |
358 int divisor = 0; | 393 int divisor = 0; |
359 | 394 |
360 m_values.clear(); | 395 m_values.clear(); |
361 for (int bin = 0; bin < mh; ++bin) { | 396 for (int bin = 0; bin < mh; ++bin) { |
362 m_values.push_back(0.0); | 397 m_values.push_back(0.0); |
386 getBiasCurve(curve); | 421 getBiasCurve(curve); |
387 int cs = int(curve.size()); | 422 int cs = int(curve.size()); |
388 | 423 |
389 for (int col = col0; col <= col1; ++col) { | 424 for (int col = col0; col <= col1; ++col) { |
390 for (int bin = 0; bin < mh; ++bin) { | 425 for (int bin = 0; bin < mh; ++bin) { |
391 float value = m_sliceableModel->getValueAt(col, bin); | 426 float value = m_sliceableModel->getValueAt(col, bin0 + bin); |
392 if (bin < cs) value *= curve[bin]; | 427 if (bin < cs) value *= curve[bin]; |
393 if (m_samplingMode == SamplePeak) { | 428 if (m_samplingMode == SamplePeak) { |
394 if (value > m_values[bin]) m_values[bin] = value; | 429 if (value > m_values[bin]) m_values[bin] = value; |
395 } else { | 430 } else { |
396 m_values[bin] += value; | 431 m_values[bin] += value; |
410 for (int bin = 0; bin < mh; ++bin) { | 445 for (int bin = 0; bin < mh; ++bin) { |
411 m_values[bin] /= max; | 446 m_values[bin] /= max; |
412 } | 447 } |
413 } | 448 } |
414 | 449 |
415 double nx = xorigin; | 450 double nx = getXForBin(v, bin0); |
416 | 451 |
417 ColourMapper mapper(m_colourMap, 0, 1); | 452 ColourMapper mapper(m_colourMap, 0, 1); |
418 | 453 |
419 for (int bin = 0; bin < mh; ++bin) { | 454 for (int bin = 0; bin < mh; ++bin) { |
420 | 455 |
421 double x = nx; | 456 double x = nx; |
422 nx = xorigin + getXForBin(bin + 1, mh, w); | 457 nx = getXForBin(v, bin + bin0 + 1); |
423 | 458 |
424 double value = m_values[bin]; | 459 double value = m_values[bin]; |
425 double norm = 0.0; | 460 double norm = 0.0; |
426 double y = getYForValue(value, v, norm); | 461 double y = getYForValue(v, value, norm); |
427 | 462 |
428 if (m_plotStyle == PlotLines) { | 463 if (m_plotStyle == PlotLines) { |
429 | 464 |
430 if (bin == 0) { | 465 if (bin == 0) { |
431 path.moveTo((x + nx) / 2, y); | 466 path.moveTo((x + nx) / 2, y); |
459 | 494 |
460 if (m_plotStyle != PlotFilledBlocks) { | 495 if (m_plotStyle != PlotFilledBlocks) { |
461 paint.drawPath(path); | 496 paint.drawPath(path); |
462 } | 497 } |
463 paint.restore(); | 498 paint.restore(); |
464 /* | |
465 QPoint discard; | |
466 | |
467 if (v->getViewManager() && v->getViewManager()->shouldShowFrameCount() && | |
468 v->shouldIlluminateLocalFeatures(this, discard)) { | |
469 | |
470 int sampleRate = m_sliceableModel->getSampleRate(); | |
471 | |
472 QString startText = QString("%1 / %2") | |
473 .arg(QString::fromStdString | |
474 (RealTime::frame2RealTime | |
475 (f0, sampleRate).toText(true))) | |
476 .arg(f0); | |
477 | |
478 QString endText = QString(" %1 / %2") | |
479 .arg(QString::fromStdString | |
480 (RealTime::frame2RealTime | |
481 (f1, sampleRate).toText(true))) | |
482 .arg(f1); | |
483 | |
484 QString durationText = QString("(%1 / %2) ") | |
485 .arg(QString::fromStdString | |
486 (RealTime::frame2RealTime | |
487 (f1 - f0 + 1, sampleRate).toText(true))) | |
488 .arg(f1 - f0 + 1); | |
489 | |
490 v->drawVisibleText | |
491 (paint, xorigin + 5, | |
492 paint.fontMetrics().ascent() + 5, | |
493 startText, PaintAssistant::OutlinedText); | |
494 | |
495 v->drawVisibleText | |
496 (paint, xorigin + 5, | |
497 paint.fontMetrics().ascent() + paint.fontMetrics().height() + 10, | |
498 endText, PaintAssistant::OutlinedText); | |
499 | |
500 v->drawVisibleText | |
501 (paint, xorigin + 5, | |
502 paint.fontMetrics().ascent() + 2*paint.fontMetrics().height() + 15, | |
503 durationText, PaintAssistant::OutlinedText); | |
504 } | |
505 */ | |
506 } | 499 } |
507 | 500 |
508 int | 501 int |
509 SliceLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const | 502 SliceLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const |
510 { | 503 { |
504 int width; | |
511 if (m_energyScale == LinearScale || m_energyScale == AbsoluteScale) { | 505 if (m_energyScale == LinearScale || m_energyScale == AbsoluteScale) { |
512 return std::max(paint.fontMetrics().width("0.0") + 13, | 506 width = std::max(paint.fontMetrics().width("0.0") + 13, |
513 paint.fontMetrics().width("x10-10")); | 507 paint.fontMetrics().width("x10-10")); |
514 } else { | 508 } else { |
515 return std::max(paint.fontMetrics().width(tr("0dB")), | 509 width = std::max(paint.fontMetrics().width(tr("0dB")), |
516 paint.fontMetrics().width(tr("-Inf"))) + 13; | 510 paint.fontMetrics().width(tr("-Inf"))) + 13; |
517 } | 511 } |
512 return width; | |
518 } | 513 } |
519 | 514 |
520 void | 515 void |
521 SliceLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const | 516 SliceLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const |
522 { | 517 { |
628 | 623 |
629 *min = -50; | 624 *min = -50; |
630 *max = 50; | 625 *max = 50; |
631 *deflt = 0; | 626 *deflt = 0; |
632 | 627 |
633 cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << endl; | 628 // cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << endl; |
634 | 629 |
635 val = int(lrint(log10(m_gain) * 20.0)); | 630 val = int(lrint(log10(m_gain) * 20.0)); |
636 if (val < *min) val = *min; | 631 if (val < *min) val = *min; |
637 if (val > *max) val = *max; | 632 if (val > *max) val = *max; |
638 | 633 |
895 "samplingMode=\"%3\" " | 890 "samplingMode=\"%3\" " |
896 "plotStyle=\"%4\" " | 891 "plotStyle=\"%4\" " |
897 "binScale=\"%5\" " | 892 "binScale=\"%5\" " |
898 "gain=\"%6\" " | 893 "gain=\"%6\" " |
899 "threshold=\"%7\" " | 894 "threshold=\"%7\" " |
900 "normalize=\"%8\"") | 895 "normalize=\"%8\" %9") |
901 .arg(m_colourMap) | 896 .arg(m_colourMap) |
902 .arg(m_energyScale) | 897 .arg(m_energyScale) |
903 .arg(m_samplingMode) | 898 .arg(m_samplingMode) |
904 .arg(m_plotStyle) | 899 .arg(m_plotStyle) |
905 .arg(m_binScale) | 900 .arg(m_binScale) |
906 .arg(m_gain) | 901 .arg(m_gain) |
907 .arg(m_threshold) | 902 .arg(m_threshold) |
908 .arg(m_normalize ? "true" : "false"); | 903 .arg(m_normalize ? "true" : "false") |
904 .arg(QString("minbin=\"%1\" " | |
905 "maxbin=\"%2\"") | |
906 .arg(m_minbin) | |
907 .arg(m_maxbin)); | |
909 | 908 |
910 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s); | 909 SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s); |
911 } | 910 } |
912 | 911 |
913 void | 912 void |
942 float threshold = attributes.value("threshold").toFloat(&ok); | 941 float threshold = attributes.value("threshold").toFloat(&ok); |
943 if (ok) setThreshold(threshold); | 942 if (ok) setThreshold(threshold); |
944 | 943 |
945 bool normalize = (attributes.value("normalize").trimmed() == "true"); | 944 bool normalize = (attributes.value("normalize").trimmed() == "true"); |
946 setNormalize(normalize); | 945 setNormalize(normalize); |
946 | |
947 bool alsoOk = false; | |
948 | |
949 float min = attributes.value("minbin").toFloat(&ok); | |
950 float max = attributes.value("maxbin").toFloat(&alsoOk); | |
951 if (ok && alsoOk) setDisplayExtents(min, max); | |
947 } | 952 } |
948 | 953 |
949 bool | 954 bool |
950 SliceLayer::getValueExtents(double &, double &, bool &, QString &) const | 955 SliceLayer::getValueExtents(double &min, double &max, bool &logarithmic, |
951 { | 956 QString &unit) const |
952 return false; | 957 { |
953 } | 958 if (!m_sliceableModel) return false; |
954 | 959 |
960 min = 0; | |
961 max = double(m_sliceableModel->getHeight()); | |
962 | |
963 logarithmic = (m_binScale == BinScale::LogBins); | |
964 unit = ""; | |
965 | |
966 return true; | |
967 } | |
968 | |
969 bool | |
970 SliceLayer::getDisplayExtents(double &min, double &max) const | |
971 { | |
972 if (!m_sliceableModel) return false; | |
973 | |
974 double hmax = double(m_sliceableModel->getHeight()); | |
975 | |
976 min = m_minbin; | |
977 max = m_maxbin; | |
978 if (max <= min) { | |
979 min = 0; | |
980 max = hmax; | |
981 } | |
982 if (min < 0) min = 0; | |
983 if (max > hmax) max = hmax; | |
984 | |
985 return true; | |
986 } | |
987 | |
988 bool | |
989 SliceLayer::setDisplayExtents(double min, double max) | |
990 { | |
991 if (!m_sliceableModel) return false; | |
992 | |
993 m_minbin = int(lrint(min)); | |
994 m_maxbin = int(lrint(max)); | |
995 | |
996 emit layerParametersChanged(); | |
997 return true; | |
998 } | |
999 | |
1000 int | |
1001 SliceLayer::getVerticalZoomSteps(int &defaultStep) const | |
1002 { | |
1003 if (!m_sliceableModel) return 0; | |
1004 | |
1005 defaultStep = 0; | |
1006 int h = m_sliceableModel->getHeight(); | |
1007 return h; | |
1008 } | |
1009 | |
1010 int | |
1011 SliceLayer::getCurrentVerticalZoomStep() const | |
1012 { | |
1013 if (!m_sliceableModel) return 0; | |
1014 | |
1015 double min, max; | |
1016 getDisplayExtents(min, max); | |
1017 return m_sliceableModel->getHeight() - int(lrint(max - min)); | |
1018 } | |
1019 | |
1020 void | |
1021 SliceLayer::setVerticalZoomStep(int step) | |
1022 { | |
1023 if (!m_sliceableModel) return; | |
1024 | |
1025 // SVDEBUG << "SliceLayer::setVerticalZoomStep(" <<step <<"): before: minbin = " << m_minbin << ", maxbin = " << m_maxbin << endl; | |
1026 | |
1027 int dist = m_sliceableModel->getHeight() - step; | |
1028 if (dist < 1) dist = 1; | |
1029 double centre = m_minbin + (m_maxbin - m_minbin) / 2.0; | |
1030 m_minbin = int(lrint(centre - dist/2.0)); | |
1031 if (m_minbin < 0) m_minbin = 0; | |
1032 m_maxbin = m_minbin + dist; | |
1033 if (m_maxbin > m_sliceableModel->getHeight()) m_maxbin = m_sliceableModel->getHeight(); | |
1034 | |
1035 // SVDEBUG << "SliceLayer::setVerticalZoomStep(" <<step <<"): after: minbin = " << m_minbin << ", maxbin = " << m_maxbin << endl; | |
1036 | |
1037 emit layerParametersChanged(); | |
1038 } | |
1039 | |
1040 RangeMapper * | |
1041 SliceLayer::getNewVerticalZoomRangeMapper() const | |
1042 { | |
1043 if (!m_sliceableModel) return 0; | |
1044 | |
1045 return new LinearRangeMapper(0, m_sliceableModel->getHeight(), | |
1046 0, m_sliceableModel->getHeight(), ""); | |
1047 } |