changeset 159:c1fb771b7646

* Various improvements to colour 3d plot layer, particularly for large and/or dense plots. Still a work in progress
author Chris Cannam
date Fri, 06 Oct 2006 16:53:25 +0000
parents 9c3a4b42d8f8
children f4be20ebdfa4
files layer/Colour3DPlotLayer.cpp layer/Colour3DPlotLayer.h view/View.cpp
diffstat 3 files changed, 226 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/layer/Colour3DPlotLayer.cpp	Thu Oct 05 15:39:23 2006 +0000
+++ b/layer/Colour3DPlotLayer.cpp	Fri Oct 06 16:53:25 2006 +0000
@@ -32,7 +32,8 @@
 Colour3DPlotLayer::Colour3DPlotLayer() :
     Layer(),
     m_model(0),
-    m_cache(0)
+    m_cache(0),
+    m_colourScale(LinearScale)
 {
     
 }
@@ -74,6 +75,96 @@
     cacheInvalid();
 }
 
+Layer::PropertyList
+Colour3DPlotLayer::getProperties() const
+{
+    PropertyList list;
+    list.push_back("Colour Scale");
+    return list;
+}
+
+QString
+Colour3DPlotLayer::getPropertyLabel(const PropertyName &name) const
+{
+    if (name == "Colour Scale") return tr("Colour Scale");
+    return "";
+}
+
+Layer::PropertyType
+Colour3DPlotLayer::getPropertyType(const PropertyName &name) const
+{
+    return ValueProperty;
+}
+
+QString
+Colour3DPlotLayer::getPropertyGroupName(const PropertyName &name) const
+{
+    return QString();
+}
+
+int
+Colour3DPlotLayer::getPropertyRangeAndValue(const PropertyName &name,
+                                        int *min, int *max) const
+{
+    int deft = 0;
+
+    int garbage0, garbage1;
+    if (!min) min = &garbage0;
+    if (!max) max = &garbage1;
+
+    if (name == "Colour Scale") {
+
+	*min = 0;
+	*max = 3;
+
+	deft = (int)m_colourScale;
+
+    } else {
+	deft = Layer::getPropertyRangeAndValue(name, min, max);
+    }
+
+    return deft;
+}
+
+QString
+Colour3DPlotLayer::getPropertyValueLabel(const PropertyName &name,
+				    int value) const
+{
+    if (name == "Colour Scale") {
+	switch (value) {
+	default:
+	case 0: return tr("Linear");
+	case 1: return tr("Absolute");
+	case 2: return tr("Meter");
+	case 3: return tr("dB");
+	}
+    }
+    return tr("<unknown>");
+}
+
+void
+Colour3DPlotLayer::setProperty(const PropertyName &name, int value)
+{
+    if (name == "Colour Scale") {
+	switch (value) {
+	default:
+	case 0: setColourScale(LinearScale); break;
+	case 1: setColourScale(AbsoluteScale); break;
+	case 2: setColourScale(MeterScale); break;
+	case 3: setColourScale(dBScale); break;
+	}
+    }
+}
+
+void
+Colour3DPlotLayer::setColourScale(ColourScale scale)
+{
+    if (m_colourScale == scale) return;
+    m_colourScale = scale;
+    cacheInvalid();
+    emit layerParametersChanged();
+}
+
 bool
 Colour3DPlotLayer::isLayerScrollable(const View *v) const
 {
@@ -92,14 +183,20 @@
     size_t modelStart = m_model->getStartFrame();
     size_t modelResolution = m_model->getResolution();
 
+    float srRatio =
+        float(v->getViewManager()->getMainModelSampleRate()) /
+        float(m_model->getSampleRate());
+
     int sx0 = modelResolution *
-	int((v->getFrameForX(x) - long(modelStart)) / long(modelResolution));
+	int((v->getFrameForX(x) / srRatio - long(modelStart)) / long(modelResolution));
     int sx1 = sx0 + modelResolution;
 
     float binHeight = float(v->height()) / m_model->getYBinCount();
     int sy = (v->height() - y) / binHeight;
 
     float value = m_model->getBinValue(sx0, sy);
+
+//    std::cerr << "bin value (" << sx0 << "," << sy << ") is " << value << std::endl;
     
     QString binName = m_model->getBinName(sy);
     if (binName == "") binName = QString("[%1]").arg(sy + 1);
@@ -117,6 +214,13 @@
 }
 
 int
