annotate layer/Colour3DPlotLayer.cpp @ 1567:77ffd5421627

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