Mercurial > hg > svgui
diff layer/SliceLayer.cpp @ 1324:13d9b422f7fe zoom
Merge from default branch
author | Chris Cannam |
---|---|
date | Mon, 17 Sep 2018 13:51:31 +0100 |
parents | 51e6125627fa |
children | d79e21855aef |
line wrap: on
line diff
--- a/layer/SliceLayer.cpp Mon Dec 12 15:18:52 2016 +0000 +++ b/layer/SliceLayer.cpp Mon Sep 17 13:51:31 2018 +0100 @@ -31,15 +31,17 @@ SliceLayer::SliceLayer() : m_sliceableModel(0), - m_colourMap(0), + m_colourMap(int(ColourMapper::Ice)), m_energyScale(dBScale), m_samplingMode(SampleMean), - m_plotStyle(PlotSteps), + m_plotStyle(PlotLines), m_binScale(LinearBins), m_normalize(false), m_threshold(0.0), m_initialThreshold(0.0), m_gain(1.0), + m_minbin(0), + m_maxbin(0), m_currentf0(0), m_currentf1(0) { @@ -65,9 +67,15 @@ m_sliceableModel = sliceable; + if (!m_sliceableModel) return; + connectSignals(m_sliceableModel); + m_minbin = 0; + m_maxbin = m_sliceableModel->getHeight(); + emit modelReplaced(); + emit layerParametersChanged(); } void @@ -107,13 +115,10 @@ maxbin = 0; if (!m_sliceableModel) return ""; - int xorigin = m_xorigins[v]; - int w = v->getPaintWidth() - xorigin - 1; - + minbin = int(getBinForX(v, p.x())); + maxbin = int(getBinForX(v, p.x() + 1)); + int mh = m_sliceableModel->getHeight(); - minbin = getBinForX(p.x() - xorigin, mh, w); - maxbin = getBinForX(p.x() - xorigin + 1, mh, w); - if (minbin >= mh) minbin = mh - 1; if (maxbin >= mh) maxbin = mh - 1; if (minbin < 0) minbin = 0; @@ -133,12 +138,15 @@ if (includeBinDescription) { + int i0 = minbin - m_minbin; + int i1 = maxbin - m_minbin; + float minvalue = 0.0; - if (minbin < int(m_values.size())) minvalue = m_values[minbin]; + if (in_range_for(m_values, i0)) minvalue = m_values[i0]; float maxvalue = minvalue; - if (maxbin < int(m_values.size())) maxvalue = m_values[maxbin]; - + if (in_range_for(m_values, i1)) maxvalue = m_values[i1]; + if (minvalue > maxvalue) std::swap(minvalue, maxvalue); QString binstr; @@ -180,10 +188,21 @@ } double -SliceLayer::getXForBin(int bin, int count, double w) const +SliceLayer::getXForBin(const LayerGeometryProvider *v, double bin) const { double x = 0; + bin -= m_minbin; + if (bin < 0) bin = 0; + + double count = m_maxbin - m_minbin; + if (count < 0) count = 0; + + int pw = v->getPaintWidth(); + int origin = m_xorigins[v->getId()]; + int w = pw - origin; + if (w < 1) w = 1; + switch (m_binScale) { case LinearBins: @@ -191,51 +210,78 @@ break; case LogBins: - x = (w * log10(bin + 1)) / log10(count + 1); + // The 0.8 here is an awkward compromise. Our x-coord is + // proportional to log of bin number, with the x-coord "of a + // bin" being that of the left edge of the bin range. We can't + // start counting bins from 0, as that would give us x = -Inf + // and hide the first bin entirely. But if we start from 1, we + // are giving a lot of space to the first bin, which in most + // display modes won't be used because the "point" location + // for that bin is in the middle of it. Yet in some modes + // we'll still want it. A compromise is to count our first bin + // as "a bit less than 1", so that most of it is visible but a + // bit is tactfully cropped at the left edge so it doesn't + // take up so much space. + x = (w * log10(bin + 0.8)) / log10(count + 0.8); break; case InvertedLogBins: x = w - (w * log10(count - bin - 1)) / log10(count); break; } - - return x; + + return x + origin; } -int -SliceLayer::getBinForX(double x, int count, double w) const +double +SliceLayer::getBinForX(const LayerGeometryProvider *v, double x) const { - int bin = 0; + double bin = 0; + double count = m_maxbin - m_minbin; + if (count < 0) count = 0; + + int pw = v->getPaintWidth(); + int origin = m_xorigins[v->getId()]; + + int w = pw - origin; + if (w < 1) w = 1; + + x = x - origin; + if (x < 0) x = 0; + + double eps = 1e-10; + switch (m_binScale) { case LinearBins: - bin = int((x * count) / w + 0.0001); + bin = (x * count) / w + eps; break; case LogBins: - bin = int(pow(10.0, (x * log10(count + 1)) / w) - 1 + 0.0001); + // See comment in getXForBin + bin = pow(10.0, (x * log10(count + 0.8)) / w) - 0.8 + eps; break; case InvertedLogBins: - bin = count + 1 - int(pow(10.0, (log10(count) * (w - x)) / double(w)) + 0.0001); + bin = count + 1 - pow(10.0, (log10(count) * (w - x)) / double(w)) + eps; break; } - return bin; + return bin + m_minbin; } double -SliceLayer::getYForValue(double value, const LayerGeometryProvider *v, double &norm) const +SliceLayer::getYForValue(const LayerGeometryProvider *v, double value, double &norm) const { norm = 0.0; - if (m_yorigins.find(v) == m_yorigins.end()) return 0; + if (m_yorigins.find(v->getId()) == m_yorigins.end()) return 0; value *= m_gain; - int yorigin = m_yorigins[v]; - int h = m_heights[v]; + int yorigin = m_yorigins[v->getId()]; + int h = m_heights[v->getId()]; double thresh = getThresholdDb(); double y = 0.0; @@ -262,7 +308,9 @@ case AbsoluteScale: value = fabs(value); - // and fall through +#if (__GNUC__ >= 7) + __attribute__ ((fallthrough)); +#endif case LinearScale: default: @@ -276,14 +324,14 @@ } double -SliceLayer::getValueForY(double y, const LayerGeometryProvider *v) const +SliceLayer::getValueForY(const LayerGeometryProvider *v, double y) const { double value = 0.0; - if (m_yorigins.find(v) == m_yorigins.end()) return value; + if (m_yorigins.find(v->getId()) == m_yorigins.end()) return value; - int yorigin = m_yorigins[v]; - int h = m_heights[v]; + int yorigin = m_yorigins[v->getId()]; + int h = m_heights[v->getId()]; double thresh = getThresholdDb(); if (h <= 0) return value; @@ -325,31 +373,43 @@ if (v->getViewManager() && v->getViewManager()->shouldShowScaleGuides()) { if (!m_scalePoints.empty()) { paint.setPen(QColor(240, 240, 240)); //!!! and dark background? + int ratio = int(round(double(v->getPaintHeight()) / + m_scalePaintHeight)); for (int i = 0; i < (int)m_scalePoints.size(); ++i) { - paint.drawLine(0, m_scalePoints[i], rect.width(), m_scalePoints[i]); + paint.drawLine(0, m_scalePoints[i] * ratio, + rect.width(), m_scalePoints[i] * ratio); } } } - paint.setPen(getBaseQColor()); + if (m_plotStyle == PlotBlocks) { + // Must use actual zero-width pen, too slow otherwise + paint.setPen(QPen(getBaseQColor(), 0)); + } else { + paint.setPen(PaintAssistant::scalePen(getBaseQColor())); + } int xorigin = getVerticalScaleWidth(v, true, paint) + 1; - int w = v->getPaintWidth() - xorigin - 1; - - m_xorigins[v] = xorigin; // for use in getFeatureDescription + m_xorigins[v->getId()] = xorigin; // for use in getFeatureDescription int yorigin = v->getPaintHeight() - 20 - paint.fontMetrics().height() - 7; int h = yorigin - paint.fontMetrics().height() - 8; - m_yorigins[v] = yorigin; // for getYForValue etc - m_heights[v] = h; + m_yorigins[v->getId()] = yorigin; // for getYForValue etc + m_heights[v->getId()] = h; if (h <= 0) return; QPainterPath path; int mh = m_sliceableModel->getHeight(); + int bin0 = 0; + if (m_maxbin > m_minbin) { + mh = m_maxbin - m_minbin; + bin0 = m_minbin; + } + int divisor = 0; m_values.clear(); @@ -383,7 +443,7 @@ for (int col = col0; col <= col1; ++col) { for (int bin = 0; bin < mh; ++bin) { - float value = m_sliceableModel->getValueAt(col, bin); + float value = m_sliceableModel->getValueAt(col, bin0 + bin); if (bin < cs) value *= curve[bin]; if (m_samplingMode == SamplePeak) { if (value > m_values[bin]) m_values[bin] = value; @@ -407,25 +467,25 @@ } } - double nx = xorigin; + double nx = getXForBin(v, bin0); ColourMapper mapper(m_colourMap, 0, 1); for (int bin = 0; bin < mh; ++bin) { double x = nx; - nx = xorigin + getXForBin(bin + 1, mh, w); + nx = getXForBin(v, bin + bin0 + 1); double value = m_values[bin]; double norm = 0.0; - double y = getYForValue(value, v, norm); + double y = getYForValue(v, value, norm); if (m_plotStyle == PlotLines) { if (bin == 0) { - path.moveTo(x, y); + path.moveTo((x + nx) / 2, y); } else { - path.lineTo(x, y); + path.lineTo((x + nx) / 2, y); } } else if (m_plotStyle == PlotSteps) { @@ -456,60 +516,20 @@ paint.drawPath(path); } paint.restore(); -/* - QPoint discard; - - if (v->getViewManager() && v->getViewManager()->shouldShowFrameCount() && - v->shouldIlluminateLocalFeatures(this, discard)) { - - int sampleRate = m_sliceableModel->getSampleRate(); - - QString startText = QString("%1 / %2") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (f0, sampleRate).toText(true))) - .arg(f0); - - QString endText = QString(" %1 / %2") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (f1, sampleRate).toText(true))) - .arg(f1); - - QString durationText = QString("(%1 / %2) ") - .arg(QString::fromStdString - (RealTime::frame2RealTime - (f1 - f0 + 1, sampleRate).toText(true))) - .arg(f1 - f0 + 1); - - v->drawVisibleText - (paint, xorigin + 5, - paint.fontMetrics().ascent() + 5, - startText, PaintAssistant::OutlinedText); - - v->drawVisibleText - (paint, xorigin + 5, - paint.fontMetrics().ascent() + paint.fontMetrics().height() + 10, - endText, PaintAssistant::OutlinedText); - - v->drawVisibleText - (paint, xorigin + 5, - paint.fontMetrics().ascent() + 2*paint.fontMetrics().height() + 15, - durationText, PaintAssistant::OutlinedText); - } -*/ } int SliceLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const { + int width; if (m_energyScale == LinearScale || m_energyScale == AbsoluteScale) { - return std::max(paint.fontMetrics().width("0.0") + 13, - paint.fontMetrics().width("x10-10")); + width = std::max(paint.fontMetrics().width("0.0") + 13, + paint.fontMetrics().width("x10-10")); } else { - return std::max(paint.fontMetrics().width(tr("0dB")), - paint.fontMetrics().width(tr("-Inf"))) + 13; + width = std::max(paint.fontMetrics().width(tr("0dB")), + paint.fontMetrics().width(tr("-Inf"))) + 13; } + return width; } void @@ -537,6 +557,15 @@ mult, const_cast<std::vector<int> *>(&m_scalePoints)); + // Ugly hack (but then everything about this scale drawing is a + // bit ugly). In pixel-doubling hi-dpi scenarios, the scale is + // painted at pixel-doubled resolution but we do explicit + // pixel-doubling ourselves when painting the layer content. We + // make a note of this here so that we can compare with the + // equivalent dimension in the paint method when deciding where to + // place scale continuation lines. + m_scalePaintHeight = v->getPaintHeight(); + if (mult != 1 && mult != 0) { int log = int(lrint(log10(mult))); QString a = tr("x10"); @@ -547,6 +576,17 @@ } } +bool +SliceLayer::hasLightBackground() const +{ + if (usesSolidColour()) { + ColourMapper mapper(m_colourMap, 0, 1); + return mapper.hasLightBackground(); + } else { + return SingleColourLayer::hasLightBackground(); + } +} + Layer::PropertyList SliceLayer::getProperties() const { @@ -591,7 +631,7 @@ if (name == "Scale") return ValueProperty; if (name == "Sampling Mode") return ValueProperty; if (name == "Bin Scale") return ValueProperty; - if (name == "Colour" && m_plotStyle == PlotFilledBlocks) return ValueProperty; + if (name == "Colour" && usesSolidColour()) return ColourMapProperty; return SingleColourLayer::getPropertyType(name); } @@ -621,57 +661,57 @@ if (name == "Gain") { - *min = -50; - *max = 50; + *min = -50; + *max = 50; *deflt = 0; - cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << endl; +// cerr << "gain is " << m_gain << ", mode is " << m_samplingMode << endl; - val = int(lrint(log10(m_gain) * 20.0)); - if (val < *min) val = *min; - if (val > *max) val = *max; + val = int(lrint(log10(m_gain) * 20.0)); + if (val < *min) val = *min; + if (val > *max) val = *max; } else if (name == "Threshold") { - *min = -80; - *max = 0; + *min = -80; + *max = 0; *deflt = int(lrint(AudioLevel::multiplier_to_dB(m_initialThreshold))); - if (*deflt < *min) *deflt = *min; - if (*deflt > *max) *deflt = *max; + if (*deflt < *min) *deflt = *min; + if (*deflt > *max) *deflt = *max; - val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); - if (val < *min) val = *min; - if (val > *max) val = *max; + val = int(lrint(AudioLevel::multiplier_to_dB(m_threshold))); + if (val < *min) val = *min; + if (val > *max) val = *max; } else if (name == "Normalize") { - - val = (m_normalize ? 1 : 0); + + val = (m_normalize ? 1 : 0); *deflt = 0; - } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) { + } else if (name == "Colour" && usesSolidColour()) { *min = 0; *max = ColourMapper::getColourMapCount() - 1; - *deflt = 0; + *deflt = int(ColourMapper::Ice); val = m_colourMap; } else if (name == "Scale") { - *min = 0; - *max = 3; + *min = 0; + *max = 3; *deflt = (int)dBScale; - val = (int)m_energyScale; + val = (int)m_energyScale; } else if (name == "Sampling Mode") { - *min = 0; - *max = 2; + *min = 0; + *max = 2; *deflt = (int)SampleMean; - val = (int)m_samplingMode; + val = (int)m_samplingMode; } else if (name == "Plot Type") { @@ -691,7 +731,7 @@ val = (int)m_binScale; } else { - val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); + val = SingleColourLayer::getPropertyRangeAndValue(name, min, max, deflt); } return val; @@ -699,44 +739,44 @@ QString SliceLayer::getPropertyValueLabel(const PropertyName &name, - int value) const + int value) const { - if (name == "Colour" && m_plotStyle == PlotFilledBlocks) { + if (name == "Colour" && usesSolidColour()) { return ColourMapper::getColourMapName(value); } if (name == "Scale") { - switch (value) { - default: - case 0: return tr("Linear"); - case 1: return tr("Meter"); - case 2: return tr("Log"); - case 3: return tr("Absolute"); - } + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Meter"); + case 2: return tr("Log"); + case 3: return tr("Absolute"); + } } if (name == "Sampling Mode") { - switch (value) { - default: - case 0: return tr("Any"); - case 1: return tr("Mean"); - case 2: return tr("Peak"); - } + switch (value) { + default: + case 0: return tr("Any"); + case 1: return tr("Mean"); + case 2: return tr("Peak"); + } } if (name == "Plot Type") { - switch (value) { - default: - case 0: return tr("Lines"); - case 1: return tr("Steps"); - case 2: return tr("Blocks"); - case 3: return tr("Colours"); - } + switch (value) { + default: + case 0: return tr("Lines"); + case 1: return tr("Steps"); + case 2: return tr("Blocks"); + case 3: return tr("Colours"); + } } if (name == "Bin Scale") { - switch (value) { - default: - case 0: return tr("Linear"); - case 1: return tr("Log"); - case 2: return tr("Rev Log"); - } + switch (value) { + default: + case 0: return tr("Linear"); + case 1: return tr("Log"); + case 2: return tr("Rev Log"); + } } return SingleColourLayer::getPropertyValueLabel(name, value); } @@ -757,38 +797,38 @@ SliceLayer::setProperty(const PropertyName &name, int value) { if (name == "Gain") { - setGain(powf(10, float(value)/20.0f)); + setGain(powf(10, float(value)/20.0f)); } else if (name == "Threshold") { - if (value == -80) setThreshold(0.0f); - else setThreshold(float(AudioLevel::dB_to_multiplier(value))); - } else if (name == "Colour" && m_plotStyle == PlotFilledBlocks) { + if (value == -80) setThreshold(0.0f); + else setThreshold(float(AudioLevel::dB_to_multiplier(value))); + } else if (name == "Colour" && usesSolidColour()) { setFillColourMap(value); } else if (name == "Scale") { - switch (value) { - default: - case 0: setEnergyScale(LinearScale); break; - case 1: setEnergyScale(MeterScale); break; - case 2: setEnergyScale(dBScale); break; - case 3: setEnergyScale(AbsoluteScale); break; - } + switch (value) { + default: + case 0: setEnergyScale(LinearScale); break; + case 1: setEnergyScale(MeterScale); break; + case 2: setEnergyScale(dBScale); break; + case 3: setEnergyScale(AbsoluteScale); break; + } } else if (name == "Plot Type") { - setPlotStyle(PlotStyle(value)); + setPlotStyle(PlotStyle(value)); } else if (name == "Sampling Mode") { - switch (value) { - default: - case 0: setSamplingMode(NearestSample); break; - case 1: setSamplingMode(SampleMean); break; - case 2: setSamplingMode(SamplePeak); break; - } + switch (value) { + default: + case 0: setSamplingMode(NearestSample); break; + case 1: setSamplingMode(SampleMean); break; + case 2: setSamplingMode(SamplePeak); break; + } } else if (name == "Bin Scale") { - switch (value) { - default: - case 0: setBinScale(LinearBins); break; - case 1: setBinScale(LogBins); break; - case 2: setBinScale(InvertedLogBins); break; - } + switch (value) { + default: + case 0: setBinScale(LinearBins); break; + case 1: setBinScale(LogBins); break; + case 2: setBinScale(InvertedLogBins); break; + } } else if (name == "Normalize") { - setNormalize(value ? true : false); + setNormalize(value ? true : false); } else { SingleColourLayer::setProperty(name, value); } @@ -886,21 +926,25 @@ QString s; s += QString("colourScheme=\"%1\" " - "energyScale=\"%2\" " + "energyScale=\"%2\" " "samplingMode=\"%3\" " "plotStyle=\"%4\" " "binScale=\"%5\" " "gain=\"%6\" " "threshold=\"%7\" " - "normalize=\"%8\"") + "normalize=\"%8\" %9") .arg(m_colourMap) - .arg(m_energyScale) + .arg(m_energyScale) .arg(m_samplingMode) .arg(m_plotStyle) .arg(m_binScale) .arg(m_gain) .arg(m_threshold) - .arg(m_normalize ? "true" : "false"); + .arg(m_normalize ? "true" : "false") + .arg(QString("minbin=\"%1\" " + "maxbin=\"%2\"") + .arg(m_minbin) + .arg(m_maxbin)); SingleColourLayer::toXml(stream, indent, extraAttributes + " " + s); } @@ -913,22 +957,22 @@ SingleColourLayer::setProperties(attributes); EnergyScale scale = (EnergyScale) - attributes.value("energyScale").toInt(&ok); + attributes.value("energyScale").toInt(&ok); if (ok) setEnergyScale(scale); SamplingMode mode = (SamplingMode) - attributes.value("samplingMode").toInt(&ok); + attributes.value("samplingMode").toInt(&ok); if (ok) setSamplingMode(mode); int colourMap = attributes.value("colourScheme").toInt(&ok); if (ok) setFillColourMap(colourMap); PlotStyle s = (PlotStyle) - attributes.value("plotStyle").toInt(&ok); + attributes.value("plotStyle").toInt(&ok); if (ok) setPlotStyle(s); BinScale b = (BinScale) - attributes.value("binScale").toInt(&ok); + attributes.value("binScale").toInt(&ok); if (ok) setBinScale(b); float gain = attributes.value("gain").toFloat(&ok); @@ -939,11 +983,105 @@ bool normalize = (attributes.value("normalize").trimmed() == "true"); setNormalize(normalize); + + bool alsoOk = false; + + float min = attributes.value("minbin").toFloat(&ok); + float max = attributes.value("maxbin").toFloat(&alsoOk); + if (ok && alsoOk) setDisplayExtents(min, max); } bool -SliceLayer::getValueExtents(double &, double &, bool &, QString &) const +SliceLayer::getValueExtents(double &min, double &max, bool &logarithmic, + QString &unit) const { - return false; + if (!m_sliceableModel) return false; + + min = 0; + max = double(m_sliceableModel->getHeight()); + + logarithmic = (m_binScale == BinScale::LogBins); + unit = ""; + + return true; } +bool +SliceLayer::getDisplayExtents(double &min, double &max) const +{ + if (!m_sliceableModel) return false; + + double hmax = double(m_sliceableModel->getHeight()); + + min = m_minbin; + max = m_maxbin; + if (max <= min) { + min = 0; + max = hmax; + } + if (min < 0) min = 0; + if (max > hmax) max = hmax; + + return true; +} + +bool +SliceLayer::setDisplayExtents(double min, double max) +{ + if (!m_sliceableModel) return false; + + m_minbin = int(lrint(min)); + m_maxbin = int(lrint(max)); + + emit layerParametersChanged(); + return true; +} + +int +SliceLayer::getVerticalZoomSteps(int &defaultStep) const +{ + if (!m_sliceableModel) return 0; + + defaultStep = 0; + int h = m_sliceableModel->getHeight(); + return h; +} + +int +SliceLayer::getCurrentVerticalZoomStep() const +{ + if (!m_sliceableModel) return 0; + + double min, max; + getDisplayExtents(min, max); + return m_sliceableModel->getHeight() - int(lrint(max - min)); +} + +void +SliceLayer::setVerticalZoomStep(int step) +{ + if (!m_sliceableModel) return; + +// SVDEBUG << "SliceLayer::setVerticalZoomStep(" <<step <<"): before: minbin = " << m_minbin << ", maxbin = " << m_maxbin << endl; + + int dist = m_sliceableModel->getHeight() - step; + if (dist < 1) dist = 1; + double centre = m_minbin + (m_maxbin - m_minbin) / 2.0; + m_minbin = int(lrint(centre - dist/2.0)); + if (m_minbin < 0) m_minbin = 0; + m_maxbin = m_minbin + dist; + if (m_maxbin > m_sliceableModel->getHeight()) m_maxbin = m_sliceableModel->getHeight(); + +// SVDEBUG << "SliceLayer::setVerticalZoomStep(" <<step <<"): after: minbin = " << m_minbin << ", maxbin = " << m_maxbin << endl; + + emit layerParametersChanged(); +} + +RangeMapper * +SliceLayer::getNewVerticalZoomRangeMapper() const +{ + if (!m_sliceableModel) return 0; + + return new LinearRangeMapper(0, m_sliceableModel->getHeight(), + 0, m_sliceableModel->getHeight(), ""); +}