diff 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
line wrap: on
line diff
--- a/layer/SliceLayer.cpp	Fri Jan 27 13:19:21 2017 +0000
+++ b/layer/SliceLayer.cpp	Tue Feb 07 14:55:19 2017 +0000
@@ -40,6 +40,8 @@
     m_threshold(0.0),
     m_initialThreshold(0.0),
     m_gain(1.0),
+    m_minbin(0),
+    m_maxbin(0),
     m_currentf0(0),
     m_currentf1(0)
 {
@@ -67,7 +69,11 @@
 
     connectSignals(m_sliceableModel);
 
+    m_minbin = 0;
+    m_maxbin = m_sliceableModel->getHeight();
+    
     emit modelReplaced();
+    emit layerParametersChanged();
 }
 
 void
@@ -107,13 +113,10 @@
     maxbin = 0;
     if (!m_sliceableModel) return "";
 
-    int xorigin = m_xorigins[v];
-    int w = v->getPaintWidth() - xorigin - 1;
-    
+    minbin = int(round(getBinForX(v, p.x())));
+    maxbin = int(round(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 +136,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 +186,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:
@@ -198,15 +215,27 @@
         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;
+    
     switch (m_binScale) {
 
     case LinearBins:
@@ -222,20 +251,20 @@
         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;
@@ -276,14 +305,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;
@@ -341,20 +370,26 @@
     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();
@@ -388,7 +423,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;
@@ -412,18 +447,18 @@
         }
     }
 
-    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) {
 
@@ -461,60 +496,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
@@ -630,7 +625,7 @@
 	*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;
@@ -897,7 +892,7 @@
                  "binScale=\"%5\" "
                  "gain=\"%6\" "
                  "threshold=\"%7\" "
-                 "normalize=\"%8\"")
+                 "normalize=\"%8\" %9")
         .arg(m_colourMap)
 	.arg(m_energyScale)
         .arg(m_samplingMode)
@@ -905,7 +900,11 @@
         .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);
 }
@@ -944,11 +943,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(), "");
+}