annotate layer/Colour3DPlotLayer.cpp @ 1548:bd6af89982d7

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