annotate layer/Colour3DPlotLayer.cpp @ 150:b1a3a9400284

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