changeset 197:6b023411087b

* Work on harmonising colour and scale ranges between types of layer * Add normalize options to colour 3d plot layer
author Chris Cannam
date Thu, 01 Feb 2007 14:31:28 +0000
parents 22c99c8aa1e0
children c2ed5014d4ff
files layer/Colour3DPlotLayer.cpp layer/Colour3DPlotLayer.h layer/ColourMapper.cpp layer/ColourMapper.h layer/Layer.h layer/NoteLayer.cpp layer/SliceLayer.cpp layer/SliceLayer.h layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h layer/TimeValueLayer.cpp layer/TimeValueLayer.h view/View.cpp view/View.h widgets/PropertyBox.cpp widgets/PropertyBox.h widgets/PropertyStack.cpp widgets/PropertyStack.h
diffstat 18 files changed, 666 insertions(+), 257 deletions(-) [+]
line wrap: on
line diff
--- a/layer/Colour3DPlotLayer.cpp	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/Colour3DPlotLayer.cpp	Thu Feb 01 14:31:28 2007 +0000
@@ -17,6 +17,8 @@
 
 #include "view/View.h"
 #include "base/Profiler.h"
+#include "base/LogRange.h"
+#include "ColourMapper.h"
 
 #include <QPainter>
 #include <QImage>
@@ -32,7 +34,11 @@
 Colour3DPlotLayer::Colour3DPlotLayer() :
     m_model(0),
     m_cache(0),
-    m_colourScale(LinearScale)
+    m_cacheStart(0),
+    m_colourScale(LinearScale),
+    m_colourMap(0),
+    m_normalizeColumns(false),
+    m_normalizeVisibleArea(false)
 {
     
 }
@@ -81,26 +87,37 @@
 Colour3DPlotLayer::getProperties() const
 {
     PropertyList list;
+    list.push_back("Colour");
     list.push_back("Colour Scale");
+    list.push_back("Normalize Columns");
+    list.push_back("Normalize Visible Area");
     return list;
 }
 
 QString
 Colour3DPlotLayer::getPropertyLabel(const PropertyName &name) const
 {
-    if (name == "Colour Scale") return tr("Colour Scale");
+    if (name == "Colour") return tr("Colour");
+    if (name == "Colour Scale") return tr("Scale");
+    if (name == "Normalize Columns") return tr("Normalize Columns");
+    if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
     return "";
 }
 
 Layer::PropertyType
 Colour3DPlotLayer::getPropertyType(const PropertyName &name) const
 {
+    if (name == "Normalize Columns") return ToggleProperty;
+    if (name == "Normalize Visible Area") return ToggleProperty;
     return ValueProperty;
 }
 
 QString
 Colour3DPlotLayer::getPropertyGroupName(const PropertyName &name) const
 {
+    if (name == "Normalize Columns" ||
+        name == "Normalize Visible Area" ||
+	name == "Colour Scale") return tr("Scale");
     return QString();
 }
 
@@ -117,10 +134,25 @@
     if (name == "Colour Scale") {
 
 	*min = 0;
-	*max = 3;
+	*max = 2;
 
 	deft = (int)m_colourScale;
 
+    } else if (name == "Colour") {
+
+	*min = 0;
+	*max = ColourMapper::getColourMapCount() - 1;
+
+	deft = m_colourMap;
+
+    } else if (name == "Normalize Columns") {
+	
+	deft = (m_normalizeColumns ? 1 : 0);
+
+    } else if (name == "Normalize Visible Area") {
+	
+	deft = (m_normalizeVisibleArea ? 1 : 0);
+
     } else {
 	deft = Layer::getPropertyRangeAndValue(name, min, max);
     }
@@ -132,13 +164,15 @@
 Colour3DPlotLayer::getPropertyValueLabel(const PropertyName &name,
 				    int value) const
 {
+    if (name == "Colour") {
+        return ColourMapper::getColourMapName(value);
+    }
     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");
+	case 0: return tr("Linear Scale");
+	case 1: return tr("Log Scale");
+	case 2: return tr("+/-1 Scale");
 	}
     }
     return tr("<unknown>");
@@ -151,10 +185,15 @@
 	switch (value) {
 	default:
 	case 0: setColourScale(LinearScale); break;
-	case 1: setColourScale(AbsoluteScale); break;
-	case 2: setColourScale(MeterScale); break;
-	case 3: setColourScale(dBScale); break;
+	case 1: setColourScale(LogScale); break;
+	case 2: setColourScale(PlusMinusOneScale); break;
 	}
+    } else if (name == "Colour") {
+        setColourMap(value);
+    } else if (name == "Normalize Columns") {
+	setNormalizeColumns(value ? true : false);
+    } else if (name == "Normalize Visible Area") {
+	setNormalizeVisibleArea(value ? true : false);
     }
 }
 
@@ -167,9 +206,49 @@
     emit layerParametersChanged();
 }
 
+void
+Colour3DPlotLayer::setColourMap(int map)
+{
+    if (m_colourMap == map) return;
+    m_colourMap = map;
+    cacheInvalid();
+    emit layerParametersChanged();
+}
+
+void
+Colour3DPlotLayer::setNormalizeColumns(bool n)
+{
+    if (m_normalizeColumns == n) return;
+    m_normalizeColumns = n;
+    cacheInvalid();
+    emit layerParametersChanged();
+}
+
+bool
+Colour3DPlotLayer::getNormalizeColumns() const
+{
+    return m_normalizeColumns;
+}
+
+void
+Colour3DPlotLayer::setNormalizeVisibleArea(bool n)
+{
+    if (m_normalizeVisibleArea == n) return;
+    m_normalizeVisibleArea = n;
+    cacheInvalid();
+    emit layerParametersChanged();
+}
+
+bool
+Colour3DPlotLayer::getNormalizeVisibleArea() const
+{
+    return m_normalizeVisibleArea;
+}
+
 bool
 Colour3DPlotLayer::isLayerScrollable(const View *v) const
 {
+    if (m_normalizeVisibleArea) return false;
     QPoint discard;
     return !v->shouldIlluminateLocalFeatures(this, discard);
 }
@@ -295,6 +374,155 @@
 }
 
 void
