# HG changeset patch # User Chris Cannam # Date 1542287198 0 # Node ID decb7741d036753352e467320e1f32abdb1521ce # Parent ba1f0234efa73f4eebc6a6936683eaec9f11ff74 Different approach to x-coord calculation in slice layer - let's acknowledge that we really do have two different types of model, those whose "bins" are actually at a single value (frequency in the case of the spectrum) and those whose bins are just labels. diff -r ba1f0234efa7 -r decb7741d036 layer/SliceLayer.cpp --- a/layer/SliceLayer.cpp Wed Nov 14 15:47:21 2018 +0000 +++ b/layer/SliceLayer.cpp Thu Nov 15 13:06:38 2018 +0000 @@ -33,6 +33,7 @@ SliceLayer::SliceLayer() : m_sliceableModel(0), + m_binAlignment(BinsSpanScalePoints), m_colourMap(int(ColourMapper::Ice)), m_colourInverted(false), m_energyScale(dBScale), @@ -120,8 +121,13 @@ maxbin = 0; if (!m_sliceableModel) return ""; - minbin = int(getBinForX(v, p.x())); - maxbin = int(getBinForX(v, p.x() + 1)); + if (m_binAlignment == BinsSpanScalePoints) { + minbin = int(getBinForX(v, p.x())); + maxbin = int(getBinForX(v, p.x() + 1)); + } else { + minbin = int(getBinForX(v, p.x()) + 0.5); + maxbin = int(getBinForX(v, p.x() + 1) + 0.5); + } int mh = m_sliceableModel->getHeight(); if (minbin >= mh) minbin = mh - 1; @@ -449,9 +455,11 @@ } else { // Similarly, if there are very many bins here, we use a // thinner pen - QPen pen(getBaseQColor(), 1); + QPen pen; if (mh < 10000) { - pen = PaintAssistant::scalePen(pen); + pen = PaintAssistant::scalePen(QPen(getBaseQColor(), 0.8)); + } else { + pen = QPen(getBaseQColor(), 1); } paint.setPen(pen); } @@ -530,21 +538,30 @@ } } - double nx = getXForBin(v, bin0); - ColourMapper mapper(m_colourMap, m_colourInverted, 0, 1); double ytop = 0, ybottom = 0; bool firstBinOfPixel = true; QColor prevColour = v->getBackground(); - double prevPx = 0; double prevYtop = 0; + double xleft = -1, xmiddle = -1, xright = -1; + double prevXmiddle = 0; + for (int bin = 0; bin < mh; ++bin) { - double x = nx; - nx = getXForBin(v, bin + bin0 + 1); + if (m_binAlignment == BinsSpanScalePoints) { + if (xright >= 0) xleft = xright; // previous value of + else xleft = getXForBin(v, bin0 + bin); + xmiddle = getXForBin(v, bin0 + bin + 0.5); + xright = getXForBin(v, bin0 + bin + 1); + } else { + if (xright >= 0) xleft = xright; // previous value of + else xleft = getXForBin(v, bin0 + bin - 0.5); + xmiddle = getXForBin(v, bin0 + bin); + xright = getXForBin(v, bin0 + bin + 0.5); + } double value = m_values[bin]; double norm = 0.0; @@ -557,43 +574,41 @@ ybottom = y; } - if (int(nx) != int(x) || bin+1 == mh) { + if (int(xright) != int(xleft) || bin+1 == mh) { if (m_plotStyle == PlotLines) { - double px = (x + nx) / 2; - if (bin == 0) { - path.moveTo(px, y); + path.moveTo(xmiddle, y); } else { if (ytop != ybottom) { - path.lineTo(px, ybottom); - path.lineTo(px, ytop); - path.moveTo(px, ybottom); + path.lineTo(xmiddle, ybottom); + path.lineTo(xmiddle, ytop); + path.moveTo(xmiddle, ybottom); } else { - path.lineTo(px, ytop); + path.lineTo(xmiddle, ytop); } } } else if (m_plotStyle == PlotSteps) { if (bin == 0) { - path.moveTo(x, y); + path.moveTo(xleft, y); } else { - path.lineTo(x, ytop); + path.lineTo(xleft, ytop); } - path.lineTo(nx, ytop); + path.lineTo(xright, ytop); } else if (m_plotStyle == PlotBlocks) { // work in pixel coords here, as we don't want the // vertical edges to be antialiased - path.moveTo(QPoint(int(x), int(yorigin))); - path.lineTo(QPoint(int(x), int(ytop))); - path.lineTo(QPoint(int(nx), int(ytop))); - path.lineTo(QPoint(int(nx), int(yorigin))); - path.lineTo(QPoint(int(x), int(yorigin))); + path.moveTo(QPoint(int(xleft), int(yorigin))); + path.lineTo(QPoint(int(xleft), int(ytop))); + path.lineTo(QPoint(int(xright), int(ytop))); + path.lineTo(QPoint(int(xright), int(yorigin))); + path.lineTo(QPoint(int(xleft), int(yorigin))); } else if (m_plotStyle == PlotFilledBlocks) { @@ -603,45 +618,44 @@ // work in pixel coords here, as we don't want the // vertical edges to be antialiased - if (nx > x + 1) { + if (xright > xleft + 1) { - double px = (x + nx) / 2; - QVector pp; if (bin > 0) { paint.setBrush(prevColour); pp.clear(); - pp << QPoint(int(prevPx), int(yorigin)); - pp << QPoint(int(prevPx), int(prevYtop)); - pp << QPoint(int((px + prevPx) / 2), + pp << QPoint(int(prevXmiddle), int(yorigin)); + pp << QPoint(int(prevXmiddle), int(prevYtop)); + pp << QPoint(int((xmiddle + prevXmiddle) / 2), int((ytop + prevYtop) / 2)); - pp << QPoint(int((px + prevPx) / 2), + pp << QPoint(int((xmiddle + prevXmiddle) / 2), int(yorigin)); paint.drawConvexPolygon(QPolygon(pp)); paint.setBrush(c); pp.clear(); - pp << QPoint(int((px + prevPx) / 2), + pp << QPoint(int((xmiddle + prevXmiddle) / 2), int(yorigin)); - pp << QPoint(int((px + prevPx) / 2), + pp << QPoint(int((xmiddle + prevXmiddle) / 2), int((ytop + prevYtop) / 2)); - pp << QPoint(int(px), int(ytop)); - pp << QPoint(int(px), int(yorigin)); + pp << QPoint(int(xmiddle), int(ytop)); + pp << QPoint(int(xmiddle), int(yorigin)); paint.drawConvexPolygon(QPolygon(pp)); } - prevPx = px; prevColour = c; prevYtop = ytop; } else { - paint.fillRect(QRect(int(x), int(ytop), - int(nx) - int(x), + paint.fillRect(QRect(int(xleft), int(ytop), + int(xright) - int(xleft), int(yorigin) - int(ytop)), c); } + + prevXmiddle = xmiddle; } firstBinOfPixel = true; diff -r ba1f0234efa7 -r decb7741d036 layer/SliceLayer.h --- a/layer/SliceLayer.h Wed Nov 14 15:47:21 2018 +0000 +++ b/layer/SliceLayer.h Thu Nov 15 13:06:38 2018 +0000 @@ -155,7 +155,24 @@ virtual int getDefaultColourHint(bool dark, bool &impose); + // Determine how the bins are lined up + // horizontally. BinsCentredOnScalePoint means we operate like a + // spectrum, where a bin maps to a specific frequency, and so the + // bin should be visually centred on the scale point that + // corresponds to that frequency. BinsSpanScalePoints means we + // have numbered or labelled bins that are not mapped to a + // continuous scale, like a typical chromagram output, and so bin + // N spans from scale point N to N+1. This is a fundamental + // quality of the class or input data, not a user-configurable + // property. + // + enum BinAlignment { + BinsCentredOnScalePoints, + BinsSpanScalePoints + }; + const DenseThreeDimensionalModel *m_sliceableModel; + BinAlignment m_binAlignment; int m_colourMap; bool m_colourInverted; EnergyScale m_energyScale; diff -r ba1f0234efa7 -r decb7741d036 layer/SpectrumLayer.cpp --- a/layer/SpectrumLayer.cpp Wed Nov 14 15:47:21 2018 +0000 +++ b/layer/SpectrumLayer.cpp Thu Nov 15 13:06:38 2018 +0000 @@ -43,6 +43,8 @@ m_showPeaks(false), m_newFFTNeeded(true) { + m_binAlignment = BinsCentredOnScalePoints; + Preferences *prefs = Preferences::getInstance(); connect(prefs, SIGNAL(propertyChanged(PropertyContainer::PropertyName)), this, SLOT(preferenceChanged(PropertyContainer::PropertyName))); @@ -126,7 +128,7 @@ m_minbin = 1; m_maxbin = newFFT->getHeight(); } - + setSliceableModel(newFFT); m_biasCurve.clear(); @@ -296,13 +298,19 @@ SVDEBUG << "setWindowSize: from " << m_windowSize << " to " << ws << ": updating min and max bins from " << m_minbin << " and " << m_maxbin << " to "; - + /* m_minbin = int(round((double(m_minbin) / m_windowSize) * ws)); + */ m_maxbin = int(round((double(m_maxbin) / m_windowSize) * ws)); + m_windowSize = ws; + + int h = getFFTSize() / 2 + 1; + if (m_minbin > h) m_minbin = h; + if (m_maxbin > h) m_maxbin = h; + SVDEBUG << m_minbin << " and " << m_maxbin << endl; - m_windowSize = ws; m_newFFTNeeded = true; emit layerParametersChanged(); } @@ -333,13 +341,19 @@ SVDEBUG << "setOversampling: from " << m_oversampling << " to " << oversampling << ": updating min and max bins from " << m_minbin << " and " << m_maxbin << " to "; - +/* m_minbin = int(round((double(m_minbin) / m_oversampling) * oversampling)); +*/ m_maxbin = int(round((double(m_maxbin) / m_oversampling) * oversampling)); + m_oversampling = oversampling; + + int h = getFFTSize() / 2 + 1; + if (m_minbin > h) m_minbin = h; + if (m_maxbin > h) m_maxbin = h; + SVDEBUG << m_minbin << " and " << m_maxbin << endl; - m_oversampling = oversampling; m_newFFTNeeded = true; emit layerParametersChanged(); @@ -376,9 +390,6 @@ { if (!m_sliceableModel) return 0; double bin = (freq * getFFTSize()) / m_sliceableModel->getSampleRate(); - // we assume the frequency of a bin corresponds to the centre of - // its visual range - bin += 0.5; return bin; } @@ -396,17 +407,6 @@ if (!m_sliceableModel) return 0; double fmin = getFrequencyForBin(m_minbin); - - if (m_binScale == LogBins && m_minbin == 0) { - // Avoid too much space going to the first bin, but do so in a - // way that usually avoids us shifting left/right as the - // window size or oversampling ratio change - i.e. base this - // on frequency rather than bin number unless we have a lot of - // very low-resolution content - fmin = getFrequencyForBin(0.8); - if (fmin > 6.0) fmin = 6.0; - } - double fmax = getFrequencyForBin(m_maxbin); double freq = getScalePointForX(v, x, fmin, fmax); @@ -417,9 +417,6 @@ SpectrumLayer::getFrequencyForBin(double bin) const { if (!m_sliceableModel) return 0; - // we assume the frequency of a bin corresponds to the centre of - // its visual range - bin -= 0.5; double freq = (bin * m_sliceableModel->getSampleRate()) / getFFTSize(); return freq; } @@ -438,13 +435,6 @@ if (!m_sliceableModel) return 0; double fmin = getFrequencyForBin(m_minbin); - - if (m_binScale == LogBins && m_minbin == 0) { - // See comment in getFrequencyForX above - fmin = getFrequencyForBin(0.8); - if (fmin > 6.0) fmin = 6.0; - } - double fmax = getFrequencyForBin(m_maxbin); double x = getXForScalePoint(v, freq, fmin, fmax);