+Colour3DPlotLayer::getColourScaleWidth(QPainter &paint) const
+{
+    int cw = 20;
+    return cw;
+}
+
+int
 Colour3DPlotLayer::getVerticalScaleWidth(View *v, QPainter &paint) const
 {
     if (!m_model) return 0;
@@ -135,7 +239,7 @@
 	tw = std::max(tw, paint.fontMetrics().width(sampleText));
     }
 
-    return tw + 13;
+    return tw + 13 + getColourScaleWidth(paint);
 }
 
 void
@@ -146,6 +250,24 @@
     int h = rect.height(), w = rect.width();
     float binHeight = float(v->height()) / m_model->getYBinCount();
 
+    int cw = getColourScaleWidth(paint);
+    
+    int ch = h - 20;
+    if (ch > 20 && m_cache) {
+
+        paint.setPen(Qt::black);
+        paint.drawRect(4, 10, cw - 8, ch - 19);
+
+        for (int y = 0; y < ch - 20; ++y) {
+            QRgb c = m_cache->color(((ch - 20 - y) * 255) / (ch - 20));
+//            std::cerr << "y = " << y << ": rgb " << qRed(c) << "," << qGreen(c) << "," << qBlue(c) << std::endl;
+            paint.setPen(QColor(qRed(c), qGreen(c), qBlue(c)));
+            paint.drawLine(5, 11 + y, cw - 5, 11 + y);
+        }
+    }
+
+    paint.setPen(Qt::black);
+
     int count = v->height() / paint.fontMetrics().height();
     int step = m_model->getYBinCount() / count;
     if (step == 0) step = 1;
@@ -159,12 +281,12 @@
 	QString text = m_model->getBinName(i);
 	if (text == "") text = QString("[%1]").arg(i + 1);
 
-	paint.drawLine(0, y0, w, y0);
+	paint.drawLine(cw, y0, w, y0);
 
 	int cy = y0 - (step * binHeight)/2;
 	int ty = cy + paint.fontMetrics().ascent()/2;
 
-	paint.drawText(10, ty, text);
+	paint.drawText(cw + 5, ty, text);
     }
 }
 
@@ -176,17 +298,6 @@
     std::cerr << "Colour3DPlotLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << std::endl;
 #endif
 