+Colour3DPlotLayer::getColumn(size_t col,
+                             DenseThreeDimensionalModel::Column &values) const
+{
+    m_model->getColumn(col, values);
+
+    float colMax = 0.f;
+
+    float min = 0.f, max = 0.f;
+    if (m_normalizeColumns) {
+        min = m_model->getMinimumLevel();
+        max = m_model->getMaximumLevel();
+    }
+
+    if (m_normalizeColumns) {
+        for (size_t y = 0; y < values.size(); ++y) {
+            if (values[y] > colMax || y == 0) colMax = values[y];
+        }
+        if (m_colourScale == LogScale) {
+            colMax = LogRange::map(colMax);
+        }
+    }
+    
+    for (size_t y = 0; y < values.size(); ++y) {
+        
+        float value = min;
+
+        value = values[y];
+        if (m_colourScale == LogScale) {
+            value = LogRange::map(value);
+        }
+
+        if (m_normalizeColumns) {
+            if (colMax != 0) {
+                value = max * (value / colMax);
+            } else {
+                value = 0;
+            }
+        }
+
+        values[y] = value;
+    }    
+}
+    
+void
+Colour3DPlotLayer::fillCache(size_t firstBin, size_t lastBin) const
+{
+    size_t modelStart = m_model->getStartFrame();
+    size_t modelEnd = m_model->getEndFrame();
+    size_t modelResolution = m_model->getResolution();
+
+    std::cerr << "Colour3DPlotLayer::fillCache: " << firstBin << " -> " << lastBin << std::endl;
+
+    if (!m_normalizeVisibleArea || m_normalizeColumns) {
+        firstBin = modelStart / modelResolution;
+        lastBin = modelEnd / modelResolution;
+    }
+
+    size_t cacheWidth = lastBin - firstBin + 1;
+    size_t cacheHeight = m_model->getHeight();
+
+    if (m_cache &&
+	(m_cacheStart != firstBin ||
+         m_cache->width() != cacheWidth ||
+	 m_cache->height() != cacheHeight)) {
+
+	delete m_cache;
+	m_cache = 0;
+    }
+
+    if (m_cache) return;
+
+    m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
+    m_cacheStart = firstBin;
+
+    std::cerr << "Cache size " << cacheWidth << "x" << cacheHeight << " starting " << m_cacheStart << std::endl;
+
+    m_cache->setNumColors(256);
+    DenseThreeDimensionalModel::Column values;
+
+    float min = m_model->getMinimumLevel();
+    float max = m_model->getMaximumLevel();
+
+    if (m_colourScale == LogScale) {
+        LogRange::mapRange(min, max);
+    } else if (m_colourScale == PlusMinusOneScale) {
+        min = -1.f;
+        max = 1.f;
+    }
+    
+    if (max == min) max = min + 1.0;
+    
+    ColourMapper mapper(m_colourMap, 0.f, 255.f);
+    
+    for (int index = 0; index < 256; ++index) {
+        
+        QColor colour = mapper.map(index);
+        m_cache->setColor(index, qRgb(colour.red(), colour.green(), colour.blue()));
+    }
+    
+    m_cache->fill(0);
+
+    float visibleMax = 0.f;
+
+    if (m_normalizeVisibleArea && !m_normalizeColumns) {
+        
+        for (size_t c = firstBin; c <= lastBin; ++c) {
+	
+            values.clear();
+            getColumn(c, values);
+
+            float colMax = 0.f;
+
+            for (size_t y = 0; y < m_model->getHeight(); ++y) {
+                if (y >= values.size()) break;
+                if (y == 0 || values[y] > colMax) colMax = values[y];
+            }
+
+            if (c == firstBin || colMax > visibleMax) visibleMax = colMax;
+        }
+    }
+    
+    for (size_t c = firstBin; c <= lastBin; ++c) {
+	
+        values.clear();
+        getColumn(c, values);
+
+        for (size_t y = 0; y < m_model->getHeight(); ++y) {
+
+            float value = min;
+            if (y < values.size()) {
+                value = values[y];
+            }
+            
+            if (m_normalizeVisibleArea && !m_normalizeColumns) {
+                if (visibleMax != 0) {
+                    value = max * (value / visibleMax);
+                }
+            }
+
+            int pixel = int(((value - min) * 256) / (max - min));
+            if (pixel < 0) pixel = 0;
+            if (pixel > 255) pixel = 255;
+
+            m_cache->setPixel(c - firstBin, y, pixel);
+        }
+    }
+}
+
+void
 Colour3DPlotLayer::paint(View *v, QPainter &paint, QRect rect) const
 {
 //    Profiler profiler("Colour3DPlotLayer::paint");
@@ -311,96 +539,39 @@
 	return;
     }
 
+    if (m_normalizeVisibleArea && !m_normalizeColumns) rect = v->rect();
+
     size_t modelStart = m_model->getStartFrame();
     size_t modelEnd = m_model->getEndFrame();
     size_t modelResolution = m_model->getResolution();
 
-    size_t cacheWidth = (modelEnd - modelStart) / modelResolution + 1;
-    size_t cacheHeight = m_model->getHeight();
+    // The cache is from the model's start frame to the model's end
+    // frame at the model's window increment frames per pixel.  We
+    // want to draw from our start frame + x0 * zoomLevel to our start
+    // frame + x1 * zoomLevel at zoomLevel frames per pixel.
 
-    if (m_cache &&
-	(m_cache->width() != cacheWidth ||
-	 m_cache->height() != cacheHeight)) {
+    //  We have quite different paint mechanisms for rendering "large"
+    //  bins (more than one bin per pixel in both directions) and
+    //  "small".  This is "large"; see paintDense below for "small".
 
-	delete m_cache;
-	m_cache = 0;
-    }
+    int x0 = rect.left();
+    int x1 = rect.right() + 1;
 
-    if (!m_cache) { 
+    int h = v->height();
 
-	m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
+    float srRatio =
+        float(v->getViewManager()->getMainModelSampleRate()) /
+        float(m_model->getSampleRate());
 
-        std::cerr << "Cache size " << cacheWidth << "x" << cacheHeight << std::endl;
+    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->getHeight();
 
-	m_cache->setNumColors(256);
-	DenseThreeDimensionalModel::Column values;
-
-	float min = m_model->getMinimumLevel();
-	float max = m_model->getMaximumLevel();
-
-	if (max == min) max = min + 1.0;
-
-        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(zeroIndex);
-
-	for (size_t f = modelStart; f <= modelEnd; f += modelResolution) {
-	
-	    values.clear();
-	    m_model->getColumn(f / modelResolution, values);
-	    
-	    for (size_t y = 0; y < m_model->getHeight(); ++y) {
-
-		float value = min;
-		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;
-		if (pixel > 255) pixel = 255;
-
-		m_cache->setPixel(f / modelResolution, y, pixel);
-	    }
-	}
-    }
+    if (sx0 > 0) --sx0;
+    fillCache(sx0 < 0 ? 0 : sx0,
+              sx1 < 0 ? 0 : sx1);
 
     if (m_model->getHeight() >= v->height() ||
         modelResolution < v->getZoomLevel() / 2) {
@@ -408,29 +579,6 @@
         return;
     }
 
-    int x0 = rect.left();
-    int x1 = rect.right() + 1;
-
-    int h = v->height();
-
-    // The cache is from the model's start frame to the model's end
-    // frame at the model's window increment frames per pixel.  We
-    // want to draw from our start frame + x0 * zoomLevel to our start
-    // frame + x1 * zoomLevel at zoomLevel frames per pixel.
-
-    //!!! Strictly speaking we want quite different paint mechanisms
-    //for models that have more than one bin per pixel in either
-    //direction.  This one is only really appropriate for models with
-    //far fewer bins in both directions.
-
-    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->getHeight();
-
 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
     std::cerr << "Colour3DPlotLayer::paint: w " << w << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sw << ", sh " << sh << std::endl;
     std::cerr << "Colour3DPlotLayer: sample rate is " << m_model->getSampleRate() << ", resolution " << m_model->getResolution() << std::endl;
@@ -440,8 +588,11 @@
     bool illuminate = v->shouldIlluminateLocalFeatures(this, illuminatePos);
     char labelbuf[10];
 
