changeset 1400:decb7741d036

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.
author Chris Cannam
date Thu, 15 Nov 2018 13:06:38 +0000
parents ba1f0234efa7
children 28075cc658c9
files layer/SliceLayer.cpp layer/SliceLayer.h layer/SpectrumLayer.cpp
diffstat 3 files changed, 89 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- 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<QPoint> 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;
--- 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;
--- 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);