annotate layer/Colour3DPlotLayer.cpp @ 317:e251c3599ea8

* Make RemoteFile far more pervasive, and use it for local files as well so that we can handle both transparently. Make it shallow copy with reference counting, so it can be used by value without having to worry about the cache file lifetime. Use RemoteFile for MainWindow file-open functions, etc
author Chris Cannam
date Thu, 18 Oct 2007 15:31:20 +0000
parents c0b9eec70639
children 984c1975f1ff
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@182 7 This file copyright 2006 Chris Cannam and QMUL.
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@197 20 #include "base/LogRange.h"
Chris@285 21 #include "base/ColourMapper.h"
Chris@0 22
Chris@0 23 #include <QPainter>
Chris@0 24 #include <QImage>
Chris@0 25 #include <QRect>
Chris@316 26 #include <QTextStream>
Chris@0 27
Chris@0 28 #include <iostream>
Chris@0 29
Chris@0 30 #include <cassert>
Chris@0 31
Chris@125 32 //#define DEBUG_COLOUR_3D_PLOT_LAYER_PAINT 1
Chris@125 33
Chris@0 34
Chris@44 35 Colour3DPlotLayer::Colour3DPlotLayer() :
Chris@0 36 m_model(0),
Chris@159 37 m_cache(0),
Chris@197 38 m_cacheStart(0),
Chris@197 39 m_colourScale(LinearScale),
Chris@197 40 m_colourMap(0),
Chris@197 41 m_normalizeColumns(false),
Chris@197 42 m_normalizeVisibleArea(false)
Chris@0 43 {
Chris@44 44
Chris@0 45 }
Chris@0 46
Chris@0 47 Colour3DPlotLayer::~Colour3DPlotLayer()
Chris@0 48 {
Chris@0 49 }
Chris@0 50
Chris@0 51 void
Chris@0 52 Colour3DPlotLayer::setModel(const DenseThreeDimensionalModel *model)
Chris@0 53 {
Chris@193 54 if (m_model == model) return;
Chris@193 55 const DenseThreeDimensionalModel *oldModel = m_model;
Chris@0 56 m_model = model;
Chris@0 57 if (!m_model || !m_model->isOK()) return;
Chris@0 58
Chris@0 59 connect(m_model, SIGNAL(modelChanged()), this, SIGNAL(modelChanged()));
Chris@0 60 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 61 this, SIGNAL(modelChanged(size_t, size_t)));
Chris@0 62
Chris@0 63 connect(m_model, SIGNAL(completionChanged()),
Chris@0 64 this, SIGNAL(modelCompletionChanged()));
Chris@0 65
Chris@0 66 connect(m_model, SIGNAL(modelChanged()), this, SLOT(cacheInvalid()));
Chris@0 67 connect(m_model, SIGNAL(modelChanged(size_t, size_t)),
Chris@0 68 this, SLOT(cacheInvalid(size_t, size_t)));
Chris@0 69
Chris@0 70 emit modelReplaced();
Chris@193 71 emit sliceableModelReplaced(oldModel, model);
Chris@0 72 }
Chris@0 73
Chris@0 74 void
Chris@0 75 Colour3DPlotLayer::cacheInvalid()
Chris@0 76 {
Chris@0 77 delete m_cache;
Chris@0 78 m_cache = 0;
Chris@0 79 }
Chris@0 80
Chris@0 81 void
Chris@0 82 Colour3DPlotLayer::cacheInvalid(size_t, size_t)
Chris@0 83 {
Chris@0 84 cacheInvalid();
Chris@0 85 }
Chris@0 86
Chris@159 87 Layer::PropertyList
Chris@159 88 Colour3DPlotLayer::getProperties() const
Chris@159 89 {
Chris@159 90 PropertyList list;
Chris@197 91 list.push_back("Colour");
Chris@159 92 list.push_back("Colour Scale");
Chris@197 93 list.push_back("Normalize Columns");
Chris@197 94 list.push_back("Normalize Visible Area");
Chris@159 95 return list;
Chris@159 96 }
Chris@159 97
Chris@159 98 QString
Chris@159 99 Colour3DPlotLayer::getPropertyLabel(const PropertyName &name) const
Chris@159 100 {
Chris@197 101 if (name == "Colour") return tr("Colour");
Chris@197 102 if (name == "Colour Scale") return tr("Scale");
Chris@197 103 if (name == "Normalize Columns") return tr("Normalize Columns");
Chris@197 104 if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
Chris@159 105 return "";
Chris@159 106 }
Chris@159 107
Chris@159 108 Layer::PropertyType
Chris@159 109 Colour3DPlotLayer::getPropertyType(const PropertyName &name) const
Chris@159 110 {
Chris@197 111 if (name == "Normalize Columns") return ToggleProperty;
Chris@197 112 if (name == "Normalize Visible Area") return ToggleProperty;
Chris@159 113 return ValueProperty;
Chris@159 114 }
Chris@159 115
Chris@159 116 QString
Chris@159 117 Colour3DPlotLayer::getPropertyGroupName(const PropertyName &name) const
Chris@159 118 {
Chris@197 119 if (name == "Normalize Columns" ||
Chris@197 120 name == "Normalize Visible Area" ||
Chris@197 121 name == "Colour Scale") return tr("Scale");
Chris@159 122 return QString();
Chris@159 123 }
Chris@159 124
Chris@159 125 int
Chris@159 126 Colour3DPlotLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 127 int *min, int *max, int *deflt) const
Chris@159 128 {
Chris@216 129 int val = 0;
Chris@159 130
Chris@216 131 int garbage0, garbage1, garbage2;
Chris@159 132 if (!min) min = &garbage0;
Chris@159 133 if (!max) max = &garbage1;
Chris@216 134 if (!deflt) deflt = &garbage2;
Chris@159 135
Chris@159 136 if (name == "Colour Scale") {
Chris@159 137
Chris@159 138 *min = 0;
Chris@197 139 *max = 2;
Chris@216 140 *deflt = (int)LinearScale;
Chris@159 141
Chris@216 142 val = (int)m_colourScale;
Chris@159 143
Chris@197 144 } else if (name == "Colour") {
Chris@197 145
Chris@197 146 *min = 0;
Chris@197 147 *max = ColourMapper::getColourMapCount() - 1;
Chris@216 148 *deflt = 0;
Chris@197 149
Chris@216 150 val = m_colourMap;
Chris@197 151
Chris@197 152 } else if (name == "Normalize Columns") {
Chris@197 153
Chris@216 154 *deflt = 0;
Chris@216 155 val = (m_normalizeColumns ? 1 : 0);
Chris@197 156
Chris@197 157 } else if (name == "Normalize Visible Area") {
Chris@197 158
Chris@216 159 *deflt = 0;
Chris@216 160 val = (m_normalizeVisibleArea ? 1 : 0);
Chris@197 161
Chris@159 162 } else {
Chris@216 163 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@159 164 }
Chris@159 165
Chris@216 166 return val;
Chris@159 167 }
Chris@159 168
Chris@159 169 QString
Chris@159 170 Colour3DPlotLayer::getPropertyValueLabel(const PropertyName &name,
Chris@159 171 int value) const
Chris@159 172 {
Chris@197 173 if (name == "Colour") {
Chris@197 174 return ColourMapper::getColourMapName(value);
Chris@197 175 }
Chris@159 176 if (name == "Colour Scale") {
Chris@159 177 switch (value) {
Chris@159 178 default:
Chris@198 179 case 0: return tr("Linear");
Chris@198 180 case 1: return tr("Log");
Chris@198 181 case 2: return tr("+/-1");
Chris@159 182 }
Chris@159 183 }
Chris@159 184 return tr("<unknown>");
Chris@159 185 }
Chris@159 186
Chris@159 187 void
Chris@159 188 Colour3DPlotLayer::setProperty(const PropertyName &name, int value)
Chris@159 189 {
Chris@159 190 if (name == "Colour Scale") {
Chris@159 191 switch (value) {
Chris@159 192 default:
Chris@159 193 case 0: setColourScale(LinearScale); break;
Chris@197 194 case 1: setColourScale(LogScale); break;
Chris@197 195 case 2: setColourScale(PlusMinusOneScale); break;
Chris@159 196 }
Chris@197 197 } else if (name == "Colour") {
Chris@197 198 setColourMap(value);
Chris@197 199 } else if (name == "Normalize Columns") {
Chris@197 200 setNormalizeColumns(value ? true : false);
Chris@197 201 } else if (name == "Normalize Visible Area") {
Chris@197 202 setNormalizeVisibleArea(value ? true : false);
Chris@159 203 }
Chris@159 204 }
Chris@159 205
Chris@159 206 void
Chris@159 207 Colour3DPlotLayer::setColourScale(ColourScale scale)
Chris@159 208 {
Chris@159 209 if (m_colourScale == scale) return;
Chris@159 210 m_colourScale = scale;
Chris@159 211 cacheInvalid();
Chris@159 212 emit layerParametersChanged();
Chris@159 213 }
Chris@159 214
Chris@197 215 void
Chris@197 216 Colour3DPlotLayer::setColourMap(int map)
Chris@197 217 {
Chris@197 218 if (m_colourMap == map) return;
Chris@197 219 m_colourMap = map;
Chris@197 220 cacheInvalid();
Chris@197 221 emit layerParametersChanged();
Chris@197 222 }
Chris@197 223
Chris@197 224 void
Chris@197 225 Colour3DPlotLayer::setNormalizeColumns(bool n)
Chris@197 226 {
Chris@197 227 if (m_normalizeColumns == n) return;
Chris@197 228 m_normalizeColumns = n;
Chris@197 229 cacheInvalid();
Chris@197 230 emit layerParametersChanged();
Chris@197 231 }
Chris@197 232
Chris@197 233 bool
Chris@197 234 Colour3DPlotLayer::getNormalizeColumns() const
Chris@197 235 {
Chris@197 236 return m_normalizeColumns;
Chris@197 237 }
Chris@197 238
Chris@197 239 void
Chris@197 240 Colour3DPlotLayer::setNormalizeVisibleArea(bool n)
Chris@197 241 {
Chris@197 242 if (m_normalizeVisibleArea == n) return;
Chris@197 243 m_normalizeVisibleArea = n;
Chris@197 244 cacheInvalid();
Chris@197 245 emit layerParametersChanged();
Chris@197 246 }
Chris@197 247
Chris@197 248 bool
Chris@197 249 Colour3DPlotLayer::getNormalizeVisibleArea() const
Chris@197 250 {
Chris@197 251 return m_normalizeVisibleArea;
Chris@197 252 }
Chris@197 253
Chris@25 254 bool
Chris@44 255 Colour3DPlotLayer::isLayerScrollable(const View *v) const
Chris@25 256 {
Chris@197 257 if (m_normalizeVisibleArea) return false;
Chris@25 258 QPoint discard;
Chris@44 259 return !v->shouldIlluminateLocalFeatures(this, discard);
Chris@25 260 }
Chris@25 261
Chris@25 262 QString
Chris@44 263 Colour3DPlotLayer::getFeatureDescription(View *v, QPoint &pos) const
Chris@25 264 {
Chris@25 265 if (!m_model) return "";
Chris@25 266
Chris@25 267 int x = pos.x();
Chris@25 268 int y = pos.y();
Chris@25 269
Chris@25 270 size_t modelStart = m_model->getStartFrame();
Chris@130 271 size_t modelResolution = m_model->getResolution();
Chris@25 272
Chris@159 273 float srRatio =
Chris@159 274 float(v->getViewManager()->getMainModelSampleRate()) /
Chris@159 275 float(m_model->getSampleRate());
Chris@159 276
Chris@160 277 int sx0 = int((v->getFrameForX(x) / srRatio - long(modelStart)) /
Chris@160 278 long(modelResolution));
Chris@25 279
Chris@160 280 int f0 = sx0 * modelResolution;
Chris@160 281 int f1 = f0 + modelResolution;
Chris@160 282
Chris@160 283 float binHeight = float(v->height()) / m_model->getHeight();
Chris@248 284 int sy = int((v->height() - y) / binHeight);
Chris@25 285
Chris@160 286 float value = m_model->getValueAt(sx0, sy);
Chris@159 287
Chris@159 288 // std::cerr << "bin value (" << sx0 << "," << sy << ") is " << value << std::endl;
Chris@25 289
Chris@25 290 QString binName = m_model->getBinName(sy);
Chris@25 291 if (binName == "") binName = QString("[%1]").arg(sy + 1);
Chris@25 292 else binName = QString("%1 [%2]").arg(binName).arg(sy + 1);
Chris@25 293
Chris@25 294 QString text = tr("Time:\t%1 - %2\nBin:\t%3\nValue:\t%4")
Chris@160 295 .arg(RealTime::frame2RealTime(f0, m_model->getSampleRate())
Chris@25 296 .toText(true).c_str())
Chris@160 297 .arg(RealTime::frame2RealTime(f1, m_model->getSampleRate())
Chris@25 298 .toText(true).c_str())
Chris@25 299 .arg(binName)
Chris@25 300 .arg(value);
Chris@25 301
Chris@25 302 return text;
Chris@25 303 }
Chris@25 304
Chris@25 305 int
Chris@248 306 Colour3DPlotLayer::getColourScaleWidth(QPainter &) const
Chris@159 307 {
Chris@159 308 int cw = 20;
Chris@159 309 return cw;
Chris@159 310 }
Chris@159 311
Chris@159 312 int
Chris@248 313 Colour3DPlotLayer::getVerticalScaleWidth(View *, QPainter &paint) const
Chris@25 314 {
Chris@25 315 if (!m_model) return 0;
Chris@25 316
Chris@160 317 QString sampleText = QString("[%1]").arg(m_model->getHeight());
Chris@25 318 int tw = paint.fontMetrics().width(sampleText);
Chris@98 319 bool another = false;
Chris@25 320
Chris@160 321 for (size_t i = 0; i < m_model->getHeight(); ++i) {
Chris@25 322 if (m_model->getBinName(i).length() > sampleText.length()) {
Chris@25 323 sampleText = m_model->getBinName(i);
Chris@98 324 another = true;
Chris@25 325 }
Chris@25 326 }
Chris@98 327 if (another) {
Chris@25 328 tw = std::max(tw, paint.fontMetrics().width(sampleText));
Chris@25 329 }
Chris@25 330
Chris@159 331 return tw + 13 + getColourScaleWidth(paint);
Chris@25 332 }
Chris@25 333
Chris@25 334 void
Chris@44 335 Colour3DPlotLayer::paintVerticalScale(View *v, QPainter &paint, QRect rect) const
Chris@25 336 {
Chris@25 337 if (!m_model) return;
Chris@25 338
Chris@25 339 int h = rect.height(), w = rect.width();
Chris@160 340 float binHeight = float(v->height()) / m_model->getHeight();
Chris@25 341
Chris@159 342 int cw = getColourScaleWidth(paint);
Chris@159 343
Chris@159 344 int ch = h - 20;
Chris@159 345 if (ch > 20 && m_cache) {
Chris@159 346
Chris@287 347 paint.setPen(v->getForeground());
Chris@159 348 paint.drawRect(4, 10, cw - 8, ch - 19);
Chris@159 349
Chris@159 350 for (int y = 0; y < ch - 20; ++y) {
Chris@159 351 QRgb c = m_cache->color(((ch - 20 - y) * 255) / (ch - 20));
Chris@159 352 // std::cerr << "y = " << y << ": rgb " << qRed(c) << "," << qGreen(c) << "," << qBlue(c) << std::endl;
Chris@159 353 paint.setPen(QColor(qRed(c), qGreen(c), qBlue(c)));
Chris@159 354 paint.drawLine(5, 11 + y, cw - 5, 11 + y);
Chris@159 355 }
Chris@159 356 }
Chris@159 357
Chris@287 358 paint.setPen(v->getForeground());
Chris@159 359
Chris@98 360 int count = v->height() / paint.fontMetrics().height();
Chris@160 361 int step = m_model->getHeight() / count;
Chris@98 362 if (step == 0) step = 1;
Chris@25 363
Chris@160 364 for (size_t i = 0; i < m_model->getHeight(); ++i) {
Chris@25 365
Chris@98 366 if ((i % step) != 0) continue;
Chris@98 367
Chris@248 368 int y0 = int(v->height() - (i * binHeight) - 1);
Chris@25 369
Chris@25 370 QString text = m_model->getBinName(i);
Chris@25 371 if (text == "") text = QString("[%1]").arg(i + 1);
Chris@25 372
Chris@159 373 paint.drawLine(cw, y0, w, y0);
Chris@25 374
Chris@248 375 int cy = int(y0 - (step * binHeight)/2);
Chris@25 376 int ty = cy + paint.fontMetrics().ascent()/2;
Chris@25 377
Chris@159 378 paint.drawText(cw + 5, ty, text);
Chris@25 379 }
Chris@25 380 }
Chris@25 381
Chris@0 382 void
Chris@197 383 Colour3DPlotLayer::getColumn(size_t col,
Chris@197 384 DenseThreeDimensionalModel::Column &values) const
Chris@197 385 {
Chris@197 386 m_model->getColumn(col, values);
Chris@197 387
Chris@224 388 float colMax = 0.f, colMin = 0.f;
Chris@197 389
Chris@197 390 float min = 0.f, max = 0.f;
Chris@197 391 if (m_normalizeColumns) {
Chris@197 392 min = m_model->getMinimumLevel();
Chris@197 393 max = m_model->getMaximumLevel();
Chris@197 394 }
Chris@197 395
Chris@197 396 if (m_normalizeColumns) {
Chris@197 397 for (size_t y = 0; y < values.size(); ++y) {
Chris@224 398 if (y == 0 || values[y] > colMax) colMax = values[y];
Chris@224 399 if (y == 0 || values[y] < colMin) colMin = values[y];
Chris@197 400 }
Chris@224 401 if (colMin == colMax) colMax = colMin + 1;
Chris@197 402 }
Chris@197 403
Chris@197 404 for (size_t y = 0; y < values.size(); ++y) {
Chris@197 405
Chris@197 406 float value = min;
Chris@197 407
Chris@197 408 value = values[y];
Chris@197 409
Chris@197 410 if (m_normalizeColumns) {
Chris@224 411 float norm = (value - colMin) / (colMax - colMin);
Chris@224 412 value = min + (max - min) * norm;
Chris@197 413 }
Chris@197 414
Chris@197 415 values[y] = value;
Chris@197 416 }
Chris@197 417 }
Chris@197 418
Chris@197 419 void
Chris@197 420 Colour3DPlotLayer::fillCache(size_t firstBin, size_t lastBin) const
Chris@197 421 {
Chris@197 422 size_t modelStart = m_model->getStartFrame();
Chris@197 423 size_t modelEnd = m_model->getEndFrame();
Chris@197 424 size_t modelResolution = m_model->getResolution();
Chris@197 425
Chris@224 426 // std::cerr << "Colour3DPlotLayer::fillCache: " << firstBin << " -> " << lastBin << std::endl;
Chris@197 427
Chris@197 428 if (!m_normalizeVisibleArea || m_normalizeColumns) {
Chris@197 429 firstBin = modelStart / modelResolution;
Chris@197 430 lastBin = modelEnd / modelResolution;
Chris@197 431 }
Chris@197 432
Chris@197 433 size_t cacheWidth = lastBin - firstBin + 1;
Chris@197 434 size_t cacheHeight = m_model->getHeight();
Chris@197 435
Chris@197 436 if (m_cache &&
Chris@197 437 (m_cacheStart != firstBin ||
Chris@248 438 m_cache->width() != int(cacheWidth) ||
Chris@248 439 m_cache->height() != int(cacheHeight))) {
Chris@197 440
Chris@197 441 delete m_cache;
Chris@197 442 m_cache = 0;
Chris@197 443 }
Chris@197 444
Chris@197 445 if (m_cache) return;
Chris@197 446
Chris@197 447 m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
Chris@197 448 m_cacheStart = firstBin;
Chris@197 449
Chris@224 450 // std::cerr << "Cache size " << cacheWidth << "x" << cacheHeight << " starting " << m_cacheStart << std::endl;
Chris@197 451
Chris@197 452 m_cache->setNumColors(256);
Chris@197 453 DenseThreeDimensionalModel::Column values;
Chris@197 454
Chris@197 455 float min = m_model->getMinimumLevel();
Chris@197 456 float max = m_model->getMaximumLevel();
Chris@197 457
Chris@197 458 if (m_colourScale == LogScale) {
Chris@197 459 LogRange::mapRange(min, max);
Chris@197 460 } else if (m_colourScale == PlusMinusOneScale) {
Chris@197 461 min = -1.f;
Chris@197 462 max = 1.f;
Chris@197 463 }
Chris@197 464
Chris@197 465 if (max == min) max = min + 1.0;
Chris@197 466
Chris@197 467 ColourMapper mapper(m_colourMap, 0.f, 255.f);
Chris@197 468
Chris@197 469 for (int index = 0; index < 256; ++index) {
Chris@197 470
Chris@197 471 QColor colour = mapper.map(index);
Chris@197 472 m_cache->setColor(index, qRgb(colour.red(), colour.green(), colour.blue()));
Chris@197 473 }
Chris@197 474
Chris@197 475 m_cache->fill(0);
Chris@197 476
Chris@224 477 float visibleMax = 0.f, visibleMin = 0.f;
Chris@197 478
Chris@197 479 if (m_normalizeVisibleArea && !m_normalizeColumns) {
Chris@197 480
Chris@197 481 for (size_t c = firstBin; c <= lastBin; ++c) {
Chris@197 482
Chris@197 483 values.clear();
Chris@197 484 getColumn(c, values);
Chris@197 485
Chris@224 486 float colMax = 0.f, colMin = 0.f;
Chris@197 487
Chris@197 488 for (size_t y = 0; y < m_model->getHeight(); ++y) {
Chris@197 489 if (y >= values.size()) break;
Chris@197 490 if (y == 0 || values[y] > colMax) colMax = values[y];
Chris@224 491 if (y == 0 || values[y] < colMin) colMin = values[y];
Chris@197 492 }
Chris@197 493
Chris@197 494 if (c == firstBin || colMax > visibleMax) visibleMax = colMax;
Chris@224 495 if (c == firstBin || colMin < visibleMin) visibleMin = colMin;
Chris@197 496 }
Chris@197 497 }
Chris@197 498
Chris@224 499 if (visibleMin == visibleMax) visibleMax = visibleMin + 1;
Chris@224 500
Chris@197 501 for (size_t c = firstBin; c <= lastBin; ++c) {
Chris@197 502
Chris@197 503 values.clear();
Chris@197 504 getColumn(c, values);
Chris@197 505
Chris@197 506 for (size_t y = 0; y < m_model->getHeight(); ++y) {
Chris@197 507
Chris@197 508 float value = min;
Chris@197 509 if (y < values.size()) {
Chris@197 510 value = values[y];
Chris@197 511 }
Chris@197 512
Chris@197 513 if (m_normalizeVisibleArea && !m_normalizeColumns) {
Chris@224 514 float norm = (value - visibleMin) / (visibleMax - visibleMin);
Chris@224 515 value = min + (max - min) * norm;
Chris@224 516 }
Chris@224 517
Chris@224 518 if (m_colourScale == LogScale) {
Chris@224 519 value = LogRange::map(value);
Chris@197 520 }
Chris@197 521
Chris@197 522 int pixel = int(((value - min) * 256) / (max - min));
Chris@197 523 if (pixel < 0) pixel = 0;
Chris@197 524 if (pixel > 255) pixel = 255;
Chris@197 525
Chris@197 526 m_cache->setPixel(c - firstBin, y, pixel);
Chris@197 527 }
Chris@197 528 }
Chris@197 529 }
Chris@197 530
Chris@197 531 void
Chris@44 532 Colour3DPlotLayer::paint(View *v, QPainter &paint, QRect rect) const
Chris@0 533 {
Chris@0 534 // Profiler profiler("Colour3DPlotLayer::paint");
Chris@125 535 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@125 536 std::cerr << "Colour3DPlotLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << std::endl;
Chris@125 537 #endif
Chris@0 538
Chris@0 539 int completion = 0;
Chris@0 540 if (!m_model || !m_model->isOK() || !m_model->isReady(&completion)) {
Chris@0 541 if (completion > 0) {
Chris@44 542 paint.fillRect(0, 10, v->width() * completion / 100,
Chris@0 543 10, QColor(120, 120, 120));
Chris@0 544 }
Chris@0 545 return;
Chris@0 546 }
Chris@0 547
Chris@197 548 if (m_normalizeVisibleArea && !m_normalizeColumns) rect = v->rect();
Chris@197 549
Chris@0 550 size_t modelStart = m_model->getStartFrame();
Chris@0 551 size_t modelEnd = m_model->getEndFrame();
Chris@130 552 size_t modelResolution = m_model->getResolution();
Chris@0 553
Chris@197 554 // The cache is from the model's start frame to the model's end
Chris@197 555 // frame at the model's window increment frames per pixel. We
Chris@197 556 // want to draw from our start frame + x0 * zoomLevel to our start
Chris@197 557 // frame + x1 * zoomLevel at zoomLevel frames per pixel.
Chris@12 558
Chris@197 559 // We have quite different paint mechanisms for rendering "large"
Chris@197 560 // bins (more than one bin per pixel in both directions) and
Chris@197 561 // "small". This is "large"; see paintDense below for "small".
Chris@12 562
Chris@197 563 int x0 = rect.left();
Chris@197 564 int x1 = rect.right() + 1;
Chris@12 565
Chris@197 566 int h = v->height();
Chris@0 567
Chris@197 568 float srRatio =
Chris@197 569 float(v->getViewManager()->getMainModelSampleRate()) /
Chris@197 570 float(m_model->getSampleRate());
Chris@0 571
Chris@197 572 int sx0 = int((v->getFrameForX(x0) / srRatio - long(modelStart))
Chris@197 573 / long(modelResolution));
Chris@197 574 int sx1 = int((v->getFrameForX(x1) / srRatio - long(modelStart))
Chris@197 575 / long(modelResolution));
Chris@197 576 int sh = m_model->getHeight();
Chris@159 577
Chris@197 578 if (sx0 > 0) --sx0;
Chris@197 579 fillCache(sx0 < 0 ? 0 : sx0,
Chris@197 580 sx1 < 0 ? 0 : sx1);
Chris@0 581
Chris@248 582 if (int(m_model->getHeight()) >= v->height() ||
Chris@248 583 int(modelResolution) < v->getZoomLevel() / 2) {
Chris@98 584 paintDense(v, paint, rect);
Chris@98 585 return;
Chris@98 586 }
Chris@98 587
Chris@125 588 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@0 589 std::cerr << "Colour3DPlotLayer::paint: w " << w << ", h " << h << ", sx0 " << sx0 << ", sx1 " << sx1 << ", sw " << sw << ", sh " << sh << std::endl;
Chris@130 590 std::cerr << "Colour3DPlotLayer: sample rate is " << m_model->getSampleRate() << ", resolution " << m_model->getResolution() << std::endl;
Chris@125 591 #endif
Chris@0 592
Chris@25 593 QPoint illuminatePos;
Chris@44 594 bool illuminate = v->shouldIlluminateLocalFeatures(this, illuminatePos);
Chris@54 595 char labelbuf[10];
Chris@25 596
Chris@197 597 for (int sx = sx0; sx <= sx1; ++sx) {
Chris@0 598
Chris@197 599 int scx = 0;
Chris@248 600 if (sx > int(m_cacheStart)) scx = sx - m_cacheStart;
Chris@197 601
Chris@130 602 int fx = sx * int(modelResolution);
Chris@0 603
Chris@248 604 if (fx + int(modelResolution) < int(modelStart) ||
Chris@0 605 fx > int(modelEnd)) continue;
Chris@0 606
Chris@248 607 int rx0 = v->getXForFrame(int((fx + int(modelStart)) * srRatio));
Chris@248 608 int rx1 = v->getXForFrame(int((fx + int(modelStart) + int(modelResolution) + 1) * srRatio));
Chris@54 609
Chris@159 610 int rw = rx1 - rx0;
Chris@159 611 if (rw < 1) rw = 1;
Chris@54 612
Chris@159 613 bool showLabel = (rw > 10 &&
Chris@159 614 paint.fontMetrics().width("0.000000") < rw - 3 &&
Chris@54 615 paint.fontMetrics().height() < (h / sh));
Chris@98 616
Chris@0 617 for (int sy = 0; sy < sh; ++sy) {
Chris@0 618
Chris@0 619 int ry0 = h - (sy * h) / sh - 1;
Chris@0 620 QRgb pixel = qRgb(255, 255, 255);
Chris@197 621 if (scx >= 0 && scx < m_cache->width() &&
Chris@0 622 sy >= 0 && sy < m_cache->height()) {
Chris@197 623 pixel = m_cache->pixel(scx, sy);
Chris@0 624 }
Chris@0 625
Chris@159 626 QRect r(rx0, ry0 - h / sh - 1, rw, h / sh + 1);
Chris@159 627
Chris@159 628 if (rw == 1) {
Chris@159 629 paint.setPen(pixel);
Chris@159 630 paint.setBrush(Qt::NoBrush);
Chris@159 631 paint.drawLine(r.x(), r.y(), r.x(), r.y() + r.height() - 1);
Chris@159 632 continue;
Chris@159 633 }
Chris@159 634
Chris@0 635 QColor pen(255, 255, 255, 80);
Chris@0 636 QColor brush(pixel);
Chris@159 637
Chris@159 638 if (rw > 3 && r.height() > 3) {
Chris@159 639 brush.setAlpha(160);
Chris@159 640 }
Chris@159 641
Chris@0 642 paint.setPen(Qt::NoPen);
Chris@0 643 paint.setBrush(brush);
Chris@0 644
Chris@25 645 if (illuminate) {
Chris@25 646 if (r.contains(illuminatePos)) {
Chris@287 647 paint.setPen(v->getForeground());
Chris@25 648 }
Chris@25 649 }
Chris@76 650
Chris@125 651 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@159 652 std::cerr << "rect " << r.x() << "," << r.y() << " "
Chris@159 653 << r.width() << "x" << r.height() << std::endl;
Chris@125 654 #endif
Chris@25 655
Chris@25 656 paint.drawRect(r);
Chris@0 657
Chris@54 658 if (showLabel) {
Chris@197 659 if (scx >= 0 && scx < m_cache->width() &&
Chris@54 660 sy >= 0 && sy < m_cache->height()) {
Chris@197 661 float value = m_model->getValueAt(scx, sy);
Chris@54 662 sprintf(labelbuf, "%06f", value);
Chris@54 663 QString text(labelbuf);
Chris@287 664 paint.setPen(v->getBackground());
Chris@54 665 paint.drawText(rx0 + 2,
Chris@54 666 ry0 - h / sh - 1 + 2 + paint.fontMetrics().ascent(),
Chris@54 667 text);
Chris@0 668 }
Chris@0 669 }
Chris@0 670 }
Chris@0 671 }
Chris@98 672 }
Chris@0 673
Chris@98 674 void
Chris@98 675 Colour3DPlotLayer::paintDense(View *v, QPainter &paint, QRect rect) const
Chris@98 676 {
Chris@98 677 size_t modelStart = m_model->getStartFrame();
Chris@130 678 size_t modelResolution = m_model->getResolution();
Chris@0 679
Chris@159 680 float srRatio =
Chris@159 681 float(v->getViewManager()->getMainModelSampleRate()) /
Chris@159 682 float(m_model->getSampleRate());
Chris@159 683
Chris@98 684 int x0 = rect.left();
Chris@98 685 int x1 = rect.right() + 1;
Chris@0 686
Chris@98 687 int w = x1 - x0;
Chris@98 688 int h = v->height();
Chris@160 689 int sh = m_model->getHeight();
Chris@98 690
Chris@98 691 QImage img(w, h, QImage::Format_RGB32);
Chris@98 692
Chris@98 693 for (int x = x0; x < x1; ++x) {
Chris@98 694
Chris@248 695 long xf = long(v->getFrameForX(x) / srRatio);
Chris@105 696 if (xf < 0) {
Chris@105 697 for (int y = 0; y < h; ++y) {
Chris@105 698 img.setPixel(x - x0, y, m_cache->color(0));
Chris@105 699 }
Chris@105 700 continue;
Chris@105 701 }
Chris@105 702
Chris@130 703 float sx0 = (float(xf) - modelStart) / modelResolution;
Chris@159 704 float sx1 = (float(v->getFrameForX(x+1) / srRatio) - modelStart) / modelResolution;
Chris@98 705
Chris@98 706 int sx0i = int(sx0 + 0.001);
Chris@98 707 int sx1i = int(sx1);
Chris@98 708
Chris@98 709 for (int y = 0; y < h; ++y) {
Chris@98 710
Chris@98 711 float sy0 = (float(h - y - 1) * sh) / h;
Chris@98 712 float sy1 = (float(h - y) * sh) / h;
Chris@98 713
Chris@98 714 int sy0i = int(sy0 + 0.001);
Chris@98 715 int sy1i = int(sy1);
Chris@98 716
Chris@98 717 float mag = 0.0, div = 0.0;
Chris@159 718 int max = 0;
Chris@98 719
Chris@98 720 for (int sx = sx0i; sx <= sx1i; ++sx) {
Chris@98 721
Chris@197 722 int scx = 0;
Chris@248 723 if (sx > int(m_cacheStart)) scx = sx - int(m_cacheStart);
Chris@197 724
Chris@197 725 if (scx < 0 || scx >= m_cache->width()) continue;
Chris@98 726
Chris@98 727 for (int sy = sy0i; sy <= sy1i; ++sy) {
Chris@98 728
Chris@98 729 if (sy < 0 || sy >= m_cache->height()) continue;
Chris@98 730
Chris@98 731 float prop = 1.0;
Chris@98 732 if (sx == sx0i) prop *= (sx + 1) - sx0;
Chris@98 733 if (sx == sx1i) prop *= sx1 - sx;
Chris@98 734 if (sy == sy0i) prop *= (sy + 1) - sy0;
Chris@98 735 if (sy == sy1i) prop *= sy1 - sy;
Chris@98 736
Chris@197 737 mag += prop * m_cache->pixelIndex(scx, sy);
Chris@197 738 max = std::max(max, m_cache->pixelIndex(scx, sy));
Chris@98 739 div += prop;
Chris@98 740 }
Chris@98 741 }
Chris@98 742
Chris@98 743 if (div != 0) mag /= div;
Chris@98 744 if (mag < 0) mag = 0;
Chris@98 745 if (mag > 255) mag = 255;
Chris@159 746 if (max < 0) max = 0;
Chris@159 747 if (max > 255) max = 255;
Chris@98 748
Chris@98 749 img.setPixel(x - x0, y, m_cache->color(int(mag + 0.001)));
Chris@159 750 // img.setPixel(x - x0, y, m_cache->color(max));
Chris@98 751 }
Chris@0 752 }
Chris@0 753
Chris@98 754 paint.drawImage(x0, 0, img);
Chris@0 755 }
Chris@0 756
Chris@28 757 bool
Chris@44 758 Colour3DPlotLayer::snapToFeatureFrame(View *v, int &frame,
Chris@28 759 size_t &resolution,
Chris@28 760 SnapType snap) const
Chris@24 761 {
Chris@24 762 if (!m_model) {
Chris@44 763 return Layer::snapToFeatureFrame(v, frame, resolution, snap);
Chris@24 764 }
Chris@24 765
Chris@130 766 resolution = m_model->getResolution();
Chris@28 767 int left = (frame / resolution) * resolution;
Chris@28 768 int right = left + resolution;
Chris@28 769
Chris@28 770 switch (snap) {
Chris@28 771 case SnapLeft: frame = left; break;
Chris@28 772 case SnapRight: frame = right; break;
Chris@28 773 case SnapNearest:
Chris@28 774 case SnapNeighbouring:
Chris@28 775 if (frame - left > right - frame) frame = right;
Chris@28 776 else frame = left;
Chris@28 777 break;
Chris@28 778 }
Chris@24 779
Chris@28 780 return true;
Chris@24 781 }
Chris@24 782
Chris@316 783 void
Chris@316 784 Colour3DPlotLayer::toXml(QTextStream &stream,
Chris@316 785 QString indent, QString extraAttributes) const
Chris@197 786 {
Chris@316 787 QString s = QString("scale=\"%1\" "
Chris@316 788 "colourScheme=\"%2\" "
Chris@316 789 "normalizeColumns=\"%3\" "
Chris@316 790 "normalizeVisibleArea=\"%4\"")
Chris@197 791 .arg((int)m_colourScale)
Chris@197 792 .arg(m_colourMap)
Chris@197 793 .arg(m_normalizeColumns ? "true" : "false")
Chris@197 794 .arg(m_normalizeVisibleArea ? "true" : "false");
Chris@197 795
Chris@316 796 Layer::toXml(stream, indent, extraAttributes + " " + s);
Chris@197 797 }
Chris@197 798
Chris@197 799 void
Chris@197 800 Colour3DPlotLayer::setProperties(const QXmlAttributes &attributes)
Chris@197 801 {
Chris@197 802 bool ok = false;
Chris@197 803
Chris@197 804 ColourScale scale = (ColourScale)attributes.value("scale").toInt(&ok);
Chris@197 805 if (ok) setColourScale(scale);
Chris@197 806
Chris@197 807 int colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@197 808 if (ok) setColourMap(colourMap);
Chris@197 809
Chris@197 810 bool normalizeColumns =
Chris@197 811 (attributes.value("normalizeColumns").trimmed() == "true");
Chris@197 812 setNormalizeColumns(normalizeColumns);
Chris@197 813
Chris@197 814 bool normalizeVisibleArea =
Chris@197 815 (attributes.value("normalizeVisibleArea").trimmed() == "true");
Chris@197 816 setNormalizeVisibleArea(normalizeVisibleArea);
Chris@197 817 }
Chris@197 818