-    for (int sx = sx0 - 1; sx <= sx1; ++sx) {
+    for (int sx = sx0; sx <= sx1; ++sx) {
 
+        int scx = 0;
+        if (sx > m_cacheStart) scx = sx - m_cacheStart;
+        
 	int fx = sx * int(modelResolution);
 
 	if (fx + modelResolution < int(modelStart) ||
@@ -461,9 +612,9 @@
 
 	    int ry0 = h - (sy * h) / sh - 1;
 	    QRgb pixel = qRgb(255, 255, 255);
-	    if (sx >= 0 && sx < m_cache->width() &&
+	    if (scx >= 0 && scx < m_cache->width() &&
 		sy >= 0 && sy < m_cache->height()) {
-		pixel = m_cache->pixel(sx, sy);
+		pixel = m_cache->pixel(scx, sy);
 	    }
 
 	    QRect r(rx0, ry0 - h / sh - 1, rw, h / sh + 1);
@@ -499,9 +650,9 @@
 	    paint.drawRect(r);
 
 	    if (showLabel) {
-		if (sx >= 0 && sx < m_cache->width() &&
+		if (scx >= 0 && scx < m_cache->width() &&
 		    sy >= 0 && sy < m_cache->height()) {
-		    float value = m_model->getValueAt(sx, sy);
+		    float value = m_model->getValueAt(scx, sy);
 		    sprintf(labelbuf, "%06f", value);
 		    QString text(labelbuf);
 		    paint.setPen(Qt::white);
@@ -566,7 +717,10 @@
 
             for (int sx = sx0i; sx <= sx1i; ++sx) {
 
-                if (sx < 0 || sx >= m_cache->width()) continue;
+                int scx = 0;
+                if (sx > m_cacheStart) scx = sx - m_cacheStart;
+        
+                if (scx < 0 || scx >= m_cache->width()) continue;
 
                 for (int sy = sy0i; sy <= sy1i; ++sy) {
 
@@ -578,8 +732,8 @@
                     if (sy == sy0i) prop *= (sy + 1) - sy0;
                     if (sy == sy1i) prop *= sy1 - sy;
 
-                    mag += prop * m_cache->pixelIndex(sx, sy);
-                    max = std::max(max, m_cache->pixelIndex(sx, sy));
+                    mag += prop * m_cache->pixelIndex(scx, sy);
+                    max = std::max(max, m_cache->pixelIndex(scx, sy));
                     div += prop;
                 }
             }
@@ -624,6 +778,43 @@
     return true;
 }
 
+QString
+Colour3DPlotLayer::toXmlString(QString indent, QString extraAttributes) const
+{
+    QString s;
+    
+    s += QString("scale=\"%1\" "
+                 "colourScheme=\"%2\" "
+		 "normalizeColumns=\"%3\" "
+                 "normalizeVisibleArea=\"%4\"")
+	.arg((int)m_colourScale)
+        .arg(m_colourMap)
+        .arg(m_normalizeColumns ? "true" : "false")
+        .arg(m_normalizeVisibleArea ? "true" : "false");
+
+    return Layer::toXmlString(indent, extraAttributes + " " + s);
+}
+
+void
+Colour3DPlotLayer::setProperties(const QXmlAttributes &attributes)
+{
+    bool ok = false;
+
+    ColourScale scale = (ColourScale)attributes.value("scale").toInt(&ok);
+    if (ok) setColourScale(scale);
+
+    int colourMap = attributes.value("colourScheme").toInt(&ok);
+    if (ok) setColourMap(colourMap);
+
+    bool normalizeColumns =
+        (attributes.value("normalizeColumns").trimmed() == "true");
+    setNormalizeColumns(normalizeColumns);
+
+    bool normalizeVisibleArea =
+        (attributes.value("normalizeVisibleArea").trimmed() == "true");
+    setNormalizeVisibleArea(normalizeVisibleArea);
+}
+
 #ifdef INCLUDE_MOCFILES
 #include "Colour3DPlotLayer.moc.cpp"
 #endif
--- a/layer/Colour3DPlotLayer.h	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/Colour3DPlotLayer.h	Thu Feb 01 14:31:28 2007 +0000
@@ -79,17 +79,27 @@
     virtual QString getPropertyValueLabel(const PropertyName &,
 					  int value) const;
     virtual void setProperty(const PropertyName &, int value);
-
-    void setProperties(const QXmlAttributes &) { }
+    virtual void setProperties(const QXmlAttributes &);
     
-    //!!! harmonize with spectrogram
-    enum ColourScale { LinearScale, AbsoluteScale, MeterScale, dBScale };
+    enum ColourScale { LinearScale, LogScale, PlusMinusOneScale };
 
     void setColourScale(ColourScale);
     ColourScale getColourScale() const { return m_colourScale; }
 
+    void setColourMap(int map);
+    int getColourMap() const;
+
+    void setNormalizeColumns(bool n);
+    bool getNormalizeColumns() const;
+
+    void setNormalizeVisibleArea(bool n);
+    bool getNormalizeVisibleArea() const;
+
     virtual const Model *getSliceableModel() const { return m_model; }
 
+    virtual QString toXmlString(QString indent = "",
+				QString extraAttributes = "") const;
+
 protected slots:
     void cacheInvalid();
     void cacheInvalid(size_t startFrame, size_t endFrame);
@@ -98,10 +108,17 @@
     const DenseThreeDimensionalModel *m_model; // I do not own this
     
     mutable QImage *m_cache;
+    mutable size_t m_cacheStart;
 
     ColourScale m_colourScale;
+    int         m_colourMap;
+    bool        m_normalizeColumns;
+    bool        m_normalizeVisibleArea;
+
+    void getColumn(size_t col, DenseThreeDimensionalModel::Column &) const;
 
     virtual int getColourScaleWidth(QPainter &) const;
+    virtual void fillCache(size_t firstBin, size_t lastBin) const;
     virtual void paintDense(View *v, QPainter &paint, QRect rect) const;
 };
 
--- a/layer/ColourMapper.cpp	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/ColourMapper.cpp	Thu Feb 01 14:31:28 2007 +0000
@@ -15,12 +15,19 @@
 
 #include "ColourMapper.h"
 
+#include <iostream>
+
 ColourMapper::ColourMapper(int map, float min, float max) :
     QObject(),
     m_map(map),
     m_min(min),
     m_max(max)
 {
+    if (m_min == m_max) {
+        std::cerr << "WARNING: ColourMapper: min == max (== " << m_min
+                  << "), adjusting" << std::endl;
+        m_max = m_min + 1;
+    }
 }
 
 ColourMapper::~ColourMapper()
@@ -30,13 +37,13 @@
 int
 ColourMapper::getColourMapCount()
 {
-    return 8;
+    return 10;
 }
 
 QString
 ColourMapper::getColourMapName(int n)
 {
-    if (n >= 8) return tr("<unknown>");
+    if (n >= getColourMapCount()) return tr("<unknown>");
     StandardMap map = (StandardMap)n;
 
     switch (map) {
@@ -48,6 +55,8 @@
     case BlueOnBlack:      return tr("Blue on Black");
     case Sunset:           return tr("Sunset");
     case FruitSalad:       return tr("Fruit Salad");
+    case Banded:           return tr("Banded");
+    case Highlight:        return tr("Highlight");
     }
 
     return tr("<unknown>");
@@ -65,7 +74,7 @@
 
     float red = 0.f, green = 0.3333f, blue = 0.6666f, pieslice = 0.3333f;
 
-    if (m_map >= 8) return Qt::black;
+    if (m_map >= getColourMapCount()) return Qt::black;
     StandardMap map = (StandardMap)m_map;
 
     switch (map) {
@@ -99,12 +108,14 @@
         break;
 
     case BlueOnBlack:
-        h = blue / 1.f;
+        h = blue;
         s = 1.f;
         v = norm * 2.f;
-        if (v > 1) {
+        if (v > 1.f) {
             v = 1.f;
-            s = 1.f - (sqrtf(norm) - 0.707f) * 3.414f;
+            s = 1.f - (sqrtf(norm) - 0.707f) * 3.413f;
+            if (s < 0.f) s = 0.f;
+            if (s > 1.f) s = 1.f;
         }
         break;
 
@@ -123,11 +134,26 @@
         break;
 
     case FruitSalad:
-        h = blue + (pieslice/2.f) - norm;
+        h = blue + (pieslice/6.f) - norm;
         if (h < 0.f) h += 1.f;
         s = 1.f;
         v = 1.f;
         break;
+
+    case Banded:
+        if      (norm < 0.125) return Qt::darkGreen;
+        else if (norm < 0.25)  return Qt::green;
+        else if (norm < 0.375) return Qt::darkBlue;
+        else if (norm < 0.5)   return Qt::blue;
+        else if (norm < 0.625) return Qt::darkYellow;
+        else if (norm < 0.75)  return Qt::yellow;
+        else if (norm < 0.875) return Qt::darkRed;
+        else                   return Qt::red;
+        break;
+
+    case Highlight:
+        if (norm > 0.99) return Qt::white;
+        else return Qt::darkBlue;
     }
 
     if (hsv) {
@@ -140,7 +166,7 @@
 QColor
 ColourMapper::getContrastingColour() const
 {
-    if (m_map >= 8) return Qt::white;
+    if (m_map >= getColourMapCount()) return Qt::white;
     StandardMap map = (StandardMap)m_map;
 
     switch (map) {
@@ -168,6 +194,12 @@
 
     case FruitSalad:
         return Qt::white;
+
+    case Banded:
+        return Qt::cyan;
+
+    case Highlight:
+        return Qt::red;
     }
 
     return Qt::white;
--- a/layer/ColourMapper.h	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/ColourMapper.h	Thu Feb 01 14:31:28 2007 +0000
@@ -38,7 +38,9 @@
         RedOnBlue,
         YellowOnBlack,
         BlueOnBlack,
-        FruitSalad
+        FruitSalad,
+        Banded,
+        Highlight
     };
 
     int getMap() const { return m_map; }
--- a/layer/Layer.h	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/Layer.h	Thu Feb 01 14:31:28 2007 +0000
@@ -357,6 +357,7 @@
     void modelReplaced();
 
     void layerParametersChanged();
+    void layerParameterRangesChanged();
     void layerNameChanged();
 
     void verticalZoomChanged();
--- a/layer/NoteLayer.cpp	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/NoteLayer.cpp	Thu Feb 01 14:31:28 2007 +0000
@@ -19,6 +19,7 @@
 #include "base/RealTime.h"
 #include "base/Profiler.h"
 #include "base/Pitch.h"
+#include "base/LogRange.h"
 #include "view/View.h"
 
 #include "data/model/NoteModel.h"
@@ -485,8 +486,7 @@
 
         } else if (log) {
 
-            min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
-            max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+            LogRange::mapRange(min, max);
 
             std::cerr << "NoteLayer[" << this << "]::getScaleExtents: min = " << min << ", max = " << max << ", log = " << log << std::endl;
 
@@ -506,8 +506,7 @@
         }
 
         if (m_verticalScale == LogScale || m_verticalScale == MIDIRangeScale) {
-            min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
-            max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+            LogRange::mapRange(min, max);
             log = true;
         }
     }
@@ -533,7 +532,7 @@
     }
 
     if (logarithmic) {
-        val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val);
+        val = LogRange::map(val);
 //        std::cerr << "logarithmic true, val now = " << val << std::endl;
     }
 
@@ -554,7 +553,7 @@
     float val = min + (float(h - y) * float(max - min)) / h;
 
     if (logarithmic) {
-        val = pow(10, val);
+        val = powf(10.f, val);
     }
 
     if (shouldConvertMIDIToHz()) {
--- a/layer/SliceLayer.cpp	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/SliceLayer.cpp	Thu Feb 01 14:31:28 2007 +0000
@@ -21,6 +21,7 @@
 #include "base/RangeMapper.h"
 #include "base/RealTime.h"
 
+#include "ColourMapper.h"
 #include "PaintAssistant.h"
 
 #include <QPainter>
@@ -29,6 +30,7 @@
 SliceLayer::SliceLayer() :
     m_sliceableModel(0),
     m_colour(Qt::darkBlue),
+    m_colourMap(0),
     m_energyScale(dBScale),
     m_samplingMode(SamplePeak),
     m_plotStyle(PlotSteps),
@@ -175,6 +177,8 @@
     float py = 0;
     float nx = xorigin;
 
+    ColourMapper mapper(m_colourMap, 0, 1);
+
     for (size_t bin = 0; bin < mh; ++bin) {
 
         float x;
@@ -202,6 +206,7 @@
         float value = values[bin];
 
         value *= m_gain;
+        float norm = 0.f;
         float y = 0.f;
  
         switch (m_energyScale) {
@@ -211,16 +216,19 @@
             float db = thresh;
             if (value > 0.f) db = 10.f * log10f(value);
             if (db < thresh) db = thresh;
-            float val = (db - thresh) / -thresh;
-            y = yorigin - (float(h) * val);
+            norm = (db - thresh) / -thresh;
+            y = yorigin - (float(h) * norm);
             break;
         }
 
         case MeterScale:
-            y = yorigin - AudioLevel::multiplier_to_preview(value, h);
+            y = AudioLevel::multiplier_to_preview(value, h);
+            norm = float(y) / float(h);
+            y = yorigin - y;
             break;
 
         default:
+            norm = value;
             y = yorigin - (float(h) * value);
             break;
         }
@@ -249,15 +257,24 @@
             path.lineTo(nx, y);
             path.lineTo(nx, yorigin);
             path.lineTo(x, yorigin);
+
+        } else if (m_plotStyle == PlotFilledBlocks) {
+
+            paint.fillRect(QRectF(x, y, nx - x, yorigin - y), mapper.map(norm));
         }
 
         py = y;
     }
 
-    paint.drawPath(path);
+    if (m_plotStyle != PlotFilledBlocks) {
+        paint.drawPath(path);
+    }
     paint.restore();
 
-    if (v->getViewManager() && v->getViewManager()->shouldShowFrameCount()) {
+    QPoint discard;
+
+    if (v->getViewManager() && v->getViewManager()->shouldShowFrameCount() &&
+        v->shouldIlluminateLocalFeatures(this, discard)) {
 
         int sampleRate = m_sliceableModel->getSampleRate();
 
@@ -435,15 +452,25 @@
 
     } else if (name == "Colour") {
 
-	*min = 0;
-	*max = 5;
+        if (m_plotStyle == PlotFilledBlocks) {
+            
+            *min = 0;
+            *max = ColourMapper::getColourMapCount() - 1;
 
-	if (m_colour == Qt::black) deft = 0;
-	else if (m_colour == Qt::darkRed) deft = 1;
-	else if (m_colour == Qt::darkBlue) deft = 2;
-	else if (m_colour == Qt::darkGreen) deft = 3;
-	else if (m_colour == QColor(200, 50, 255)) deft = 4;
-	else if (m_colour == QColor(255, 150, 50)) deft = 5;
+            deft = m_colourMap;
+
+        } else {
+
+            *min = 0;
+            *max = 5;
+
+            if (m_colour == Qt::black) deft = 0;
+            else if (m_colour == Qt::darkRed) deft = 1;
+            else if (m_colour == Qt::darkBlue) deft = 2;
+            else if (m_colour == Qt::darkGreen) deft = 3;
+            else if (m_colour == QColor(200, 50, 255)) deft = 4;
+            else if (m_colour == QColor(255, 150, 50)) deft = 5;
+        }
 
     } else if (name == "Scale") {
 
@@ -462,14 +489,15 @@
     } else if (name == "Plot Type") {
         
         *min = 0;
-        *max = 2;
+        *max = 3;
 
         deft = (int)m_plotStyle;
 
     } else if (name == "Bin Scale") {
         
         *min = 0;
-        *max = 2;
+//        *max = 2;
+        *max = 1; // I don't think we really do want to offer inverted log
 
         deft = (int)m_binScale;
 
@@ -485,14 +513,18 @@
 				    int value) const
 {
     if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0: return tr("Black");
-	case 1: return tr("Red");
-	case 2: return tr("Blue");
-	case 3: return tr("Green");
-	case 4: return tr("Purple");
-	case 5: return tr("Orange");
+        if (m_plotStyle == PlotFilledBlocks) {
+            return ColourMapper::getColourMapName(value);
+        } else {
+            switch (value) {
+            default:
+            case 0: return tr("Black");
+            case 1: return tr("Red");
+            case 2: return tr("Blue");
+            case 3: return tr("Green");
+            case 4: return tr("Purple");
+            case 5: return tr("Orange");
+            }
 	}
     }
     if (name == "Scale") {
@@ -517,6 +549,7 @@
 	case 0: return tr("Lines");
 	case 1: return tr("Steps");
 	case 2: return tr("Blocks");
+	case 3: return tr("Colours");
 	}
     }
     if (name == "Bin Scale") {
@@ -545,14 +578,18 @@
     if (name == "Gain") {
 	setGain(pow(10, float(value)/20.0));
     } else if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0:	setBaseColour(Qt::black); break;
-	case 1: setBaseColour(Qt::darkRed); break;
-	case 2: setBaseColour(Qt::darkBlue); break;
-	case 3: setBaseColour(Qt::darkGreen); break;
-	case 4: setBaseColour(QColor(200, 50, 255)); break;
-	case 5: setBaseColour(QColor(255, 150, 50)); break;
+        if (m_plotStyle == PlotFilledBlocks) {
+            setFillColourMap(value);
+        } else {
+            switch (value) {
+            default:
+            case 0: setBaseColour(Qt::black); break;
+            case 1: setBaseColour(Qt::darkRed); break;
+            case 2: setBaseColour(Qt::darkBlue); break;
+            case 3: setBaseColour(Qt::darkGreen); break;
+            case 4: setBaseColour(QColor(200, 50, 255)); break;
+            case 5: setBaseColour(QColor(255, 150, 50)); break;
+            }
 	}
     } else if (name == "Scale") {
 	switch (value) {
@@ -591,6 +628,14 @@
 }
 
 void
+SliceLayer::setFillColourMap(int map)
+{
+    if (m_colourMap == map) return;
+    m_colourMap = map;
+    emit layerParametersChanged();
+}
+
+void
 SliceLayer::setEnergyScale(EnergyScale scale)
 {
     if (m_energyScale == scale) return;
@@ -610,7 +655,12 @@
 SliceLayer::setPlotStyle(PlotStyle style)
 {
     if (m_plotStyle == style) return;
+    bool colourTypeChanged = (style == PlotFilledBlocks ||
+                              m_plotStyle == PlotFilledBlocks);
     m_plotStyle = style;
+    if (colourTypeChanged) {
+        emit layerParameterRangesChanged();
+    }
     emit layerParametersChanged();
 }
 
@@ -644,11 +694,13 @@
     QString s;
     
     s += QString("colour=\"%1\" "
-		 "energyScale=\"%2\" "
-                 "samplingMode=\"%3\" "
-                 "gain=\"%4\" "
-                 "normalize=\"%5\"")
+                 "colourScheme=\"%2\" "
+		 "energyScale=\"%3\" "
+                 "samplingMode=\"%4\" "
+                 "gain=\"%5\" "
+                 "normalize=\"%6\"")
 	.arg(encodeColour(m_colour))
+        .arg(m_colourMap)
 	.arg(m_energyScale)
         .arg(m_samplingMode)
         .arg(m_gain)
@@ -678,6 +730,9 @@
 	attributes.value("samplingMode").toInt(&ok);
     if (ok) setSamplingMode(mode);
 
+    int colourMap = attributes.value("colourScheme").toInt(&ok);
+    if (ok) setFillColourMap(colourMap);
+
     float gain = attributes.value("gain").toFloat(&ok);
     if (ok) setGain(gain);
 
--- a/layer/SliceLayer.h	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/SliceLayer.h	Thu Feb 01 14:31:28 2007 +0000
@@ -65,13 +65,16 @@
 
     enum SamplingMode { NearestSample, SampleMean, SamplePeak };
 
-    enum PlotStyle { PlotLines, PlotSteps, PlotBlocks };
+    enum PlotStyle { PlotLines, PlotSteps, PlotBlocks, PlotFilledBlocks };
 
     enum BinScale { LinearBins, LogBins, InvertedLogBins };
 
     void setBaseColour(QColor);
     QColor getBaseColour() const { return m_colour; }
 
+    void setFillColourMap(int);
+    int getFillColourMap() const { return m_colourMap; }
+
     void setEnergyScale(EnergyScale);
     EnergyScale getEnergyScale() const { return m_energyScale; }
 
@@ -100,6 +103,7 @@
 protected:
     const DenseThreeDimensionalModel *m_sliceableModel;
     QColor                            m_colour;
+    int                               m_colourMap;
     EnergyScale                       m_energyScale;
     SamplingMode                      m_samplingMode;
     PlotStyle                         m_plotStyle;
--- a/layer/SpectrogramLayer.cpp	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/SpectrogramLayer.cpp	Thu Feb 01 14:31:28 2007 +0000
@@ -54,7 +54,7 @@
     m_maxFrequency(8000),
     m_initialMaxFrequency(8000),
     m_colourScale(dBColourScale),
-    m_colourScheme(0),
+    m_colourMap(0),
     m_frequencyScale(LinearFrequencyScale),
     m_binDisplay(AllBins),
     m_normalizeColumns(false),
@@ -90,7 +90,7 @@
             this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
     setWindowType(prefs->getWindowType());
 
-    setColourmap();
+    initialisePalette();
 }
 
 SpectrogramLayer::~SpectrogramLayer()
@@ -189,18 +189,12 @@
 	name == "Window Increment" ||
         name == "Zero Padding") return tr("Window");
     if (name == "Colour" ||
-//	name == "Gain" ||
 	name == "Threshold" ||
 	name == "Colour Rotation") return tr("Colour");
     if (name == "Normalize Columns" ||
         name == "Normalize Visible Area" ||
-//	name == "Bin Display" ||
         name == "Gain" ||
 	name == "Colour Scale") return tr("Scale");
-//    if (name == "Max Frequency" ||
-//	name == "Min Frequency" ||
-//	name == "Frequency Scale" ||
-//	name == "Frequency Adjustment") return tr("Range");
     return QString();
 }
 
@@ -251,7 +245,7 @@
 	*min = 0;
 	*max = ColourMapper::getColourMapCount() - 1;
 
-	deft = m_colourScheme;
+	deft = m_colourMap;
 
     } else if (name == "Window Size") {
 
@@ -445,7 +439,7 @@
     } else if (name == "Colour Rotation") {
 	setColourRotation(value);
     } else if (name == "Colour") {
-        setColourScheme(value);
+        setColourMap(value);
     } else if (name == "Window Size") {
 	setWindowSize(32 << value);
     } else if (name == "Window Increment") {
@@ -755,7 +749,7 @@
     int distance = r - m_colourRotation;
 
     if (distance != 0) {
-	rotateColourmap(-distance);
+	rotatePalette(-distance);
 	m_colourRotation = r;
     }
     
@@ -781,22 +775,22 @@
 }
 
 void