-    //!!! This doesn't yet accommodate the fact that the model may
-    //have a different sample rate from an underlying model.  At the
-    //moment our paint mechanism assumes all models have the same
-    //sample rate.  If that isn't the case, they won't align and the
-    //time ruler will match whichever model was used to construct it.
-    //Obviously it is not going to be the case in general that models
-    //will have the same samplerate, so we need a pane samplerate as
-    //well which we trivially realign to.  (We can probably require
-    //the waveform and spectrogram layers to display at the pane
-    //samplerate.)
-
     int completion = 0;
     if (!m_model || !m_model->isOK() || !m_model->isReady(&completion)) {
 	if (completion > 0) {
@@ -211,10 +322,12 @@
 	m_cache = 0;
     }
 
-    if (!m_cache) {
+    if (!m_cache) { 
 
 	m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
 
+        std::cerr << "Cache size " << cacheWidth << "x" << cacheHeight << std::endl;
+
 	m_cache->setNumColors(256);
 	DenseThreeDimensionalModel::BinValueSet values;
 
@@ -223,13 +336,43 @@
 
 	if (max == min) max = min + 1.0;
 
-	for (int value = 0; value < 256; ++value) {
-	    int hue = 256 - value;
-	    QColor colour = QColor::fromHsv(hue, value/2 + 128, value);
-	    m_cache->setColor(value, qRgba(colour.red(), colour.green(), colour.blue(), 80));
+        int zeroIndex = 0;
+        if (min < 0.f) {
+            if (m_colourScale == LinearScale) {
+                zeroIndex = int(((-min) * 256) / (max - min));
+            } else {
+                max = std::max(-min, max);
+                min = 0;
+            }
+        }
+        if (zeroIndex < 0) zeroIndex = 0;
+        if (zeroIndex > 255) zeroIndex = 255;
+
+        //!!! want this and spectrogram to share a colour mapping unit
+
+	for (int index = 0; index < 256; ++index) {
+            int effective = abs(((index - zeroIndex) * 255) /
+                                std::max(255 - zeroIndex, zeroIndex));
+	    int hue = 256 - effective;
+            if (zeroIndex > 0) {
+                if (index <= zeroIndex) hue = 255;
+                else hue = 0;
+            }
+            while (hue < 0) hue += 255;
+            while (hue > 255) hue -= 255;
+            int saturation = effective / 2 + 128;
+            if (saturation < 0) saturation = -saturation;
+            if (saturation > 255) saturation = 255;
+            int value = effective;
+            if (value < 0) value = -value;
+            if (value > 255) value = 255;
+//            std::cerr << "min: " << min << ", max: " << max << ", zi " << zeroIndex << ", index " << index << ": " << hue << ", " << saturation << ", " << value << std::endl;
+	    QColor colour = QColor::fromHsv(hue, saturation, value);
+//            std::cerr << "rgb: " << colour.red() << "," << colour.green() << "," << colour.blue() << std::endl;
+	    m_cache->setColor(index, qRgb(colour.red(), colour.green(), colour.blue()));
 	}
 
-	m_cache->fill(min);
+	m_cache->fill(zeroIndex);
 
 	for (size_t f = modelStart; f <= modelEnd; f += modelResolution) {
 	
@@ -239,7 +382,12 @@
 	    for (size_t y = 0; y < m_model->getYBinCount(); ++y) {
 
 		float value = min;
-		if (y < values.size()) value = values[y];
+		if (y < values.size()) {
+                    value = values[y];
+                    if (m_colourScale != LinearScale) {
+                        value = fabs(value);
+                    }
+                }
 
 		int pixel = int(((value - min) * 256) / (max - min));
                 if (pixel < 0) pixel = 0;
@@ -250,7 +398,8 @@
 	}
     }
 
-    if (m_model->getYBinCount() >= v->height()) {
+    if (m_model->getYBinCount() >= v->height() ||
+        modelResolution < v->getZoomLevel() / 2) {
         paintDense(v, paint, rect);
         return;
     }
@@ -258,7 +407,6 @@
     int x0 = rect.left();
     int x1 = rect.right() + 1;
 
-    int w = x1 - x0;
     int h = v->height();
 
     // The cache is from the model's start frame to the model's end
@@ -271,9 +419,12 @@
     //direction.  This one is only really appropriate for models with
     //far fewer bins in both directions.
 
-    int sx0 = int((v->getFrameForX(x0) - long(modelStart)) / long(modelResolution));
-    int sx1 = int((v->getFrameForX(x1) - long(modelStart)) / long(modelResolution));
-    int sw = sx1 - sx0;
+    float srRatio =
+        float(v->getViewManager()->getMainModelSampleRate()) /
+        float(m_model->getSampleRate());
+
+    int sx0 = int((v->getFrameForX(x0) / srRatio - long(modelStart)) / long(modelResolution));
+    int sx1 = int((v->getFrameForX(x1) / srRatio - long(modelStart)) / long(modelResolution));
     int sh = m_model->getYBinCount();
 
 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
@@ -292,14 +443,14 @@
 	if (fx + modelResolution < int(modelStart) ||
 	    fx > int(modelEnd)) continue;
 
-	int rx0 = v->getXForFrame(fx + int(modelStart));
-	int rx1 = v->getXForFrame(fx + int(modelStart) + int(modelResolution));
+	int rx0 = v->getXForFrame((fx + int(modelStart)) * srRatio);
+	int rx1 = v->getXForFrame((fx + int(modelStart) + int(modelResolution) + 1) * srRatio);
 
-	int w = rx1 - rx0;
-	if (w < 1) w = 1;
+	int rw = rx1 - rx0;
+	if (rw < 1) rw = 1;
 
-	bool showLabel = (w > 10 &&
-			  paint.fontMetrics().width("0.000000") < w - 3 &&
+	bool showLabel = (rw > 10 &&
+			  paint.fontMetrics().width("0.000000") < rw - 3 &&
 			  paint.fontMetrics().height() < (h / sh));
         
 	for (int sy = 0; sy < sh; ++sy) {
@@ -311,14 +462,25 @@
 		pixel = m_cache->pixel(sx, sy);
 	    }
 
+	    QRect r(rx0, ry0 - h / sh - 1, rw, h / sh + 1);
+
+            if (rw == 1) {
+                paint.setPen(pixel);
+                paint.setBrush(Qt::NoBrush);
+                paint.drawLine(r.x(), r.y(), r.x(), r.y() + r.height() - 1);
+                continue;
+            }
+
 	    QColor pen(255, 255, 255, 80);
 	    QColor brush(pixel);
-	    brush.setAlpha(160);
+
+            if (rw > 3 && r.height() > 3) {
+                brush.setAlpha(160);
+            }
+
 	    paint.setPen(Qt::NoPen);
 	    paint.setBrush(brush);
 
-	    QRect r(rx0, ry0 - h / sh - 1, w, h / sh + 1);
-
 	    if (illuminate) {
 		if (r.contains(illuminatePos)) {
 		    paint.setPen(Qt::black);//!!!
@@ -326,8 +488,8 @@
 	    }
             
 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
-            std::cerr << "rect " << rx0 << "," << (ry0 - h / sh - 1) << " "
-                      << w << "x" << (h / sh + 1) << std::endl;
+            std::cerr << "rect " << r.x() << "," << r.y() << " "
+                      << r.width() << "x" << r.height() << std::endl;
 #endif
 
 	    paint.drawRect(r);
@@ -358,6 +520,10 @@
     size_t modelEnd = m_model->getEndFrame();
     size_t modelResolution = m_model->getResolution();
 
+    float srRatio =
+        float(v->getViewManager()->getMainModelSampleRate()) /
+        float(m_model->getSampleRate());
+
     int x0 = rect.left();
     int x1 = rect.right() + 1;
 
@@ -369,7 +535,7 @@
 
     for (int x = x0; x < x1; ++x) {
 
-        long xf = v->getFrameForX(x);
+        long xf = v->getFrameForX(x) / srRatio;
         if (xf < 0) {
             for (int y = 0; y < h; ++y) {
                 img.setPixel(x - x0, y, m_cache->color(0));
@@ -378,7 +544,7 @@
         }
 
         float sx0 = (float(xf) - modelStart) / modelResolution;
-        float sx1 = (float(v->getFrameForX(x+1)) - modelStart) / modelResolution;
+        float sx1 = (float(v->getFrameForX(x+1) / srRatio) - modelStart) / modelResolution;
             
         int sx0i = int(sx0 + 0.001);
         int sx1i = int(sx1);
@@ -392,6 +558,7 @@
             int sy1i = int(sy1);
 
             float mag = 0.0, div = 0.0;
+            int max = 0;
 
             for (int sx = sx0i; sx <= sx1i; ++sx) {
 
@@ -408,6 +575,7 @@
                     if (sy == sy1i) prop *= sy1 - sy;
 
                     mag += prop * m_cache->pixelIndex(sx, sy);
+                    max = std::max(max, m_cache->pixelIndex(sx, sy));
                     div += prop;
                 }
             }
@@ -415,8 +583,11 @@
             if (div != 0) mag /= div;
             if (mag < 0) mag = 0;
             if (mag > 255) mag = 255;
+            if (max < 0) max = 0;
+            if (max > 255) max = 255;
 
             img.setPixel(x - x0, y, m_cache->color(int(mag + 0.001)));
+//            img.setPixel(x - x0, y, m_cache->color(max));
         }
     }
 
--- a/layer/Colour3DPlotLayer.h	Thu Oct 05 15:39:23 2006 +0000
+++ b/layer/Colour3DPlotLayer.h	Fri Oct 06 16:53:25 2006 +0000
@@ -68,19 +68,24 @@
 
     virtual bool getValueExtents(float &, float &, bool &, QString &) const { return false; }
 
-    virtual QString getPropertyLabel(const PropertyName &) const { return ""; }
-/*
     virtual PropertyList getProperties() const;
     virtual PropertyType getPropertyType(const PropertyName &) const;
+    virtual QString getPropertyLabel(const PropertyName &) const;
+    virtual QString getPropertyGroupName(const PropertyName &) const;
     virtual int getPropertyRangeAndValue(const PropertyName &,
-					   int *min, int *max) const;
+                                         int *min, int *max) const;
     virtual QString getPropertyValueLabel(const PropertyName &,
 					  int value) const;
     virtual void setProperty(const PropertyName &, int value);
-*/
 
     void setProperties(const QXmlAttributes &) { }
     
+    //!!! harmonize with spectrogram
+    enum ColourScale { LinearScale, AbsoluteScale, MeterScale, dBScale };
+
+    void setColourScale(ColourScale);
+    ColourScale getColourScale() const { return m_colourScale; }
+
 protected slots:
     void cacheInvalid();
     void cacheInvalid(size_t startFrame, size_t endFrame);
@@ -90,6 +95,9 @@
     
     mutable QImage *m_cache;
 
+    ColourScale m_colourScale;
+
+    virtual int getColourScaleWidth(QPainter &) const;
     virtual void paintDense(View *v, QPainter &paint, QRect rect) const;
 };
 
--- a/view/View.cpp	Thu Oct 05 15:39:23 2006 +0000
+++ b/view/View.cpp	Fri Oct 06 16:53:25 2006 +0000
@@ -908,6 +908,8 @@
     // multiple samplerates, we'd probably want to do frame/time
     // conversion in the model
 
+    //!!! nah, this wants to always return the sr of the main model!
+
     for (LayerList::const_iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
 	if ((*i)->getModel() && (*i)->getModel()->isOK()) {
 	    return (*i)->getModel()->getSampleRate();