annotate layer/Colour3DPlotLayer.cpp @ 98:2be85befe873

* Reorder plugin output querying so as to cope with plugins whose number of values per output varies depending on the initialise parameters * Make the colour 3D plot layer tolerably efficient with very dense models * Allow export of colour 3D plot layers
author Chris Cannam
date Wed, 10 May 2006 16:34:03 +0000
parents 45ba0b381c5d
children 571805759a66
rev   line source
Chris@58 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@59 4 Sonic Visualiser
Chris@59 5 An audio file viewer and annotation editor.
Chris@59 6 Centre for Digital Music, Queen Mary, University of London.
Chris@59 7 This file copyright 2006 Chris Cannam.
Chris@0 8
Chris@59 9 This program is free software; you can redistribute it and/or
Chris@59 10 modify it under the terms of the GNU General Public License as
Chris@59 11 published by the Free Software Foundation; either version 2 of the
Chris@59 12 License, or (at your option) any later version. See the file
Chris@59 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "Colour3DPlotLayer.h"
Chris@0 17
Chris@0 18 #include "base/View.h"
Chris@0 19 #include "base/Profiler.h"
Chris@0 20
Chris@0 21 #include <QPainter>
Chris@0 22 #include <QImage>
Chris@0 23 #include <QRect>
Chris@0 24
Chris@0 25 #include <iostream>
Chris@0 26
Chris@0 27 #include <cassert>
Chris@0 28
Chris@0 29
Chris@44 30 Colour3DPlotLayer::Colour3DPlotLayer() :
Chris@44 31 Layer(),
Chris@0 32 m_model(0),
Chris@0 33 m_cache(0)
Chris@0 34 {
Chris@44 35
Chris@0 36 }
Chris@0 37
Chris@0 38 Colour3DPlotLayer::~Colour3DPlotLayer()
Chris@0 39 {
Chris@0 40 }
Chris@0 41
Chris@0 42 void
Chris@0 43 Colour3DPlotLayer::setModel(const DenseThreeDimensionalModel *model)
Chris@0 44 {
Chris@0 45 m_model = model;
Chris@0 46 if (!m_model || !m_model->isOK()) return;
Chris@0 47
Chris@0 48 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 49 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 50 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 51
Chris@0 52 connect(m_model, SIGNAL(completionChanged()),
Chris@0 53 this, SIGNAL(modelCompletionChanged()));
Chris@0 54
Chris@0 55 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
Chris@0 56 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 57 this, SLOT(cacheInvalid(size_t, size_t)));
Chris@0 58
Chris@0 59 emit modelReplaced();
Chris@0 60 }
Chris@0 61
Chris@0 62 void
Chris@0 63 Colour3DPlotLayer::cacheInvalid()
Chris@0 64 {
Chris@0 65 delete m_cache;
Chris@0 66 m_cache = 0;
Chris@0 67 }
Chris@0 68
Chris@0 69 void
Chris@0 70 Colour3DPlotLayer::cacheInvalid(size_t, size_t)
Chris@0 71 {
Chris@0 72 cacheInvalid();
Chris@0 73 }
Chris@0 74
Chris@25 75 bool
Chris@44 76 Colour3DPlotLayer::isLayerScrollable(const View *v) const
Chris@25 77 {
Chris@25 78 QPoint discard;
Chris@44 79 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@25 80 }
Chris@25 81
Chris@25 82 QString
Chris@44 83 Colour3DPlotLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 84 {
Chris@25 85 if (!m_model) return "";
Chris@25 86
Chris@25 87 int x = pos.x();
Chris@25 88 int y = pos.y();
Chris@25 89
Chris@25 90 size_t modelStart = m_model->getStartFrame();
Chris@25 91 size_t modelWindow = m_model->getWindowSize();
Chris@25 92
Chris@25 93 int sx0 = modelWindow *
Chris@44 94 int((v->getFrameForX(x) - long(modelStart)) / long(modelWindow));
Chris@25 95 int sx1 = sx0 + modelWindow;
Chris@25 96
Chris@44 97 float binHeight = float(v->height()) / m_model->getYBinCount();
Chris@44 98 int sy = (v->height() - y) / binHeight;
Chris@25 99
Chris@25 100 float value = m_model->getBinValue(sx0, sy);
Chris@25 101
Chris@25 102 QString binName = m_model->getBinName(sy);
Chris@25 103 if (binName == "") binName = QString("[%1]").arg(sy + 1);
Chris@25 104 else binName = QString("%1 [%2]").arg(binName).arg(sy + 1);
Chris@25 105
Chris@25 106 QString text = tr("Time:\t%1 - %2\nBin:\t%3\nValue:\t%4")
Chris@25 107 .arg(RealTime::frame2RealTime(sx0, m_model->getSampleRate())
Chris@25 108 .toText(true).c_str())
Chris@25 109 .arg(RealTime::frame2RealTime(sx1, m_model->getSampleRate())
Chris@25 110 .toText(true).c_str())
Chris@25 111 .arg(binName)
Chris@25 112 .arg(value);
Chris@25 113
Chris@25 114 return text;
Chris@25 115 }
Chris@25 116
Chris@25 117 int
Chris@44 118 Colour3DPlotLayer::getVerticalScaleWidth(View *v, QPainter &paint) const
Chris@25 119 {
Chris@25 120 if (!m_model) return 0;
Chris@25 121
Chris@98 122 QString sampleText = QString("[%1]").arg(m_model->getYBinCount());
Chris@25 123 int tw = paint.fontMetrics().width(sampleText);
Chris@98 124 bool another = false;
Chris@25 125
Chris@25 126 for (size_t i = 0; i < m_model->getYBinCount(); ++i) {
Chris@25 127 if (m_model->getBinName(i).length() > sampleText.length()) {
Chris@25 128 sampleText = m_model->getBinName(i);
Chris@98 129 another = true;
Chris@25 130 }
Chris@25 131 }
Chris@98 132 if (another) {
Chris@25 133 tw = std::max(tw, paint.fontMetrics().width(sampleText));
Chris@25 134 }
Chris@25 135
Chris@25 136 return tw + 13;
Chris@25 137 }
Chris@25 138
Chris@25 139 void
Chris@44 140 Colour3DPlotLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@25 141 {
Chris@25 142 if (!m_model) return;
Chris@25 143
Chris@25 144 int h = rect.height(), w = rect.width();
Chris@44 145 float binHeight = float(v->height()) / m_model->getYBinCount();
Chris@25 146
Chris@98 147 int count = v->height() / paint.fontMetrics().height();
Chris@98 148 int step = m_model->getYBinCount() / count;
Chris@98 149 if (step == 0) step = 1;
Chris@25 150
Chris@25 151 for (size_t i = 0; i < m_model->getYBinCount(); ++i) {
Chris@25 152
Chris@98 153 if ((i % step) != 0) continue;
Chris@98 154
Chris@44 155 int y0 = v->height() - (i * binHeight) - 1;
Chris@25 156
Chris@25 157 QString text = m_model->getBinName(i);
Chris@25 158 if (text == "") text = QString("[%1]").arg(i + 1);
Chris@25 159
Chris@25 160 paint.drawLine(0, y0, w, y0);
Chris@25 161
Chris@98 162 int cy = y0 - (step * binHeight)/2;
Chris@25 163 int ty = cy + paint.fontMetrics().ascent()/2;
Chris@25 164
Chris@25 165 paint.drawText(10, ty, text);
Chris@25 166 }
Chris@25 167 }
Chris@25 168
Chris@0 169 void
Chris@44 170 Colour3DPlotLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 171 {
Chris@0 172 // Profiler profiler("Colour3DPlotLayer::paint");
Chris@44 173 // std::cerr << "Colour3DPlotLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << std::endl;
Chris@0 174
Chris@0 175 //!!! This doesn't yet accommodate the fact that the model may
Chris@0 176 //have a different sample rate from an underlying model. At the
Chris@0 177 //moment our paint mechanism assumes all models have the same
Chris@0 178 //sample rate. If that isn't the case, they won't align and the
Chris@0 179 //time ruler will match whichever model was used to construct it.
Chris@0 180 //Obviously it is not going to be the case in general that models
Chris@0 181 //will have the same samplerate, so we need a pane samplerate as
Chris@0 182 //well which we trivially realign to. (We can probably require
Chris@0 183 //the waveform and spectrogram layers to display at the pane
Chris@0 184 //samplerate.)
Chris@0 185
Chris@0 186 int completion = 0;
Chris@0 187 if (!m_model || !m_model->isOK() || !m_model->isReady(&completion)) {
Chris@0 188 if (completion > 0) {
Chris@44 189 paint.fillRect(0, 10, v->width() * completion / 100,
Chris@0 190 10, QColor(120, 120, 120));
Chris@0 191 }
Chris@0 192 return;
Chris@0 193 }
Chris@0 194
Chris@0 195 size_t modelStart = m_model->getStartFrame();
Chris@0 196 size_t modelEnd = m_model->getEndFrame();
Chris@0 197 size_t modelWindow = m_model->getWindowSize();
Chris@0 198
Chris@12 199 size_t cacheWidth = (modelEnd - modelStart) / modelWindow + 1;
Chris@12 200 size_t cacheHeight = m_model->getYBinCount();
Chris@12 201
Chris@12 202 if (m_cache &&
Chris@12 203 (m_cache->width() != cacheWidth ||
Chris@12 204 m_cache->height() != cacheHeight)) {
Chris@12 205
Chris@12 206 delete m_cache;
Chris@12 207 m_cache = 0;
Chris@12 208 }
Chris@12 209
Chris@0 210 if (!m_cache) {
Chris@0 211
Chris@12 212 m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
Chris@0 213
Chris@0 214 m_cache->setNumColors(256);
Chris@0 215 DenseThreeDimensionalModel::BinValueSet values;
Chris@0 216
Chris@0 217 float min = m_model->getMinimumLevel();
Chris@0 218 float max = m_model->getMaximumLevel();
Chris@0 219
Chris@0 220 if (max == min) max = min + 1.0;
Chris@0 221
Chris@0 222 for (int value = 0; value < 256; ++value) {
Chris@0 223 int hue = 256 - value;
Chris@26 224 QColor colour = QColor::fromHsv(hue, value/2 + 128, value);
Chris@26 225 m_cache->setColor(value, qRgba(colour.red(), colour.green(), colour.blue(), 80));
Chris@0 226 }
Chris@0 227
Chris@0 228 m_cache->fill(min);
Chris@0 229
Chris@0 230 for (size_t f = modelStart; f <= modelEnd; f += modelWindow) {
Chris@0 231
Chris@0 232 values.clear();
Chris@0 233 m_model->getBinValues(f, values);
Chris@0 234
Chris@0 235 for (size_t y = 0; y < m_model->getYBinCount(); ++y) {
Chris@0 236
Chris@0 237 float value = min;
Chris@0 238 if (y < values.size()) value = values[y];
Chris@0 239
Chris@0 240 int pixel = int(((value - min) * 256) / (max - min));
Chris@98 241 if (pixel < 0) pixel = 0;
Chris@98 242 if (pixel > 255) pixel = 255;
Chris@24 243
Chris@0 244 m_cache->setPixel(f / modelWindow, y, pixel);
Chris@0 245 }
Chris@0 246 }
Chris@0 247 }
Chris@0 248
Chris@98 249 if (m_model->getYBinCount() >= v->height()) {
Chris@98 250 paintDense(v, paint, rect);
Chris@98 251 return;
Chris@98 252 }
Chris@98 253
Chris@0 254 int x0 = rect.left();
Chris@0 255 int x1 = rect.right() + 1;
Chris@0 256
Chris@0 257 int w = x1 - x0;
Chris@44 258 int h = v->height();
Chris@0 259
Chris@0 260 // The cache is from the model's start frame to the model's end
Chris@0 261 // frame at the model's window increment frames per pixel. We
Chris@0 262 // want to draw from our start frame + x0 * zoomLevel to our start
Chris@0 263 // frame + x1 * zoomLevel at zoomLevel frames per pixel.
Chris@0 264
Chris@0 265 //!!! Strictly speaking we want quite different paint mechanisms
Chris@0 266 //for models that have more than one bin per pixel in either
Chris@0 267 //direction. This one is only really appropriate for models with
Chris@0 268 //far fewer bins in both directions.
Chris@0 269
Chris@44 270 int sx0 = int((v->getFrameForX(x0) - long(modelStart)) / long(modelWindow));
Chris@44 271 int sx1 = int((v->getFrameForX(x1) - long(modelStart)) / long(modelWindow));
Chris@0 272 int sw = sx1 - sx0;
Chris@0 273 int sh = m_model->getYBinCount();
Chris@0 274
Chris@0 275 /*
Chris@0 276 std::cerr << "Colour3DPlotLayer::paint: w " << w << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sw << ", sh " << sh << std::endl;
Chris@0 277 std::cerr << "Colour3DPlotLayer: sample rate is " << m_model->getSampleRate() << ", window size " << m_model->getWindowSize() << std::endl;
Chris@0 278 */
Chris@0 279
Chris@25 280 QPoint illuminatePos;
Chris@44 281 bool illuminate = v->shouldIlluminateLocalFeatures(this, illuminatePos);
Chris@54 282 char labelbuf[10];
Chris@25 283
Chris@0 284 for (int sx = sx0 - 1; sx <= sx1; ++sx) {
Chris@0 285
Chris@0 286 int fx = sx * int(modelWindow);
Chris@0 287
Chris@0 288 if (fx + modelWindow < int(modelStart) ||
Chris@0 289 fx > int(modelEnd)) continue;
Chris@0 290
Chris@54 291 int rx0 = v->getXForFrame(fx + int(modelStart));
Chris@54 292 int rx1 = v->getXForFrame(fx + int(modelStart) + int(modelWindow));
Chris@54 293
Chris@54 294 int w = rx1 - rx0;
Chris@54 295 if (w < 1) w = 1;
Chris@54 296
Chris@54 297 bool showLabel = (w > 10 &&
Chris@54 298 paint.fontMetrics().width("0.000000") < w - 3 &&
Chris@54 299 paint.fontMetrics().height() < (h / sh));
Chris@98 300
Chris@0 301 for (int sy = 0; sy < sh; ++sy) {
Chris@0 302
Chris@0 303 int ry0 = h - (sy * h) / sh - 1;
Chris@0 304 QRgb pixel = qRgb(255, 255, 255);
Chris@0 305 if (sx >= 0 && sx < m_cache->width() &&
Chris@0 306 sy >= 0 && sy < m_cache->height()) {
Chris@0 307 pixel = m_cache->pixel(sx, sy);
Chris@0 308 }
Chris@0 309
Chris@0 310 QColor pen(255, 255, 255, 80);
Chris@0 311 QColor brush(pixel);
Chris@0 312 brush.setAlpha(160);
Chris@0 313 paint.setPen(Qt::NoPen);
Chris@0 314 paint.setBrush(brush);
Chris@0 315
Chris@25 316 QRect r(rx0, ry0 - h / sh - 1, w, h / sh + 1);
Chris@25 317
Chris@25 318 if (illuminate) {
Chris@25 319 if (r.contains(illuminatePos)) {
Chris@25 320 paint.setPen(Qt::black);//!!!
Chris@25 321 }
Chris@25 322 }
Chris@76 323
Chris@76 324 // std::cout << "rect " << rx0 << "," << (ry0 - h / sh - 1) << " "
Chris@76 325 // << w << "x" << (h / sh + 1) << std::endl;
Chris@25 326
Chris@25 327 paint.drawRect(r);
Chris@0 328
Chris@54 329 if (showLabel) {
Chris@54 330 if (sx >= 0 && sx < m_cache->width() &&
Chris@54 331 sy >= 0 && sy < m_cache->height()) {
Chris@54 332 float value = m_model->getBinValue(fx, sy);
Chris@54 333 sprintf(labelbuf, "%06f", value);
Chris@54 334 QString text(labelbuf);
Chris@54 335 paint.setPen(Qt::white);
Chris@54 336 paint.drawText(rx0 + 2,
Chris@54 337 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
Chris@54 338 text);
Chris@0 339 }
Chris@0 340 }
Chris@0 341 }
Chris@0 342 }
Chris@98 343 }
Chris@0 344
Chris@98 345 void
Chris@98 346 Colour3DPlotLayer::paintDense(View *v, QPainter &paint, QRect rect) const
Chris@98 347 {
Chris@98 348 long startFrame = v->getStartFrame();
Chris@98 349 int zoomLevel = v->getZoomLevel();
Chris@0 350
Chris@98 351 size_t modelStart = m_model->getStartFrame();
Chris@98 352 size_t modelEnd = m_model->getEndFrame();
Chris@98 353 size_t modelWindow = m_model->getWindowSize();
Chris@0 354
Chris@98 355 int x0 = rect.left();
Chris@98 356 int x1 = rect.right() + 1;
Chris@0 357
Chris@98 358 int w = x1 - x0;
Chris@98 359 int h = v->height();
Chris@98 360 int sh = m_model->getYBinCount();
Chris@98 361
Chris@98 362 QImage img(w, h, QImage::Format_RGB32);
Chris@98 363
Chris@98 364 for (int x = x0; x < x1; ++x) {
Chris@98 365
Chris@98 366 float sx0 = (float(v->getFrameForX(x)) - modelStart) / modelWindow;
Chris@98 367 float sx1 = (float(v->getFrameForX(x+1)) - modelStart) / modelWindow;
Chris@98 368
Chris@98 369 int sx0i = int(sx0 + 0.001);
Chris@98 370 int sx1i = int(sx1);
Chris@98 371
Chris@98 372 for (int y = 0; y < h; ++y) {
Chris@98 373
Chris@98 374 float sy0 = (float(h - y - 1) * sh) / h;
Chris@98 375 float sy1 = (float(h - y) * sh) / h;
Chris@98 376
Chris@98 377 int sy0i = int(sy0 + 0.001);
Chris@98 378 int sy1i = int(sy1);
Chris@98 379
Chris@98 380 float mag = 0.0, div = 0.0;
Chris@98 381
Chris@98 382 for (int sx = sx0i; sx <= sx1i; ++sx) {
Chris@98 383
Chris@98 384 if (sx < 0 || sx >= m_cache->width()) continue;
Chris@98 385
Chris@98 386 for (int sy = sy0i; sy <= sy1i; ++sy) {
Chris@98 387
Chris@98 388 if (sy < 0 || sy >= m_cache->height()) continue;
Chris@98 389
Chris@98 390 float prop = 1.0;
Chris@98 391 if (sx == sx0i) prop *= (sx + 1) - sx0;
Chris@98 392 if (sx == sx1i) prop *= sx1 - sx;
Chris@98 393 if (sy == sy0i) prop *= (sy + 1) - sy0;
Chris@98 394 if (sy == sy1i) prop *= sy1 - sy;
Chris@98 395
Chris@98 396 mag += prop * m_cache->pixelIndex(sx, sy);
Chris@98 397 div += prop;
Chris@98 398 }
Chris@98 399 }
Chris@98 400
Chris@98 401 if (div != 0) mag /= div;
Chris@98 402 if (mag < 0) mag = 0;
Chris@98 403 if (mag > 255) mag = 255;
Chris@98 404
Chris@98 405 img.setPixel(x - x0, y, m_cache->color(int(mag + 0.001)));
Chris@98 406 }
Chris@0 407 }
Chris@0 408
Chris@98 409 paint.drawImage(x0, 0, img);
Chris@0 410 }
Chris@0 411
Chris@28 412 bool
Chris@44 413 Colour3DPlotLayer::snapToFeatureFrame(View *v, int &frame,
Chris@28 414 size_t &resolution,
Chris@28 415 SnapType snap) const
Chris@24 416 {
Chris@24 417 if (!m_model) {
Chris@44 418 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@24 419 }
Chris@24 420
Chris@24 421 resolution = m_model->getWindowSize();
Chris@28 422 int left = (frame / resolution) * resolution;
Chris@28 423 int right = left + resolution;
Chris@28 424
Chris@28 425 switch (snap) {
Chris@28 426 case SnapLeft: frame = left; break;
Chris@28 427 case SnapRight: frame = right; break;
Chris@28 428 case SnapNearest:
Chris@28 429 case SnapNeighbouring:
Chris@28 430 if (frame - left > right - frame) frame = right;
Chris@28 431 else frame = left;
Chris@28 432 break;
Chris@28 433 }
Chris@24 434
Chris@28 435 return true;
Chris@24 436 }
Chris@24 437
Chris@0 438 #ifdef INCLUDE_MOCFILES
Chris@0 439 #include "Colour3DPlotLayer.moc.cpp"
Chris@0 440 #endif
Chris@0 441
Chris@0 442