-SpectrogramLayer::setColourScheme(int scheme)
+SpectrogramLayer::setColourMap(int map)
 {
-    if (m_colourScheme == scheme) return;
+    if (m_colourMap == map) return;
 
     invalidatePixmapCaches();
     
-    m_colourScheme = scheme;
-    setColourmap();
+    m_colourMap = map;
+    initialisePalette();
 
     emit layerParametersChanged();
 }
 
 int
-SpectrogramLayer::getColourScheme() const
+SpectrogramLayer::getColourMap() const
 {
-    return m_colourScheme;
+    return m_colourMap;
 }
 
 void
@@ -992,46 +986,45 @@
 }
 
 void
-SpectrogramLayer::setColourmap()
+SpectrogramLayer::initialisePalette()
 {
     int formerRotation = m_colourRotation;
 
-    if (m_colourScheme == (int)ColourMapper::BlackOnWhite) {
-	m_colourMap.setColour(NO_VALUE, Qt::white);
+    if (m_colourMap == (int)ColourMapper::BlackOnWhite) {
+	m_palette.setColour(NO_VALUE, Qt::white);
     } else {
-	m_colourMap.setColour(NO_VALUE, Qt::black);
+	m_palette.setColour(NO_VALUE, Qt::black);
     }
 
-    ColourMapper mapper(m_colourScheme, 1.f, 256.f);
+    ColourMapper mapper(m_colourMap, 1.f, 255.f);
     
     for (int pixel = 1; pixel < 256; ++pixel) {
-
-        m_colourMap.setColour(pixel, mapper.map(pixel));
+        m_palette.setColour(pixel, mapper.map(pixel));
     }
 
     m_crosshairColour = mapper.getContrastingColour();
 
     m_colourRotation = 0;
-    rotateColourmap(m_colourRotation - formerRotation);
+    rotatePalette(m_colourRotation - formerRotation);
     m_colourRotation = formerRotation;
 }
 
 void
-SpectrogramLayer::rotateColourmap(int distance)
+SpectrogramLayer::rotatePalette(int distance)
 {
     QColor newPixels[256];
 
-    newPixels[NO_VALUE] = m_colourMap.getColour(NO_VALUE);
+    newPixels[NO_VALUE] = m_palette.getColour(NO_VALUE);
 
     for (int pixel = 1; pixel < 256; ++pixel) {
 	int target = pixel + distance;
 	while (target < 1) target += 255;
 	while (target > 255) target -= 255;
-	newPixels[target] = m_colourMap.getColour(pixel);
+	newPixels[target] = m_palette.getColour(pixel);
     }
 
     for (int pixel = 0; pixel < 256; ++pixel) {
-	m_colourMap.setColour(pixel, newPixels[pixel]);
+	m_palette.setColour(pixel, newPixels[pixel]);
     }
 }
 
@@ -1666,7 +1659,7 @@
 void
 SpectrogramLayer::paint(View *v, QPainter &paint, QRect rect) const
 {
-    if (m_colourScheme == (int)ColourMapper::BlackOnWhite) {
+    if (m_colourMap == (int)ColourMapper::BlackOnWhite) {
 	v->setLightBackground(true);
     } else {
 	v->setLightBackground(false);
@@ -1930,7 +1923,7 @@
         m_drawBuffer = QImage(w, h, QImage::Format_RGB32);
     }
 
-    m_drawBuffer.fill(m_colourMap.getColour(0).rgb());
+    m_drawBuffer.fill(m_palette.getColour(0).rgb());
 
     int sr = m_model->getSampleRate();
 
@@ -2108,7 +2101,7 @@
 		pixel = getDisplayValue(v, avg);
 
 		assert(x <= m_drawBuffer.width());
-		QColor c = m_colourMap.getColour(pixel);
+		QColor c = m_palette.getColour(pixel);
 		m_drawBuffer.setPixel(x, y,
                                       qRgb(c.red(), c.green(), c.blue()));
 	    }
@@ -2659,7 +2652,7 @@
 /*
 	    int colour = (i * 255) / ch + 1;
 */
-	    paint.setPen(m_colourMap.getColour(colour));
+	    paint.setPen(m_palette.getColour(colour));
 
             int y = textHeight * topLines + 4 + ch - i;
 
@@ -2939,7 +2932,7 @@
 	.arg(m_minFrequency)
 	.arg(m_maxFrequency)
 	.arg(m_colourScale)
-	.arg(m_colourScheme)
+	.arg(m_colourMap)
 	.arg(m_colourRotation)
 	.arg(m_frequencyScale)
 	.arg(m_binDisplay)
@@ -2996,8 +2989,8 @@
 	attributes.value("colourScale").toInt(&ok);
     if (ok) setColourScale(colourScale);
 
-    int colourScheme = attributes.value("colourScheme").toInt(&ok);
-    if (ok) setColourScheme(colourScheme);
+    int colourMap = attributes.value("colourScheme").toInt(&ok);
+    if (ok) setColourMap(colourMap);
 
     int colourRotation = attributes.value("colourRotation").toInt(&ok);
     if (ok) setColourRotation(colourRotation);
--- a/layer/SpectrogramLayer.h	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/SpectrogramLayer.h	Thu Feb 01 14:31:28 2007 +0000
@@ -170,8 +170,8 @@
     void setNormalizeVisibleArea(bool n);
     bool getNormalizeVisibleArea() const;
 
-    void setColourScheme(int scheme);
-    int getColourScheme() const;
+    void setColourMap(int map);
+    int getColourMap() const;
 
     /**
      * Specify the colourmap rotation for the colour scale.
@@ -238,7 +238,7 @@
     size_t              m_maxFrequency;
     size_t              m_initialMaxFrequency;
     ColourScale         m_colourScale;
-    int                 m_colourScheme;
+    int                 m_colourMap;
     QColor              m_crosshairColour;
     FrequencyScale      m_frequencyScale;
     BinDisplay          m_binDisplay;
@@ -248,7 +248,7 @@
 
     enum { NO_VALUE = 0 }; // colour index for unused pixels
 
-    class ColourMap
+    class Palette
     {
     public:
         QColor getColour(unsigned char index) const {
@@ -263,7 +263,7 @@
         QColor m_colours[256];
     };
 
-    ColourMap m_colourMap;
+    Palette m_palette;
 
     struct PixmapCache
     {
@@ -283,8 +283,8 @@
     mutable size_t m_candidateFillStartFrame;
     bool m_exiting;
 
-    void setColourmap();
-    void rotateColourmap(int distance);
+    void initialisePalette();
+    void rotatePalette(int distance);
 
     static float calculateFrequency(size_t bin,
 				    size_t windowSize,
--- a/layer/TimeValueLayer.cpp	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/TimeValueLayer.cpp	Thu Feb 01 14:31:28 2007 +0000
@@ -18,6 +18,7 @@
 #include "data/model/Model.h"
 #include "base/RealTime.h"
 #include "base/Profiler.h"
+#include "base/LogRange.h"
 #include "view/View.h"
 
 #include "data/model/SparseTimeValueModel.h"
@@ -26,6 +27,7 @@
 #include "widgets/ListInputDialog.h"
 
 #include "SpectrogramLayer.h" // for optional frequency alignment
+#include "ColourMapper.h"
 
 #include <QPainter>
 #include <QPainterPath>
@@ -43,6 +45,7 @@
     m_editingPoint(0, 0.0, tr("New Point")),
     m_editingCommand(0),
     m_colour(Qt::darkGreen),
+    m_colourMap(0),
     m_plotStyle(PlotConnectedPoints),
     m_verticalScale(AutoAlignScale)
 {
@@ -105,15 +108,25 @@
 
     if (name == "Colour") {
 
-	if (min) *min = 0;
-	if (max) *max = 5;
+        if (m_plotStyle == PlotSegmentation) {
+            
+            if (min) *min = 0;
+            if (max) *max = ColourMapper::getColourMapCount() - 1;
 
-	if (m_colour == Qt::black) deft = 0;
-	else if (m_colour == Qt::darkRed) deft = 1;
-	else if (m_colour == Qt::darkBlue) deft = 2;
-	else if (m_colour == Qt::darkGreen) deft = 3;
-	else if (m_colour == QColor(200, 50, 255)) deft = 4;
-	else if (m_colour == QColor(255, 150, 50)) deft = 5;
+            deft = m_colourMap;
+        
+        } else {
+
+            if (min) *min = 0;
+            if (max) *max = 5;
+
+            if (m_colour == Qt::black) deft = 0;
+            else if (m_colour == Qt::darkRed) deft = 1;
+            else if (m_colour == Qt::darkBlue) deft = 2;
+            else if (m_colour == Qt::darkGreen) deft = 3;
+            else if (m_colour == QColor(200, 50, 255)) deft = 4;
+            else if (m_colour == QColor(255, 150, 50)) deft = 5;
+        }
 
     } else if (name == "Plot Type") {
 	
@@ -149,15 +162,19 @@
 				    int value) const
 {
     if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0: return tr("Black");
-	case 1: return tr("Red");
-	case 2: return tr("Blue");
-	case 3: return tr("Green");
-	case 4: return tr("Purple");
-	case 5: return tr("Orange");
-	}
+        if (m_plotStyle == PlotSegmentation) {
+            return ColourMapper::getColourMapName(value);
+        } else {
+            switch (value) {
+            default:
+            case 0: return tr("Black");
+            case 1: return tr("Red");
+            case 2: return tr("Blue");
+            case 3: return tr("Green");
+            case 4: return tr("Purple");
+            case 5: return tr("Orange");
+            }
+        }
     } else if (name == "Plot Type") {
 	switch (value) {
 	default:
@@ -184,15 +201,19 @@
 TimeValueLayer::setProperty(const PropertyName &name, int value)
 {
     if (name == "Colour") {
-	switch (value) {
-	default:
-	case 0:	setBaseColour(Qt::black); break;
-	case 1: setBaseColour(Qt::darkRed); break;
-	case 2: setBaseColour(Qt::darkBlue); break;
-	case 3: setBaseColour(Qt::darkGreen); break;
-	case 4: setBaseColour(QColor(200, 50, 255)); break;
-	case 5: setBaseColour(QColor(255, 150, 50)); break;
-	}
+        if (m_plotStyle == PlotSegmentation) {
+            setFillColourMap(value);
+        } else {
+            switch (value) {
+            default:
+            case 0:	setBaseColour(Qt::black); break;
+            case 1: setBaseColour(Qt::darkRed); break;
+            case 2: setBaseColour(Qt::darkBlue); break;
+            case 3: setBaseColour(Qt::darkGreen); break;
+            case 4: setBaseColour(QColor(200, 50, 255)); break;
+            case 5: setBaseColour(QColor(255, 150, 50)); break;
+            }
+        }
     } else if (name == "Plot Type") {
 	setPlotStyle(PlotStyle(value));
     } else if (name == "Vertical Scale") {
@@ -215,10 +236,23 @@
 }
 
 void
+TimeValueLayer::setFillColourMap(int map)
+{
+    if (m_colourMap == map) return;
+    m_colourMap = map;
+    emit layerParametersChanged();
+}
+
+void
 TimeValueLayer::setPlotStyle(PlotStyle style)
 {
     if (m_plotStyle == style) return;
+    bool colourTypeChanged = (style == PlotSegmentation ||
+                              m_plotStyle == PlotSegmentation);
     m_plotStyle = style;
+    if (colourTypeChanged) {
+        emit layerParameterRangesChanged();
+    }
     emit layerParametersChanged();
 }
 
@@ -436,8 +470,7 @@
             min = m_model->getValueMinimum();
             max = m_model->getValueMaximum();
         } else if (log) {
-            min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
-            max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+            LogRange::mapRange(min, max);
         }
 
     } else if (m_verticalScale == PlusMinusOneScale) {
@@ -451,8 +484,7 @@
         max = m_model->getValueMaximum();
 
         if (m_verticalScale == LogScale) {
-            min = (min < 0.0) ? -log10(-min) : (min == 0.0) ? 0.0 : log10(min);
-            max = (max < 0.0) ? -log10(-max) : (max == 0.0) ? 0.0 : log10(max);
+            LogRange::mapRange(min, max);
             log = true;
         }
     }
@@ -473,7 +505,7 @@
 //              << max << ", log " << logarithmic << std::endl;
 
     if (logarithmic) {
-        val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val);
+        val = LogRange::map(val);
     }
 
     return int(h - ((val - min) * h) / (max - min));
@@ -491,7 +523,7 @@
     float val = min + (float(h - y) * float(max - min)) / h;
 
     if (logarithmic) {
-        val = pow(10, val);
+        val = powf(10.f, val);
     }
 
     return val;
@@ -504,14 +536,21 @@
     bool log;
     getScaleExtents(v, min, max, log);
 
+    float logthresh = -80;
+
+    if (min > max) std::swap(min, max);
+    if (max == min) max = min + 1;
+
     if (log) {
-        val = (val < 0.0) ? -log10(-val) : (val == 0.0) ? 0.0 : log10(val);
+        LogRange::mapRange(min, max);
+        val = LogRange::map(val);
     }
 
-    int iv = ((val - min) / (max - min)) * 255.999;
+//    std::cerr << "TimeValueLayer::getColourForValue: min " << min << ", max "
+//              << max << ", log " << log << ", value " << val << std::endl;
 
-    QColor colour = QColor::fromHsv(256 - iv, iv / 2 + 128, iv);
-    return QColor(colour.red(), colour.green(), colour.blue(), 120);
+    QColor solid = ColourMapper(m_colourMap, min, max).map(val);
+    return QColor(solid.red(), solid.green(), solid.blue(), 120);
 }
 
 void
@@ -608,6 +647,7 @@
 	paint.setPen(m_colour);
 
 	if (m_plotStyle == PlotSegmentation) {
+            paint.setPen(Qt::black);
             paint.setBrush(getColourForValue(v, p.value));
 	    labelY = v->height();
 	} else if (m_plotStyle == PlotLines ||
--- a/layer/TimeValueLayer.h	Wed Jan 31 12:13:47 2007 +0000
+++ b/layer/TimeValueLayer.h	Thu Feb 01 14:31:28 2007 +0000
@@ -76,6 +76,9 @@
     void setBaseColour(QColor);
     QColor getBaseColour() const { return m_colour; }
 
+    void setFillColourMap(int);
+    int getFillColourMap() const { return m_colourMap; }
+
     enum PlotStyle {
 	PlotPoints,
 	PlotStems,
@@ -132,6 +135,7 @@
     SparseTimeValueModel::Point m_editingPoint;
     SparseTimeValueModel::EditCommand *m_editingCommand;
     QColor m_colour;
+    int m_colourMap;
     PlotStyle m_plotStyle;
     VerticalScale m_verticalScale;
 };
--- a/view/View.cpp	Wed Jan 31 12:13:47 2007 +0000
+++ b/view/View.cpp	Thu Feb 01 14:31:28 2007 +0000
@@ -450,6 +450,8 @@
     
     connect(layer, SIGNAL(layerParametersChanged()),
 	    this,    SLOT(layerParametersChanged()));
+    connect(layer, SIGNAL(layerParameterRangesChanged()),
+	    this,    SLOT(layerParameterRangesChanged()));
     connect(layer, SIGNAL(layerNameChanged()),
 	    this,    SLOT(layerNameChanged()));
     connect(layer, SIGNAL(modelChanged()),
@@ -487,6 +489,21 @@
 	}
     }
     
+    disconnect(layer, SIGNAL(layerParametersChanged()),
+               this,    SLOT(layerParametersChanged()));
+    disconnect(layer, SIGNAL(layerParameterRangesChanged()),
+               this,    SLOT(layerParameterRangesChanged()));
+    disconnect(layer, SIGNAL(layerNameChanged()),
+               this,    SLOT(layerNameChanged()));
+    disconnect(layer, SIGNAL(modelChanged()),
+               this,    SLOT(modelChanged()));
+    disconnect(layer, SIGNAL(modelCompletionChanged()),
+               this,    SLOT(modelCompletionChanged()));
+    disconnect(layer, SIGNAL(modelChanged(size_t, size_t)),
+               this,    SLOT(modelChanged(size_t, size_t)));
+    disconnect(layer, SIGNAL(modelReplaced()),
+               this,    SLOT(modelReplaced()));
+
     update();
 
     emit propertyContainerRemoved(layer);
@@ -734,6 +751,13 @@
 }
 
 void
+View::layerParameterRangesChanged()
+{
+    Layer *layer = dynamic_cast<Layer *>(sender());
+    if (layer) emit propertyContainerPropertyRangeChanged(layer);
+}
+
+void
 View::layerNameChanged()
 {
     Layer *layer = dynamic_cast<Layer *>(sender());
--- a/view/View.h	Wed Jan 31 12:13:47 2007 +0000
+++ b/view/View.h	Thu Feb 01 14:31:28 2007 +0000
@@ -236,6 +236,7 @@
     void propertyContainerAdded(PropertyContainer *pc);
     void propertyContainerRemoved(PropertyContainer *pc);
     void propertyContainerPropertyChanged(PropertyContainer *pc);
+    void propertyContainerPropertyRangeChanged(PropertyContainer *pc);
     void propertyContainerNameChanged(PropertyContainer *pc);
     void propertyChanged(PropertyContainer::PropertyName);
 
@@ -250,6 +251,7 @@
     virtual void modelCompletionChanged();
     virtual void modelReplaced();
     virtual void layerParametersChanged();
+    virtual void layerParameterRangesChanged();
     virtual void layerNameChanged();
 
     virtual void viewManagerCentreFrameChanged(void *, unsigned long, bool);
--- a/widgets/PropertyBox.cpp	Wed Jan 31 12:13:47 2007 +0000
+++ b/widgets/PropertyBox.cpp	Thu Feb 01 14:31:28 2007 +0000
@@ -248,7 +248,8 @@
 }
 
 void
-PropertyBox::updatePropertyEditor(PropertyContainer::PropertyName name)
+PropertyBox::updatePropertyEditor(PropertyContainer::PropertyName name,
+                                  bool rangeChanged)
 {
     PropertyContainer::PropertyType type = m_container->getPropertyType(name);
     int row = m_layout->rowCount();
@@ -339,6 +340,14 @@
 	if (have) {
 	    dial = dynamic_cast<AudioDial *>(m_propertyControllers[name]);
 	    assert(dial);
+            if (rangeChanged) {
+                dial->blockSignals(true);
+                dial->setMinimum(min);
+                dial->setMaximum(max);
+                dial->setRangeMapper(m_container->getNewPropertyRangeMapper(name));
+                dial->blockSignals(false);
+            }
+                
 	} else {
 #ifdef DEBUG_PROPERTY_BOX 
 	    std::cerr << "PropertyBox: creating new dial" << std::endl;
@@ -401,7 +410,11 @@
 	    cb = new NotifyingComboBox();
 	    cb->setObjectName(name);
             cb->setDuplicatesEnabled(false);
+        }
 
+        if (!have || rangeChanged) {
+            cb->blockSignals(true);
+            cb->clear();
             if (type == PropertyContainer::ValueProperty) {
                 for (int i = min; i <= max; ++i) {
                     cb->addItem(m_container->getPropertyValueLabel(name, i));
@@ -414,7 +427,10 @@
                 }
                 cb->setEditable(true);
             }
+            cb->blockSignals(false);
+        }
 
+        if (!have) {
 	    connect(cb, SIGNAL(activated(int)),
 		    this, SLOT(propertyControllerChanged(int)));
             connect(cb, SIGNAL(mouseEntered()),
@@ -484,6 +500,19 @@
 }
 
 void
+PropertyBox::propertyContainerPropertyRangeChanged(PropertyContainer *pc)
+{
+    blockSignals(true);
+
+    PropertyContainer::PropertyList properties = m_container->getProperties();
+    for (size_t i = 0; i < properties.size(); ++i) {
+	updatePropertyEditor(properties[i], true);
+    }
+
+    blockSignals(false);
+}    
+
+void
 PropertyBox::unitDatabaseChanged()
 {
     blockSignals(true);
--- a/widgets/PropertyBox.h	Wed Jan 31 12:13:47 2007 +0000
+++ b/widgets/PropertyBox.h	Thu Feb 01 14:31:28 2007 +0000
@@ -48,6 +48,7 @@
 
 public slots:
     void propertyContainerPropertyChanged(PropertyContainer *);
+    void propertyContainerPropertyRangeChanged(PropertyContainer *);
     void pluginConfigurationChanged(QString);
     void layerVisibilityChanged(bool);
 
@@ -69,7 +70,8 @@
     void mouseLeftWidget();
 
 protected:
-    void updatePropertyEditor(PropertyContainer::PropertyName);
+    void updatePropertyEditor(PropertyContainer::PropertyName,
+                              bool rangeChanged = false);
 
     QLabel *m_nameWidget;
     QWidget *m_mainWidget;
--- a/widgets/PropertyStack.cpp	Wed Jan 31 12:13:47 2007 +0000
+++ b/widgets/PropertyStack.cpp	Thu Feb 01 14:31:28 2007 +0000
@@ -54,6 +54,9 @@
     connect(m_client, SIGNAL(propertyContainerPropertyChanged(PropertyContainer *)),
 	    this, SLOT(propertyContainerPropertyChanged(PropertyContainer *)));
 
+    connect(m_client, SIGNAL(propertyContainerPropertyRangeChanged(PropertyContainer *)),
+	    this, SLOT(propertyContainerPropertyRangeChanged(PropertyContainer *)));
+
     connect(m_client, SIGNAL(propertyContainerNameChanged(PropertyContainer *)),
 	    this, SLOT(propertyContainerNameChanged(PropertyContainer *)));
 
@@ -157,6 +160,16 @@
 }
 
 void
+PropertyStack::propertyContainerPropertyRangeChanged(PropertyContainer *pc)
+{
+    for (unsigned int i = 0; i < m_boxes.size(); ++i) {
+	if (pc == m_boxes[i]->getContainer()) {
+	    m_boxes[i]->propertyContainerPropertyRangeChanged(pc);
+	}
+    }
+}
+
+void
 PropertyStack::propertyContainerNameChanged(PropertyContainer *pc)
 {
     if (sender() != m_client) return;
--- a/widgets/PropertyStack.h	Wed Jan 31 12:13:47 2007 +0000
+++ b/widgets/PropertyStack.h	Thu Feb 01 14:31:28 2007 +0000
@@ -45,6 +45,7 @@
     void propertyContainerAdded(PropertyContainer *);
     void propertyContainerRemoved(PropertyContainer *);
     void propertyContainerPropertyChanged(PropertyContainer *);
+    void propertyContainerPropertyRangeChanged(PropertyContainer *);
     void propertyContainerNameChanged(PropertyContainer *);
 
     void showLayer(bool);