annotate layer/Colour3DPlotLayer.cpp @ 1605:ae2d5f8ff005

When asked to render the whole view width, we need to wait for the layers to be ready before we can determine what the width is
author Chris Cannam
date Thu, 30 Apr 2020 14:47:13 +0100
parents 5f6fdd525158
children
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@0 18 #include "base/Profiler.h"
Chris@197 19 #include "base/LogRange.h"
Chris@444 20 #include "base/RangeMapper.h"
Chris@1077 21
Chris@376 22 #include "ColourMapper.h"
Chris@1077 23 #include "LayerGeometryProvider.h"
Chris@1078 24 #include "PaintAssistant.h"
Chris@1563 25 #include "Colour3DPlotExporter.h"
Chris@1077 26
Chris@1100 27 #include "data/model/Dense3DModelPeakCache.h"
Chris@1100 28
Chris@1077 29 #include "view/ViewManager.h"
Chris@0 30
Chris@0 31 #include <QPainter>
Chris@0 32 #include <QImage>
Chris@0 33 #include <QRect>
Chris@316 34 #include <QTextStream>
Chris@1018 35 #include <QSettings>
Chris@0 36
Chris@0 37 #include <iostream>
Chris@0 38
Chris@0 39 #include <cassert>
Chris@0 40
Chris@903 41 using std::vector;
Chris@903 42
Chris@353 43 //#define DEBUG_COLOUR_3D_PLOT_LAYER_PAINT 1
Chris@125 44
Chris@0 45
Chris@44 46 Colour3DPlotLayer::Colour3DPlotLayer() :
Chris@1105 47 m_colourScale(ColourScaleType::Linear),
Chris@461 48 m_colourScaleSet(false),
Chris@197 49 m_colourMap(0),
Chris@1362 50 m_colourInverted(false),
Chris@534 51 m_gain(1.0),
Chris@1103 52 m_binScale(BinScale::Linear),
Chris@1104 53 m_normalization(ColumnNormalization::None),
Chris@1104 54 m_normalizeVisibleArea(false),
Chris@444 55 m_invertVertical(false),
Chris@465 56 m_opaque(false),
Chris@535 57 m_smooth(false),
Chris@805 58 m_peakResolution(256),
Chris@444 59 m_miny(0),
Chris@1100 60 m_maxy(0),
Chris@1101 61 m_synchronous(false),
Chris@1100 62 m_peakCacheDivisor(8)
Chris@0 63 {
Chris@1018 64 QSettings settings;
Chris@1018 65 settings.beginGroup("Preferences");
Chris@1469 66 setColourMap(settings.value("colour-3d-plot-colour",
Chris@1469 67 ColourMapper::Green).toInt());
Chris@1018 68 settings.endGroup();
Chris@0 69 }
Chris@0 70
Chris@0 71 Colour3DPlotLayer::~Colour3DPlotLayer()
Chris@0 72 {
Chris@1107 73 invalidateRenderers();
Chris@1563 74
Chris@1563 75 for (auto exporterId: m_exporters) {
Chris@1563 76 if (auto exporter =
Chris@1563 77 ModelById::getAs<Colour3DPlotExporter>(exporterId)) {
Chris@1563 78 exporter->discardSources();
Chris@1563 79 }
Chris@1563 80 ModelById::release(exporterId);
Chris@1563 81 }
Chris@1469 82 }
Chris@1469 83
Chris@1469 84 const ZoomConstraint *
Chris@1469 85 Colour3DPlotLayer::getZoomConstraint() const
Chris@1469 86 {
Chris@1469 87 auto model = ModelById::get(m_model);
Chris@1469 88 if (model) return model->getZoomConstraint();
Chris@1469 89 else return nullptr;
Chris@0 90 }
Chris@0 91
Chris@1105 92 ColourScaleType
Chris@1104 93 Colour3DPlotLayer::convertToColourScale(int value)
Chris@1104 94 {
Chris@1104 95 switch (value) {
Chris@1104 96 default:
Chris@1105 97 case 0: return ColourScaleType::Linear;
Chris@1105 98 case 1: return ColourScaleType::Log;
Chris@1105 99 case 2: return ColourScaleType::PlusMinusOne;
Chris@1105 100 case 3: return ColourScaleType::Absolute;
Chris@1104 101 }
Chris@1104 102 }
Chris@1104 103
Chris@1104 104 int
Chris@1105 105 Colour3DPlotLayer::convertFromColourScale(ColourScaleType scale)
Chris@1104 106 {
Chris@1104 107 switch (scale) {
Chris@1105 108 case ColourScaleType::Linear: return 0;
Chris@1105 109 case ColourScaleType::Log: return 1;
Chris@1105 110 case ColourScaleType::PlusMinusOne: return 2;
Chris@1105 111 case ColourScaleType::Absolute: return 3;
Chris@1104 112
Chris@1105 113 case ColourScaleType::Meter:
Chris@1105 114 case ColourScaleType::Phase:
Chris@1104 115 default: return 0;
Chris@1104 116 }
Chris@1104 117 }
Chris@1104 118
Chris@1104 119 std::pair<ColumnNormalization, bool>
Chris@1104 120 Colour3DPlotLayer::convertToColumnNorm(int value)
Chris@1104 121 {
Chris@1104 122 switch (value) {
Chris@1104 123 default:
Chris@1104 124 case 0: return { ColumnNormalization::None, false };
Chris@1245 125 case 1: return { ColumnNormalization::Range01, false };
Chris@1104 126 case 2: return { ColumnNormalization::None, true }; // visible area
Chris@1104 127 case 3: return { ColumnNormalization::Hybrid, false };
Chris@1104 128 }
Chris@1104 129 }
Chris@1104 130
Chris@1104 131 int
Chris@1104 132 Colour3DPlotLayer::convertFromColumnNorm(ColumnNormalization norm, bool visible)
Chris@1104 133 {
Chris@1104 134 if (visible) return 2;
Chris@1104 135 switch (norm) {
Chris@1104 136 case ColumnNormalization::None: return 0;
Chris@1245 137 case ColumnNormalization::Range01: return 1;
Chris@1104 138 case ColumnNormalization::Hybrid: return 3;
Chris@1104 139
Chris@1104 140 case ColumnNormalization::Sum1:
Chris@1245 141 case ColumnNormalization::Max1:
Chris@1104 142 default: return 0;
Chris@1104 143 }
Chris@1104 144 }
Chris@1104 145
Chris@0 146 void
Chris@1101 147 Colour3DPlotLayer::setSynchronousPainting(bool synchronous)
Chris@1101 148 {
Chris@1101 149 m_synchronous = synchronous;
Chris@1101 150 }
Chris@1101 151
Chris@1101 152 void
Chris@1469 153 Colour3DPlotLayer::setModel(ModelId modelId)
Chris@0 154 {
Chris@1471 155 auto newModel = ModelById::getAs<DenseThreeDimensionalModel>(modelId);
Chris@1471 156
Chris@1471 157 if (!modelId.isNone() && !newModel) {
Chris@1471 158 throw std::logic_error("Not a DenseThreeDimensionalModel");
Chris@1471 159 }
Chris@1471 160
Chris@1469 161 if (m_model == modelId) return;
Chris@1469 162 m_model = modelId;
Chris@0 163
Chris@1471 164 if (newModel) {
Chris@1471 165 connectSignals(m_model);
Chris@0 166
Chris@1481 167 connect(newModel.get(), SIGNAL(modelChanged(ModelId)),
Chris@1481 168 this, SLOT(handleModelChanged(ModelId)));
Chris@1481 169 connect(newModel.get(), SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)),
Chris@1481 170 this, SLOT(handleModelChangedWithin(ModelId, sv_frame_t, sv_frame_t)));
Chris@0 171
Chris@1471 172 m_peakResolution = 256;
Chris@1471 173 if (newModel->getResolution() > 512) {
Chris@1471 174 m_peakResolution = 16;
Chris@1471 175 } else if (newModel->getResolution() > 128) {
Chris@1471 176 m_peakResolution = 64;
Chris@1471 177 } else if (newModel->getResolution() > 2) {
Chris@1471 178 m_peakResolution = 128;
Chris@1471 179 }
Chris@474 180 }
Chris@1471 181
Chris@1453 182 invalidatePeakCache();
Chris@474 183
Chris@0 184 emit modelReplaced();
Chris@0 185 }
Chris@0 186
Chris@0 187 void
Chris@1453 188 Colour3DPlotLayer::invalidatePeakCache()
Chris@0 189 {
Chris@1453 190 // renderers use the peak cache, so we must invalidate those too
Chris@1107 191 invalidateRenderers();
Chris@1235 192 invalidateMagnitudes();
Chris@1469 193
Chris@1473 194 if (!m_peakCache.isNone()) {
Chris@1473 195 ModelById::release(m_peakCache);
Chris@1473 196 m_peakCache = {};
Chris@1473 197 }
Chris@1107 198 }
Chris@1107 199
Chris@1107 200 void
Chris@1107 201 Colour3DPlotLayer::invalidateRenderers()
Chris@1107 202 {
Chris@1102 203 for (ViewRendererMap::iterator i = m_renderers.begin();
Chris@1102 204 i != m_renderers.end(); ++i) {
Chris@1102 205 delete i->second;
Chris@1102 206 }
Chris@1102 207 m_renderers.clear();
Chris@0 208 }
Chris@0 209
Chris@1235 210 void
Chris@1235 211 Colour3DPlotLayer::invalidateMagnitudes()
Chris@1235 212 {
Chris@1235 213 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@1412 214 SVDEBUG << "Colour3DPlotLayer::invalidateMagnitudes called" << endl;
Chris@1235 215 #endif
Chris@1235 216 m_viewMags.clear();
Chris@1235 217 }
Chris@1235 218
Chris@1473 219 ModelId
Chris@1563 220 Colour3DPlotLayer::getExportModel(LayerGeometryProvider *v) const
Chris@1563 221 {
Chris@1563 222 // Creating Colour3DPlotExporters is cheap, so we create one on
Chris@1563 223 // every call - calls probably being infrequent - to avoid having
Chris@1563 224 // to worry about view lifecycles.
Chris@1563 225
Chris@1563 226 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1563 227 if (!model) return {};
Chris@1563 228
Chris@1563 229 Colour3DPlotExporter::Sources sources;
Chris@1563 230 sources.verticalBinLayer = this;
Chris@1563 231 sources.source = m_model;
Chris@1563 232 sources.provider = v;
Chris@1563 233
Chris@1563 234 Colour3DPlotExporter::Parameters params;
Chris@1572 235 // No thresholding for Colour3DPlot layers, so no need for gain or
Chris@1572 236 // normalisaion values either
Chris@1563 237
Chris@1563 238 ModelId exporter = ModelById::add
Chris@1563 239 (std::make_shared<Colour3DPlotExporter>(sources, params));
Chris@1563 240 m_exporters.push_back(exporter);
Chris@1563 241 return exporter;
Chris@1563 242 }
Chris@1563 243
Chris@1563 244 ModelId
Chris@1100 245 Colour3DPlotLayer::getPeakCache() const
Chris@1100 246 {
Chris@1473 247 if (m_peakCache.isNone()) {
Chris@1473 248 auto peakCache = std::make_shared<Dense3DModelPeakCache>
Chris@1473 249 (m_model, m_peakCacheDivisor);
Chris@1481 250 m_peakCache = ModelById::add(peakCache);
Chris@1100 251 }
Chris@1100 252 return m_peakCache;
Chris@1100 253 }
Chris@1100 254
Chris@461 255 void
Chris@1481 256 Colour3DPlotLayer::handleModelChanged(ModelId modelId)
Chris@461 257 {
Chris@1105 258 if (!m_colourScaleSet && m_colourScale == ColourScaleType::Linear) {
Chris@1469 259 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 260 if (model) {
Chris@1469 261 if (model->shouldUseLogValueScale()) {
Chris@1105 262 setColourScale(ColourScaleType::Log);
Chris@461 263 } else {
Chris@461 264 m_colourScaleSet = true;
Chris@461 265 }
Chris@461 266 }
Chris@461 267 }
Chris@1453 268 invalidatePeakCache();
Chris@1481 269 emit modelChanged(modelId);
Chris@0 270 }
Chris@0 271
Chris@461 272 void
Chris@1481 273 Colour3DPlotLayer::handleModelChangedWithin(ModelId modelId,
Chris@1481 274 sv_frame_t startFrame,
Chris@1481 275 sv_frame_t endFrame)
Chris@461 276 {
Chris@1105 277 if (!m_colourScaleSet && m_colourScale == ColourScaleType::Linear) {
Chris@1469 278 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 279 if (model && model->getWidth() > 50) {
Chris@1469 280 if (model->shouldUseLogValueScale()) {
Chris@1105 281 setColourScale(ColourScaleType::Log);
Chris@461 282 } else {
Chris@461 283 m_colourScaleSet = true;
Chris@461 284 }
Chris@461 285 }
Chris@461 286 }
Chris@1481 287 emit modelChangedWithin(modelId, startFrame, endFrame);
Chris@461 288 }
Chris@461 289
Chris@159 290 Layer::PropertyList
Chris@159 291 Colour3DPlotLayer::getProperties() const
Chris@159 292 {
Chris@159 293 PropertyList list;
Chris@197 294 list.push_back("Colour");
Chris@159 295 list.push_back("Colour Scale");
Chris@1104 296 list.push_back("Normalization");
Chris@534 297 list.push_back("Gain");
Chris@531 298 list.push_back("Bin Scale");
Chris@357 299 list.push_back("Invert Vertical Scale");
Chris@465 300 list.push_back("Opaque");
Chris@535 301 list.push_back("Smooth");
Chris@159 302 return list;
Chris@159 303 }
Chris@159 304
Chris@159 305 QString
Chris@159 306 Colour3DPlotLayer::getPropertyLabel(const PropertyName &name) const
Chris@159 307 {
Chris@197 308 if (name == "Colour") return tr("Colour");
Chris@197 309 if (name == "Colour Scale") return tr("Scale");
Chris@1104 310 if (name == "Normalization") return tr("Normalization");
Chris@357 311 if (name == "Invert Vertical Scale") return tr("Invert Vertical Scale");
Chris@534 312 if (name == "Gain") return tr("Gain");
Chris@465 313 if (name == "Opaque") return tr("Always Opaque");
Chris@535 314 if (name == "Smooth") return tr("Smooth");
Chris@531 315 if (name == "Bin Scale") return tr("Bin Scale");
Chris@159 316 return "";
Chris@159 317 }
Chris@159 318
Chris@346 319 QString
Chris@346 320 Colour3DPlotLayer::getPropertyIconName(const PropertyName &name) const
Chris@346 321 {
Chris@357 322 if (name == "Invert Vertical Scale") return "invert-vertical";
Chris@465 323 if (name == "Opaque") return "opaque";
Chris@535 324 if (name == "Smooth") return "smooth";
Chris@346 325 return "";
Chris@346 326 }
Chris@346 327
Chris@159 328 Layer::PropertyType
Chris@159 329 Colour3DPlotLayer::getPropertyType(const PropertyName &name) const
Chris@159 330 {
Chris@534 331 if (name == "Gain") return RangeProperty;
Chris@357 332 if (name == "Invert Vertical Scale") return ToggleProperty;
Chris@465 333 if (name == "Opaque") return ToggleProperty;
Chris@535 334 if (name == "Smooth") return ToggleProperty;
Chris@1198 335 if (name == "Colour") return ColourMapProperty;
Chris@159 336 return ValueProperty;
Chris@159 337 }
Chris@159 338
Chris@159 339 QString
Chris@159 340 Colour3DPlotLayer::getPropertyGroupName(const PropertyName &name) const
Chris@159 341 {
Chris@1104 342 if (name == "Normalization" ||
Chris@1104 343 name == "Colour Scale" ||
Chris@1365 344 name == "Gain") {
Chris@1365 345 return tr("Scale");
Chris@1365 346 }
Chris@531 347 if (name == "Bin Scale" ||
Chris@1365 348 name == "Invert Vertical Scale") {
Chris@1365 349 return tr("Bins");
Chris@1365 350 }
Chris@465 351 if (name == "Opaque" ||
Chris@535 352 name == "Smooth" ||
Chris@1365 353 name == "Colour") {
Chris@1365 354 return tr("Colour");
Chris@1365 355 }
Chris@159 356 return QString();
Chris@159 357 }
Chris@159 358
Chris@159 359 int
Chris@159 360 Colour3DPlotLayer::getPropertyRangeAndValue(const PropertyName &name,
Chris@216 361 int *min, int *max, int *deflt) const
Chris@159 362 {
Chris@216 363 int val = 0;
Chris@159 364
Chris@216 365 int garbage0, garbage1, garbage2;
Chris@159 366 if (!min) min = &garbage0;
Chris@159 367 if (!max) max = &garbage1;
Chris@216 368 if (!deflt) deflt = &garbage2;
Chris@159 369
Chris@534 370 if (name == "Gain") {
Chris@534 371
Chris@1266 372 *min = -50;
Chris@1266 373 *max = 50;
Chris@534 374
Chris@902 375 *deflt = int(lrint(log10(1.0) * 20.0));
Chris@1266 376 if (*deflt < *min) *deflt = *min;
Chris@1266 377 if (*deflt > *max) *deflt = *max;
Chris@534 378
Chris@1266 379 val = int(lrint(log10(m_gain) * 20.0));
Chris@1266 380 if (val < *min) val = *min;
Chris@1266 381 if (val > *max) val = *max;
Chris@534 382
Chris@534 383 } else if (name == "Colour Scale") {
Chris@159 384
Chris@1099 385 // linear, log, +/-1, abs
Chris@1266 386 *min = 0;
Chris@1266 387 *max = 3;
Chris@1099 388 *deflt = 0;
Chris@159 389
Chris@1266 390 val = convertFromColourScale(m_colourScale);
Chris@159 391
Chris@197 392 } else if (name == "Colour") {
Chris@197 393
Chris@1266 394 *min = 0;
Chris@1266 395 *max = ColourMapper::getColourMapCount() - 1;
Chris@216 396 *deflt = 0;
Chris@197 397
Chris@1266 398 val = m_colourMap;
Chris@197 399
Chris@1099 400 } else if (name == "Normalization") {
Chris@1266 401
Chris@1099 402 *min = 0;
Chris@1099 403 *max = 3;
Chris@1104 404 *deflt = 0;
Chris@1104 405
Chris@1104 406 val = convertFromColumnNorm(m_normalization, m_normalizeVisibleArea);
Chris@197 407
Chris@357 408 } else if (name == "Invert Vertical Scale") {
Chris@1365 409
Chris@1365 410 *min = 0;
Chris@1365 411 *max = 1;
Chris@357 412 *deflt = 0;
Chris@1266 413 val = (m_invertVertical ? 1 : 0);
Chris@357 414
Chris@531 415 } else if (name == "Bin Scale") {
Chris@531 416
Chris@1266 417 *min = 0;
Chris@1266 418 *max = 1;
Chris@1103 419 *deflt = int(BinScale::Linear);
Chris@1266 420 val = (int)m_binScale;
Chris@531 421
Chris@465 422 } else if (name == "Opaque") {
Chris@1266 423
Chris@1365 424 *min = 0;
Chris@1365 425 *max = 1;
Chris@465 426 *deflt = 0;
Chris@1266 427 val = (m_opaque ? 1 : 0);
Chris@465 428
Chris@535 429 } else if (name == "Smooth") {
Chris@1266 430
Chris@1365 431 *min = 0;
Chris@1365 432 *max = 1;
Chris@535 433 *deflt = 0;
Chris@1266 434 val = (m_smooth ? 1 : 0);
Chris@535 435
Chris@159 436 } else {
Chris@1266 437 val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
Chris@159 438 }
Chris@159 439
Chris@216 440 return val;
Chris@159 441 }
Chris@159 442
Chris@159 443 QString
Chris@159 444 Colour3DPlotLayer::getPropertyValueLabel(const PropertyName &name,
Chris@1266 445 int value) const
Chris@159 446 {
Chris@197 447 if (name == "Colour") {
Chris@1362 448 return ColourMapper::getColourMapLabel(value);
Chris@197 449 }
Chris@159 450 if (name == "Colour Scale") {
Chris@1266 451 switch (value) {
Chris@1266 452 default:
Chris@1266 453 case 0: return tr("Linear");
Chris@1266 454 case 1: return tr("Log");
Chris@1266 455 case 2: return tr("+/-1");
Chris@1266 456 case 3: return tr("Absolute");
Chris@1266 457 }
Chris@159 458 }
Chris@1099 459 if (name == "Normalization") {
Chris@1209 460 switch(value) {
Chris@1209 461 default:
Chris@1209 462 case 0: return tr("None");
Chris@1209 463 case 1: return tr("Col");
Chris@1209 464 case 2: return tr("View");
Chris@1209 465 case 3: return tr("Hybrid");
Chris@1209 466 }
Chris@1209 467 // return ""; // icon only
Chris@1099 468 }
Chris@531 469 if (name == "Bin Scale") {
Chris@1266 470 switch (value) {
Chris@1266 471 default:
Chris@1266 472 case 0: return tr("Linear");
Chris@1266 473 case 1: return tr("Log");
Chris@1266 474 }
Chris@531 475 }
Chris@159 476 return tr("<unknown>");
Chris@159 477 }
Chris@159 478
Chris@1099 479 QString
Chris@1099 480 Colour3DPlotLayer::getPropertyValueIconName(const PropertyName &name,
Chris@1099 481 int value) const
Chris@1099 482 {
Chris@1099 483 if (name == "Normalization") {
Chris@1099 484 switch(value) {
Chris@1099 485 default:
Chris@1099 486 case 0: return "normalise-none";
Chris@1099 487 case 1: return "normalise-columns";
Chris@1099 488 case 2: return "normalise";
Chris@1099 489 case 3: return "normalise-hybrid";
Chris@1099 490 }
Chris@1099 491 }
Chris@1099 492 return "";
Chris@1099 493 }
Chris@1099 494
Chris@534 495 RangeMapper *
Chris@534 496 Colour3DPlotLayer::getNewPropertyRangeMapper(const PropertyName &name) const
Chris@534 497 {
Chris@534 498 if (name == "Gain") {
Chris@534 499 return new LinearRangeMapper(-50, 50, -25, 25, tr("dB"));
Chris@534 500 }
Chris@1408 501 return nullptr;
Chris@534 502 }
Chris@534 503
Chris@159 504 void
Chris@159 505 Colour3DPlotLayer::setProperty(const PropertyName &name, int value)
Chris@159 506 {
Chris@534 507 if (name == "Gain") {
Chris@1266 508 setGain(float(pow(10, value/20.0)));
Chris@534 509 } else if (name == "Colour Scale") {
Chris@1104 510 setColourScale(convertToColourScale(value));
Chris@197 511 } else if (name == "Colour") {
Chris@197 512 setColourMap(value);
Chris@357 513 } else if (name == "Invert Vertical Scale") {
Chris@1266 514 setInvertVertical(value ? true : false);
Chris@465 515 } else if (name == "Opaque") {
Chris@1266 516 setOpaque(value ? true : false);
Chris@535 517 } else if (name == "Smooth") {
Chris@1266 518 setSmooth(value ? true : false);
Chris@531 519 } else if (name == "Bin Scale") {
Chris@1266 520 switch (value) {
Chris@1266 521 default:
Chris@1266 522 case 0: setBinScale(BinScale::Linear); break;
Chris@1266 523 case 1: setBinScale(BinScale::Log); break;
Chris@1266 524 }
Chris@1099 525 } else if (name == "Normalization") {
Chris@1104 526 auto n = convertToColumnNorm(value);
Chris@1104 527 setNormalization(n.first);
Chris@1104 528 setNormalizeVisibleArea(n.second);
Chris@159 529 }
Chris@159 530 }
Chris@159 531
Chris@159 532 void
Chris@1105 533 Colour3DPlotLayer::setColourScale(ColourScaleType scale)
Chris@159 534 {
Chris@1254 535 m_colourScaleSet = true; // even if setting to the same thing
Chris@159 536 if (m_colourScale == scale) return;
Chris@159 537 m_colourScale = scale;
Chris@1107 538 invalidateRenderers();
Chris@159 539 emit layerParametersChanged();
Chris@159 540 }
Chris@159 541
Chris@197 542 void
Chris@197 543 Colour3DPlotLayer::setColourMap(int map)
Chris@197 544 {
Chris@197 545 if (m_colourMap == map) return;
Chris@197 546 m_colourMap = map;
Chris@1107 547 invalidateRenderers();
Chris@197 548 emit layerParametersChanged();
Chris@197 549 }
Chris@197 550
Chris@197 551 void
Chris@534 552 Colour3DPlotLayer::setGain(float gain)
Chris@534 553 {
Chris@534 554 if (m_gain == gain) return;
Chris@534 555 m_gain = gain;
Chris@1107 556 invalidateRenderers();
Chris@534 557 emit layerParametersChanged();
Chris@534 558 }
Chris@534 559
Chris@534 560 float
Chris@534 561 Colour3DPlotLayer::getGain() const
Chris@534 562 {
Chris@534 563 return m_gain;
Chris@534 564 }
Chris@534 565
Chris@534 566 void
Chris@1103 567 Colour3DPlotLayer::setBinScale(BinScale binScale)
Chris@531 568 {
Chris@531 569 if (m_binScale == binScale) return;
Chris@531 570 m_binScale = binScale;
Chris@1107 571 invalidateRenderers();
Chris@531 572 emit layerParametersChanged();
Chris@531 573 }
Chris@531 574
Chris@1103 575 BinScale
Chris@531 576 Colour3DPlotLayer::getBinScale() const
Chris@531 577 {
Chris@531 578 return m_binScale;
Chris@531 579 }
Chris@531 580
Chris@531 581 void
Chris@1104 582 Colour3DPlotLayer::setNormalization(ColumnNormalization n)
Chris@197 583 {
Chris@1099 584 if (m_normalization == n) return;
Chris@1099 585
Chris@1099 586 m_normalization = n;
Chris@1107 587 invalidateRenderers();
Chris@1099 588
Chris@197 589 emit layerParametersChanged();
Chris@197 590 }
Chris@197 591
Chris@1104 592 ColumnNormalization
Chris@1099 593 Colour3DPlotLayer::getNormalization() const
Chris@197 594 {
Chris@1099 595 return m_normalization;
Chris@197 596 }
Chris@197 597
Chris@357 598 void
Chris@1104 599 Colour3DPlotLayer::setNormalizeVisibleArea(bool n)
Chris@1104 600 {
Chris@1104 601 if (m_normalizeVisibleArea == n) return;
Chris@1104 602
Chris@1235 603 invalidateRenderers();
Chris@1235 604 invalidateMagnitudes();
Chris@1104 605 m_normalizeVisibleArea = n;
Chris@1104 606
Chris@1104 607 emit layerParametersChanged();
Chris@1104 608 }
Chris@1104 609
Chris@1104 610 bool
Chris@1104 611 Colour3DPlotLayer::getNormalizeVisibleArea() const
Chris@1104 612 {
Chris@1104 613 return m_normalizeVisibleArea;
Chris@1104 614 }
Chris@1104 615
Chris@1104 616 void
Chris@357 617 Colour3DPlotLayer::setInvertVertical(bool n)
Chris@357 618 {
Chris@357 619 if (m_invertVertical == n) return;
Chris@357 620 m_invertVertical = n;
Chris@1107 621 invalidateRenderers();
Chris@357 622 emit layerParametersChanged();
Chris@357 623 }
Chris@357 624
Chris@465 625 void
Chris@465 626 Colour3DPlotLayer::setOpaque(bool n)
Chris@465 627 {
Chris@465 628 if (m_opaque == n) return;
Chris@465 629 m_opaque = n;
Chris@1107 630 invalidateRenderers();
Chris@465 631 emit layerParametersChanged();
Chris@465 632 }
Chris@465 633
Chris@535 634 void
Chris@535 635 Colour3DPlotLayer::setSmooth(bool n)
Chris@535 636 {
Chris@535 637 if (m_smooth == n) return;
Chris@535 638 m_smooth = n;
Chris@1107 639 invalidateRenderers();
Chris@535 640 emit layerParametersChanged();
Chris@535 641 }
Chris@535 642
Chris@357 643 bool
Chris@357 644 Colour3DPlotLayer::getInvertVertical() const
Chris@357 645 {
Chris@357 646 return m_invertVertical;
Chris@357 647 }
Chris@357 648
Chris@25 649 bool
Chris@465 650 Colour3DPlotLayer::getOpaque() const
Chris@465 651 {
Chris@465 652 return m_opaque;
Chris@465 653 }
Chris@465 654
Chris@535 655 bool
Chris@535 656 Colour3DPlotLayer::getSmooth() const
Chris@535 657 {
Chris@535 658 return m_smooth;
Chris@535 659 }
Chris@535 660
Chris@1453 661 bool
Chris@1453 662 Colour3DPlotLayer::hasLightBackground() const
Chris@1453 663 {
Chris@1453 664 return ColourMapper(m_colourMap, m_colourInverted, 1.f, 255.f)
Chris@1453 665 .hasLightBackground();
Chris@1453 666 }
Chris@1453 667
Chris@475 668 void
Chris@916 669 Colour3DPlotLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant)
Chris@475 670 {
Chris@475 671 if (dormant) {
Chris@475 672
Chris@475 673 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@1412 674 SVDEBUG << "Colour3DPlotLayer::setLayerDormant(" << dormant << ")"
Chris@585 675 << endl;
Chris@475 676 #endif
Chris@475 677
Chris@475 678 if (isLayerDormant(v)) {
Chris@475 679 return;
Chris@475 680 }
Chris@475 681
Chris@475 682 Layer::setLayerDormant(v, true);
Chris@475 683
Chris@1453 684 invalidatePeakCache(); // for memory-saving purposes
Chris@1266 685
Chris@475 686 } else {
Chris@475 687
Chris@475 688 Layer::setLayerDormant(v, false);
Chris@475 689 }
Chris@475 690 }
Chris@475 691
Chris@465 692 bool
Chris@1150 693 Colour3DPlotLayer::isLayerScrollable(const LayerGeometryProvider * /* v */) const
Chris@25 694 {
Chris@1419 695 // we do our own cacheing, and don't want to be responsible for
Chris@1419 696 // guaranteeing to get an invisible seam if someone else scrolls
Chris@1419 697 // us and we just fill in
Chris@1121 698 return false;
Chris@25 699 }
Chris@25 700
Chris@1469 701 int
Chris@1469 702 Colour3DPlotLayer::getCompletion(LayerGeometryProvider *) const
Chris@1469 703 {
Chris@1469 704 auto model = ModelById::get(m_model);
Chris@1469 705 if (model) return model->getCompletion();
Chris@1469 706 else return 0;
Chris@1469 707 }
Chris@1469 708
Chris@444 709 bool
Chris@904 710 Colour3DPlotLayer::getValueExtents(double &min, double &max,
Chris@444 711 bool &logarithmic, QString &unit) const
Chris@444 712 {
Chris@1469 713 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 714 if (!model) return false;
Chris@444 715
Chris@444 716 min = 0;
Chris@1469 717 max = double(model->getHeight());
Chris@444 718
Chris@1238 719 logarithmic = (m_binScale == BinScale::Log);
Chris@444 720 unit = "";
Chris@444 721
Chris@444 722 return true;
Chris@444 723 }
Chris@444 724
Chris@444 725 bool
Chris@904 726 Colour3DPlotLayer::getDisplayExtents(double &min, double &max) const
Chris@444 727 {
Chris@1469 728 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 729 if (!model) return false;
Chris@444 730
Chris@1469 731 double hmax = double(model->getHeight());
Chris@902 732
Chris@904 733 min = m_miny;
Chris@904 734 max = m_maxy;
Chris@444 735 if (max <= min) {
Chris@444 736 min = 0;
Chris@902 737 max = hmax;
Chris@444 738 }
Chris@444 739 if (min < 0) min = 0;
Chris@902 740 if (max > hmax) max = hmax;
Chris@444 741
Chris@444 742 return true;
Chris@444 743 }
Chris@444 744
Chris@444 745 bool
Chris@904 746 Colour3DPlotLayer::setDisplayExtents(double min, double max)
Chris@444 747 {
Chris@904 748 m_miny = int(lrint(min));
Chris@904 749 m_maxy = int(lrint(max));
Chris@444 750
Chris@1133 751 invalidateRenderers();
Chris@1133 752
Chris@444 753 emit layerParametersChanged();
Chris@444 754 return true;
Chris@444 755 }
Chris@444 756
Chris@725 757 bool
Chris@916 758 Colour3DPlotLayer::getYScaleValue(const LayerGeometryProvider *, int,
Chris@904 759 double &, QString &) const
Chris@725 760 {
Chris@725 761 return false;//!!!
Chris@725 762 }
Chris@725 763
Chris@444 764 int
Chris@444 765 Colour3DPlotLayer::getVerticalZoomSteps(int &defaultStep) const
Chris@444 766 {
Chris@1469 767 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 768 if (!model) return 0;
Chris@444 769
Chris@444 770 defaultStep = 0;
Chris@1469 771 int h = model->getHeight();
Chris@444 772 return h;
Chris@444 773 }
Chris@444 774
Chris@444 775 int
Chris@444 776 Colour3DPlotLayer::getCurrentVerticalZoomStep() const
Chris@444 777 {
Chris@1469 778 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 779 if (!model) return 0;
Chris@444 780
Chris@904 781 double min, max;
Chris@444 782 getDisplayExtents(min, max);
Chris@1469 783 return model->getHeight() - int(lrint(max - min));
Chris@444 784 }
Chris@444 785
Chris@444 786 void
Chris@444 787 Colour3DPlotLayer::setVerticalZoomStep(int step)
Chris@444 788 {
Chris@1469 789 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 790 if (!model) return;
Chris@444 791
Chris@587 792 // SVDEBUG << "Colour3DPlotLayer::setVerticalZoomStep(" <<step <<"): before: miny = " << m_miny << ", maxy = " << m_maxy << endl;
Chris@444 793
Chris@1469 794 int dist = model->getHeight() - step;
Chris@444 795 if (dist < 1) dist = 1;
Chris@904 796 double centre = m_miny + (m_maxy - m_miny) / 2.0;
Chris@904 797 m_miny = int(lrint(centre - dist/2.0));
Chris@444 798 if (m_miny < 0) m_miny = 0;
Chris@444 799 m_maxy = m_miny + dist;
Chris@1469 800 if (m_maxy > model->getHeight()) m_maxy = model->getHeight();
Chris@444 801
Chris@1133 802 invalidateRenderers();
Chris@1133 803
Chris@587 804 // SVDEBUG << "Colour3DPlotLayer::setVerticalZoomStep(" <<step <<"): after: miny = " << m_miny << ", maxy = " << m_maxy << endl;
Chris@444 805
Chris@444 806 emit layerParametersChanged();
Chris@444 807 }
Chris@444 808
Chris@444 809 RangeMapper *
Chris@444 810 Colour3DPlotLayer::getNewVerticalZoomRangeMapper() const
Chris@444 811 {
Chris@1469 812 //!!! most of our uses of model in these functions is just to
Chris@1469 813 //!!! retrieve the model's height - perhaps we should cache it
Chris@1469 814
Chris@1469 815 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 816 if (!model) return nullptr;
Chris@444 817
Chris@1469 818 return new LinearRangeMapper(0, model->getHeight(),
Chris@1469 819 0, model->getHeight(), "");
Chris@444 820 }
Chris@444 821
Chris@904 822 double
Chris@1113 823 Colour3DPlotLayer::getYForBin(const LayerGeometryProvider *v, double bin) const
Chris@532 824 {
Chris@904 825 double y = bin;
Chris@1469 826 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 827 if (!model) return y;
Chris@1469 828 double mn = 0, mx = model->getHeight();
Chris@532 829 getDisplayExtents(mn, mx);
Chris@916 830 double h = v->getPaintHeight();
Chris@1103 831 if (m_binScale == BinScale::Linear) {
Chris@532 832 y = h - (((bin - mn) * h) / (mx - mn));
Chris@532 833 } else {
Chris@904 834 double logmin = mn + 1, logmax = mx + 1;
Chris@532 835 LogRange::mapRange(logmin, logmax);
Chris@532 836 y = h - (((LogRange::map(bin + 1) - logmin) * h) / (logmax - logmin));
Chris@532 837 }
Chris@532 838 return y;
Chris@532 839 }
Chris@532 840
Chris@904 841 double
Chris@1113 842 Colour3DPlotLayer::getBinForY(const LayerGeometryProvider *v, double y) const
Chris@532 843 {
Chris@904 844 double bin = y;
Chris@1469 845 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 846 if (!model) return bin;
Chris@1469 847 double mn = 0, mx = model->getHeight();
Chris@532 848 getDisplayExtents(mn, mx);
Chris@916 849 double h = v->getPaintHeight();
Chris@1103 850 if (m_binScale == BinScale::Linear) {
Chris@1220 851 // Arrange that the first bin (mn) appears as the exact result
Chris@1220 852 // for the first pixel (which is pixel h-1) and the first
Chris@1220 853 // out-of-range bin (mx) would appear as the exact result for
Chris@1220 854 // the first out-of-range pixel (which would be pixel -1)
Chris@1220 855 bin = mn + ((h - y - 1) * (mx - mn)) / h;
Chris@532 856 } else {
Chris@904 857 double logmin = mn + 1, logmax = mx + 1;
Chris@532 858 LogRange::mapRange(logmin, logmax);
Chris@1220 859 bin = LogRange::unmap(logmin + ((h - y - 1) * (logmax - logmin)) / h) - 1;
Chris@532 860 }
Chris@532 861 return bin;
Chris@532 862 }
Chris@532 863
Chris@25 864 QString
Chris@916 865 Colour3DPlotLayer::getFeatureDescription(LayerGeometryProvider *v, QPoint &pos) const
Chris@25 866 {
Chris@1469 867 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 868 if (!model) return "";
Chris@25 869
Chris@25 870 int x = pos.x();
Chris@25 871 int y = pos.y();
Chris@25 872
Chris@1469 873 sv_frame_t modelStart = model->getStartFrame();
Chris@1469 874 int modelResolution = model->getResolution();
Chris@25 875
Chris@902 876 double srRatio =
Chris@902 877 v->getViewManager()->getMainModelSampleRate() /
Chris@1469 878 model->getSampleRate();
Chris@159 879
Chris@902 880 int sx0 = int((double(v->getFrameForX(x)) / srRatio - double(modelStart)) /
Chris@812 881 modelResolution);
Chris@25 882
Chris@160 883 int f0 = sx0 * modelResolution;
Chris@160 884 int f1 = f0 + modelResolution;
Chris@160 885
Chris@1469 886 int sh = model->getHeight();
Chris@447 887
Chris@447 888 int symin = m_miny;
Chris@447 889 int symax = m_maxy;
Chris@447 890 if (symax <= symin) {
Chris@447 891 symin = 0;
Chris@447 892 symax = sh;
Chris@447 893 }
Chris@447 894 if (symin < 0) symin = 0;
Chris@447 895 if (symax > sh) symax = sh;
Chris@447 896
Chris@916 897 // double binHeight = double(v->getPaintHeight()) / (symax - symin);
Chris@916 898 // int sy = int((v->getPaintHeight() - y) / binHeight) + symin;
Chris@534 899
Chris@903 900 int sy = getIBinForY(v, y);
Chris@25 901
Chris@1469 902 if (sy < 0 || sy >= model->getHeight()) {
Chris@812 903 return "";
Chris@812 904 }
Chris@812 905
Chris@1362 906 if (m_invertVertical) {
Chris@1469 907 sy = model->getHeight() - sy - 1;
Chris@1362 908 }
Chris@357 909
Chris@1469 910 float value = model->getValueAt(sx0, sy);
Chris@159 911
Chris@682 912 // cerr << "bin value (" << sx0 << "," << sy << ") is " << value << endl;
Chris@25 913
Chris@1469 914 QString binName = model->getBinName(sy);
Chris@25 915 if (binName == "") binName = QString("[%1]").arg(sy + 1);
Chris@25 916 else binName = QString("%1 [%2]").arg(binName).arg(sy + 1);
Chris@25 917
Chris@25 918 QString text = tr("Time:\t%1 - %2\nBin:\t%3\nValue:\t%4")
Chris@1469 919 .arg(RealTime::frame2RealTime(f0, model->getSampleRate())
Chris@1266 920 .toText(true).c_str())
Chris@1469 921 .arg(RealTime::frame2RealTime(f1, model->getSampleRate())
Chris@1266 922 .toText(true).c_str())
Chris@1266 923 .arg(binName)
Chris@1266 924 .arg(value);
Chris@25 925
Chris@25 926 return text;
Chris@25 927 }
Chris@25 928
Chris@25 929 int
Chris@969 930 Colour3DPlotLayer::getColourScaleWidth(QPainter &p) const
Chris@159 931 {
Chris@969 932 // Font is rotated
Chris@969 933 int cw = p.fontMetrics().height();
Chris@159 934 return cw;
Chris@159 935 }
Chris@159 936
Chris@159 937 int
Chris@916 938 Colour3DPlotLayer::getVerticalScaleWidth(LayerGeometryProvider *, bool, QPainter &paint) const
Chris@25 939 {
Chris@1469 940 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 941 if (!model) return 0;
Chris@25 942
Chris@1471 943 // Qt 5.13 deprecates QFontMetrics::width(), but its suggested
Chris@1471 944 // replacement (horizontalAdvance) was only added in Qt 5.11 which
Chris@1471 945 // is too new for us
Chris@1471 946 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
Chris@1471 947
Chris@1469 948 QString sampleText = QString("[%1]").arg(model->getHeight());
Chris@25 949 int tw = paint.fontMetrics().width(sampleText);
Chris@98 950 bool another = false;
Chris@25 951
Chris@1469 952 for (int i = 0; i < model->getHeight(); ++i) {
Chris@1469 953 if (model->getBinName(i).length() > sampleText.length()) {
Chris@1469 954 sampleText = model->getBinName(i);
Chris@98 955 another = true;
Chris@1266 956 }
Chris@25 957 }
Chris@98 958 if (another) {
Chris@1266 959 tw = std::max(tw, paint.fontMetrics().width(sampleText));
Chris@25 960 }
Chris@25 961
Chris@159 962 return tw + 13 + getColourScaleWidth(paint);
Chris@25 963 }
Chris@25 964
Chris@25 965 void
Chris@916 966 Colour3DPlotLayer::paintVerticalScale(LayerGeometryProvider *v, bool, QPainter &paint, QRect rect) const
Chris@25 967 {
Chris@1469 968 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 969 if (!model) return;
Chris@25 970
Chris@25 971 int h = rect.height(), w = rect.width();
Chris@25 972
Chris@159 973 int cw = getColourScaleWidth(paint);
Chris@159 974
Chris@159 975 int ch = h - 20;
Chris@1107 976 if (ch > 20) {
Chris@159 977
Chris@1131 978 double min = m_viewMags[v->getId()].getMin();
Chris@1131 979 double max = m_viewMags[v->getId()].getMax();
Chris@447 980
Chris@1131 981 if (max <= min) max = min + 0.1;
Chris@447 982
Chris@287 983 paint.setPen(v->getForeground());
Chris@447 984 paint.drawRect(4, 10, cw - 8, ch+1);
Chris@159 985
Chris@446 986 for (int y = 0; y < ch; ++y) {
Chris@904 987 double value = ((max - min) * (double(ch-y) - 1.0)) / double(ch) + min;
Chris@1131 988 paint.setPen(getRenderer(v)->getColour(value));
Chris@1131 989 paint.drawLine(5, 11 + y, cw - 5, 11 + y);
Chris@159 990 }
Chris@446 991
Chris@446 992 QString minstr = QString("%1").arg(min);
Chris@446 993 QString maxstr = QString("%1").arg(max);
Chris@446 994
Chris@446 995 paint.save();
Chris@446 996
Chris@446 997 QFont font = paint.font();
Chris@1053 998 if (font.pixelSize() > 0) {
Chris@1053 999 int newSize = int(font.pixelSize() * 0.65);
Chris@1053 1000 if (newSize < 6) newSize = 6;
Chris@1053 1001 font.setPixelSize(newSize);
Chris@1053 1002 paint.setFont(font);
Chris@1053 1003 }
Chris@446 1004
Chris@446 1005 int msw = paint.fontMetrics().width(maxstr);
Chris@446 1006
Chris@1471 1007 QTransform m;
Chris@446 1008 m.translate(cw - 6, ch + 10);
Chris@446 1009 m.rotate(-90);
Chris@446 1010
Chris@1471 1011 paint.setWorldTransform(m);
Chris@446 1012
Chris@1471 1013 PaintAssistant::drawVisibleText(v, paint, 2, 0, minstr,
Chris@1471 1014 PaintAssistant::OutlinedText);
Chris@446 1015
Chris@446 1016 m.translate(ch - msw - 2, 0);
Chris@1471 1017 paint.setWorldTransform(m);
Chris@446 1018
Chris@1471 1019 PaintAssistant::drawVisibleText(v, paint, 0, 0, maxstr,
Chris@1471 1020 PaintAssistant::OutlinedText);
Chris@446 1021
Chris@446 1022 paint.restore();
Chris@159 1023 }
Chris@159 1024
Chris@287 1025 paint.setPen(v->getForeground());
Chris@159 1026
Chris@1469 1027 int sh = model->getHeight();
Chris@445 1028
Chris@445 1029 int symin = m_miny;
Chris@445 1030 int symax = m_maxy;
Chris@445 1031 if (symax <= symin) {
Chris@445 1032 symin = 0;
Chris@445 1033 symax = sh;
Chris@445 1034 }
Chris@445 1035 if (symin < 0) symin = 0;
Chris@445 1036 if (symax > sh) symax = sh;
Chris@445 1037
Chris@532 1038 paint.save();
Chris@456 1039
Chris@533 1040 int py = h;
Chris@25 1041
Chris@969 1042 int defaultFontHeight = paint.fontMetrics().height();
Chris@969 1043
Chris@805 1044 for (int i = symin; i <= symax; ++i) {
Chris@98 1045
Chris@532 1046 int y0;
Chris@534 1047
Chris@903 1048 y0 = getIYForBin(v, i);
Chris@532 1049 int h = py - y0;
Chris@532 1050
Chris@532 1051 if (i > symin) {
Chris@532 1052 if (paint.fontMetrics().height() >= h) {
Chris@969 1053 if (h >= defaultFontHeight * 0.8) {
Chris@532 1054 QFont tf = paint.font();
Chris@973 1055 tf.setPixelSize(int(h * 0.8));
Chris@532 1056 paint.setFont(tf);
Chris@532 1057 } else {
Chris@532 1058 continue;
Chris@532 1059 }
Chris@532 1060 }
Chris@532 1061 }
Chris@1266 1062
Chris@532 1063 py = y0;
Chris@532 1064
Chris@534 1065 if (i < symax) {
Chris@534 1066 paint.drawLine(cw, y0, w, y0);
Chris@534 1067 }
Chris@25 1068
Chris@532 1069 if (i > symin) {
Chris@534 1070
Chris@805 1071 int idx = i - 1;
Chris@1362 1072 if (m_invertVertical) {
Chris@1469 1073 idx = model->getHeight() - idx - 1;
Chris@1362 1074 }
Chris@534 1075
Chris@1469 1076 QString text = model->getBinName(idx);
Chris@534 1077 if (text == "") text = QString("[%1]").arg(idx + 1);
Chris@534 1078
Chris@534 1079 int ty = y0 + (h/2) - (paint.fontMetrics().height()/2) +
Chris@534 1080 paint.fontMetrics().ascent() + 1;
Chris@534 1081
Chris@534 1082 paint.drawText(cw + 5, ty, text);
Chris@457 1083 }
Chris@25 1084 }
Chris@456 1085
Chris@456 1086 paint.restore();
Chris@25 1087 }
Chris@25 1088
Chris@1100 1089 Colour3DPlotRenderer *
Chris@1113 1090 Colour3DPlotLayer::getRenderer(const LayerGeometryProvider *v) const
Chris@1100 1091 {
Chris@1469 1092 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 1093 if (!model) return nullptr;
Chris@1469 1094
Chris@1235 1095 int viewId = v->getId();
Chris@1235 1096
Chris@1235 1097 if (m_renderers.find(viewId) == m_renderers.end()) {
Chris@1100 1098
Chris@1100 1099 Colour3DPlotRenderer::Sources sources;
Chris@1100 1100 sources.verticalBinLayer = this;
Chris@1100 1101 sources.source = m_model;
Chris@1212 1102 sources.peakCaches.push_back(getPeakCache());
Chris@1100 1103
Chris@1100 1104 ColourScale::Parameters cparams;
Chris@1100 1105 cparams.colourMap = m_colourMap;
Chris@1362 1106 cparams.inverted = m_colourInverted;
Chris@1137 1107 cparams.scaleType = m_colourScale;
Chris@1100 1108 cparams.gain = m_gain;
Chris@1100 1109
Chris@1235 1110 double minValue = 0.0;
Chris@1235 1111 double maxValue = 1.0;
Chris@1235 1112
Chris@1235 1113 if (m_normalizeVisibleArea && m_viewMags[viewId].isSet()) {
Chris@1235 1114 minValue = m_viewMags[viewId].getMin();
Chris@1235 1115 maxValue = m_viewMags[viewId].getMax();
Chris@1245 1116 } else if (m_normalization == ColumnNormalization::Hybrid) {
Chris@1245 1117 minValue = 0;
Chris@1469 1118 maxValue = log10(model->getMaximumLevel() + 1.0);
Chris@1235 1119 } else if (m_normalization == ColumnNormalization::None) {
Chris@1469 1120 minValue = model->getMinimumLevel();
Chris@1469 1121 maxValue = model->getMaximumLevel();
Chris@1131 1122 }
Chris@1131 1123
Chris@1244 1124 SVDEBUG << "Colour3DPlotLayer: rebuilding renderer, value range is "
Chris@1362 1125 << minValue << " -> " << maxValue
Chris@1469 1126 << " (model min = " << model->getMinimumLevel()
Chris@1469 1127 << ", max = " << model->getMaximumLevel() << ")"
Chris@1362 1128 << endl;
Chris@1378 1129
Chris@1235 1130 if (maxValue <= minValue) {
Chris@1235 1131 maxValue = minValue + 0.1f;
Chris@1378 1132
Chris@1378 1133 if (!(maxValue > minValue)) { // one of them must be NaN or Inf
Chris@1378 1134 SVCERR << "WARNING: Colour3DPlotLayer::getRenderer: resetting "
Chris@1378 1135 << "minValue and maxValue to zero and one" << endl;
Chris@1378 1136 minValue = 0.f;
Chris@1378 1137 maxValue = 1.f;
Chris@1378 1138 }
Chris@1131 1139 }
Chris@1235 1140
Chris@1247 1141 cparams.threshold = minValue;
Chris@1235 1142 cparams.minValue = minValue;
Chris@1235 1143 cparams.maxValue = maxValue;
Chris@1131 1144
Chris@1235 1145 m_lastRenderedMags[viewId] = MagnitudeRange(float(minValue),
Chris@1235 1146 float(maxValue));
Chris@1235 1147
Chris@1100 1148 Colour3DPlotRenderer::Parameters params;
Chris@1100 1149 params.colourScale = ColourScale(cparams);
Chris@1100 1150 params.normalization = m_normalization;
Chris@1100 1151 params.binScale = m_binScale;
Chris@1100 1152 params.alwaysOpaque = m_opaque;
Chris@1100 1153 params.invertVertical = m_invertVertical;
Chris@1100 1154 params.interpolate = m_smooth;
Chris@1100 1155
Chris@1235 1156 m_renderers[viewId] = new Colour3DPlotRenderer(sources, params);
Chris@1100 1157 }
Chris@1100 1158
Chris@1235 1159 return m_renderers[viewId];
Chris@1100 1160 }
Chris@1100 1161
Chris@197 1162 void
Chris@1107 1163 Colour3DPlotLayer::paintWithRenderer(LayerGeometryProvider *v,
Chris@1107 1164 QPainter &paint, QRect rect) const
Chris@1101 1165 {
Chris@1101 1166 Colour3DPlotRenderer *renderer = getRenderer(v);
Chris@1101 1167
Chris@1121 1168 Colour3DPlotRenderer::RenderResult result;
Chris@1123 1169 MagnitudeRange magRange;
Chris@1123 1170 int viewId = v->getId();
Chris@1123 1171
Chris@1235 1172 bool continuingPaint = !renderer->geometryChanged(v);
Chris@1235 1173
Chris@1235 1174 if (continuingPaint) {
Chris@1123 1175 magRange = m_viewMags[viewId];
Chris@1123 1176 }
Chris@1121 1177
Chris@1101 1178 if (m_synchronous) {
Chris@1121 1179
Chris@1121 1180 result = renderer->render(v, paint, rect);
Chris@1121 1181
Chris@1121 1182 } else {
Chris@1121 1183
Chris@1121 1184 result = renderer->renderTimeConstrained(v, paint, rect);
Chris@1121 1185
Chris@1121 1186 QRect uncached = renderer->getLargestUncachedRect(v);
Chris@1121 1187 if (uncached.width() > 0) {
Chris@1121 1188 v->updatePaintRect(uncached);
Chris@1121 1189 }
Chris@1101 1190 }
Chris@1121 1191
Chris@1123 1192 magRange.sample(result.range);
Chris@1101 1193
Chris@1123 1194 if (magRange.isSet()) {
Chris@1235 1195 if (m_viewMags[viewId] != magRange) {
Chris@1123 1196 m_viewMags[viewId] = magRange;
Chris@1235 1197 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@1412 1198 SVDEBUG << "mag range in this view has changed: "
Chris@1412 1199 << magRange.getMin() << " -> "
Chris@1412 1200 << magRange.getMax() << endl;
Chris@1235 1201 #endif
Chris@1123 1202 }
Chris@1123 1203 }
Chris@1101 1204
Chris@1235 1205 if (!continuingPaint && m_normalizeVisibleArea &&
Chris@1235 1206 m_viewMags[viewId] != m_lastRenderedMags[viewId]) {
Chris@1235 1207 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@1412 1208 SVDEBUG << "mag range has changed from last rendered range: re-rendering"
Chris@1235 1209 << endl;
Chris@1235 1210 #endif
Chris@1235 1211 delete m_renderers[viewId];
Chris@1235 1212 m_renderers.erase(viewId);
Chris@1235 1213 v->updatePaintRect(v->getPaintRect());
Chris@1235 1214 }
Chris@1101 1215 }
Chris@1101 1216
Chris@1101 1217 void
Chris@916 1218 Colour3DPlotLayer::paint(LayerGeometryProvider *v, QPainter &paint, QRect rect) const
Chris@0 1219 {
Chris@1469 1220 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@514 1221 /*
Chris@1469 1222 if (model) {
Chris@1469 1223 SVDEBUG << "Colour3DPlotLayer::paint: model says shouldUseLogValueScale = " << model->shouldUseLogValueScale() << endl;
Chris@443 1224 }
Chris@514 1225 */
Chris@466 1226 Profiler profiler("Colour3DPlotLayer::paint");
Chris@125 1227 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@1412 1228 SVDEBUG << "Colour3DPlotLayer::paint(): m_model is " << m_model << ", zoom level is " << v->getZoomLevel() << ", rect is (" << rect.x() << "," << rect.y() << ") " << rect.width() << "x" << rect.height() << endl;
Chris@125 1229 #endif
Chris@0 1230
Chris@0 1231 int completion = 0;
Chris@1469 1232 if (!model || !model->isOK() || !model->isReady(&completion)) {
Chris@1266 1233 if (completion > 0) {
Chris@1266 1234 paint.fillRect(0, 10, v->getPaintWidth() * completion / 100,
Chris@1266 1235 10, QColor(120, 120, 120));
Chris@1266 1236 }
Chris@1266 1237 return;
Chris@0 1238 }
Chris@0 1239
Chris@1469 1240 if (model->getWidth() == 0) {
Chris@1053 1241 #ifdef DEBUG_COLOUR_3D_PLOT_LAYER_PAINT
Chris@1412 1242 SVDEBUG << "Colour3DPlotLayer::paint(): model width == 0, "
Chris@1053 1243 << "nothing to paint (yet)" << endl;
Chris@1053 1244 #endif
Chris@1053 1245 return;
Chris@1053 1246 }
Chris@1101 1247
Chris@1107 1248 paintWithRenderer(v, paint, rect);
Chris@1107 1249 }
Chris@1107 1250
Chris@28 1251 bool
Chris@1547 1252 Colour3DPlotLayer::snapToFeatureFrame(LayerGeometryProvider *v,
Chris@1547 1253 sv_frame_t &frame,
Chris@1266 1254 int &resolution,
Chris@1547 1255 SnapType snap,
Chris@1547 1256 int ycoord) const
Chris@24 1257 {
Chris@1469 1258 auto model = ModelById::getAs<DenseThreeDimensionalModel>(m_model);
Chris@1469 1259 if (!model) {
Chris@1547 1260 return Layer::snapToFeatureFrame(v, frame, resolution, snap, ycoord);
Chris@24 1261 }
Chris@24 1262
Chris@1469 1263 resolution = model->getResolution();
Chris@904 1264 sv_frame_t left = (frame / resolution) * resolution;
Chris@904 1265 sv_frame_t right = left + resolution;
Chris@28 1266
Chris@28 1267 switch (snap) {
Chris@28 1268 case SnapLeft: frame = left; break;
Chris@28 1269 case SnapRight: frame = right; break;
Chris@28 1270 case SnapNeighbouring:
Chris@1266 1271 if (frame - left > right - frame) frame = right;
Chris@1266 1272 else frame = left;
Chris@1266 1273 break;
Chris@28 1274 }
Chris@24 1275
Chris@28 1276 return true;
Chris@24 1277 }
Chris@24 1278
Chris@316 1279 void
Chris@316 1280 Colour3DPlotLayer::toXml(QTextStream &stream,
Chris@316 1281 QString indent, QString extraAttributes) const
Chris@197 1282 {
Chris@316 1283 QString s = QString("scale=\"%1\" "
Chris@1362 1284 "minY=\"%2\" "
Chris@1362 1285 "maxY=\"%3\" "
Chris@1362 1286 "invertVertical=\"%4\" "
Chris@1362 1287 "opaque=\"%5\" %6")
Chris@1266 1288 .arg(convertFromColourScale(m_colourScale))
Chris@445 1289 .arg(m_miny)
Chris@465 1290 .arg(m_maxy)
Chris@465 1291 .arg(m_invertVertical ? "true" : "false")
Chris@534 1292 .arg(m_opaque ? "true" : "false")
Chris@536 1293 .arg(QString("binScale=\"%1\" smooth=\"%2\" gain=\"%3\" ")
Chris@1103 1294 .arg(int(m_binScale))
Chris@536 1295 .arg(m_smooth ? "true" : "false")
Chris@536 1296 .arg(m_gain));
Chris@1362 1297
Chris@1362 1298 // New-style colour map attribute, by string id rather than by
Chris@1362 1299 // number
Chris@1362 1300
Chris@1362 1301 s += QString("colourMap=\"%1\" ")
Chris@1362 1302 .arg(ColourMapper::getColourMapId(m_colourMap));
Chris@1362 1303
Chris@1362 1304 // Old-style colour map attribute
Chris@1362 1305
Chris@1362 1306 s += QString("colourScheme=\"%1\" ")
Chris@1362 1307 .arg(ColourMapper::getBackwardCompatibilityColourMap(m_colourMap));
Chris@535 1308
Chris@1099 1309 // New-style normalization attributes, allowing for more types of
Chris@1099 1310 // normalization in future: write out the column normalization
Chris@1099 1311 // type separately, and then whether we are normalizing visible
Chris@1099 1312 // area as well afterwards
Chris@1099 1313
Chris@1099 1314 s += QString("columnNormalization=\"%1\" ")
Chris@1245 1315 .arg(m_normalization == ColumnNormalization::Range01 ? "peak" :
Chris@1104 1316 m_normalization == ColumnNormalization::Hybrid ? "hybrid" : "none");
Chris@1099 1317
Chris@1099 1318 // Old-style normalization attribute, for backward compatibility
Chris@1099 1319
Chris@1099 1320 s += QString("normalizeColumns=\"%1\" ")
Chris@1266 1321 .arg(m_normalization == ColumnNormalization::Range01 ? "true" : "false");
Chris@1099 1322
Chris@1099 1323 // And this applies to both old- and new-style attributes
Chris@1099 1324
Chris@1099 1325 s += QString("normalizeVisibleArea=\"%1\" ")
Chris@1104 1326 .arg(m_normalizeVisibleArea ? "true" : "false");
Chris@1099 1327
Chris@316 1328 Layer::toXml(stream, indent, extraAttributes + " " + s);
Chris@197 1329 }
Chris@197 1330
Chris@197 1331 void
Chris@197 1332 Colour3DPlotLayer::setProperties(const QXmlAttributes &attributes)
Chris@197 1333 {
Chris@445 1334 bool ok = false, alsoOk = false;
Chris@197 1335
Chris@1105 1336 ColourScaleType colourScale = convertToColourScale
Chris@1252 1337 (attributes.value("scale").toInt(&ok));
Chris@1099 1338 if (ok) setColourScale(colourScale);
Chris@197 1339
Chris@1362 1340 QString colourMapId = attributes.value("colourMap");
Chris@1362 1341 int colourMap = ColourMapper::getColourMapById(colourMapId);
Chris@1362 1342 if (colourMap >= 0) {
Chris@1362 1343 setColourMap(colourMap);
Chris@1362 1344 } else {
Chris@1362 1345 colourMap = attributes.value("colourScheme").toInt(&ok);
Chris@1362 1346 if (ok && colourMap < ColourMapper::getColourMapCount()) {
Chris@1362 1347 setColourMap(colourMap);
Chris@1362 1348 }
Chris@1362 1349 }
Chris@197 1350
Chris@1103 1351 BinScale binScale = (BinScale)
Chris@1266 1352 attributes.value("binScale").toInt(&ok);
Chris@1099 1353 if (ok) setBinScale(binScale);
Chris@445 1354
Chris@465 1355 bool invertVertical =
Chris@465 1356 (attributes.value("invertVertical").trimmed() == "true");
Chris@465 1357 setInvertVertical(invertVertical);
Chris@465 1358
Chris@465 1359 bool opaque =
Chris@465 1360 (attributes.value("opaque").trimmed() == "true");
Chris@535 1361 setOpaque(opaque);
Chris@535 1362
Chris@535 1363 bool smooth =
Chris@535 1364 (attributes.value("smooth").trimmed() == "true");
Chris@535 1365 setSmooth(smooth);
Chris@465 1366
Chris@536 1367 float gain = attributes.value("gain").toFloat(&ok);
Chris@536 1368 if (ok) setGain(gain);
Chris@536 1369
Chris@445 1370 float min = attributes.value("minY").toFloat(&ok);
Chris@445 1371 float max = attributes.value("maxY").toFloat(&alsoOk);
Chris@445 1372 if (ok && alsoOk) setDisplayExtents(min, max);
Chris@1099 1373
Chris@1099 1374 bool haveNewStyleNormalization = false;
Chris@1099 1375
Chris@1099 1376 QString columnNormalization = attributes.value("columnNormalization");
Chris@1099 1377
Chris@1099 1378 if (columnNormalization != "") {
Chris@1099 1379
Chris@1099 1380 haveNewStyleNormalization = true;
Chris@1099 1381
Chris@1099 1382 if (columnNormalization == "peak") {
Chris@1245 1383 setNormalization(ColumnNormalization::Range01);
Chris@1099 1384 } else if (columnNormalization == "hybrid") {
Chris@1104 1385 setNormalization(ColumnNormalization::Hybrid);
Chris@1099 1386 } else if (columnNormalization == "none") {
Chris@1104 1387 setNormalization(ColumnNormalization::None);
Chris@1099 1388 } else {
Chris@1412 1389 SVCERR << "NOTE: Unknown or unsupported columnNormalization attribute \""
Chris@1412 1390 << columnNormalization << "\"" << endl;
Chris@1099 1391 }
Chris@1099 1392 }
Chris@1099 1393
Chris@1099 1394 if (!haveNewStyleNormalization) {
Chris@1099 1395
Chris@1104 1396 setNormalization(ColumnNormalization::None);
Chris@1104 1397
Chris@1099 1398 bool normalizeColumns =
Chris@1099 1399 (attributes.value("normalizeColumns").trimmed() == "true");
Chris@1099 1400 if (normalizeColumns) {
Chris@1245 1401 setNormalization(ColumnNormalization::Range01);
Chris@1099 1402 }
Chris@1099 1403
Chris@1099 1404 bool normalizeHybrid =
Chris@1099 1405 (attributes.value("normalizeHybrid").trimmed() == "true");
Chris@1099 1406 if (normalizeHybrid) {
Chris@1104 1407 setNormalization(ColumnNormalization::Hybrid);
Chris@1099 1408 }
Chris@1099 1409 }
Chris@1099 1410
Chris@1099 1411 bool normalizeVisibleArea =
Chris@1099 1412 (attributes.value("normalizeVisibleArea").trimmed() == "true");
Chris@1104 1413 setNormalizeVisibleArea(normalizeVisibleArea);
Chris@1099 1414
Chris@1099 1415 //!!! todo: check save/reload scaling, compare with
Chris@1099 1416 //!!! SpectrogramLayer, compare with prior SV versions, compare
Chris@1099 1417 //!!! with Tony v1 and v2 and their save files
Chris@197 1418 }
Chris@197 1419