annotate layer/Colour3DPlotLayer.cpp @ 1554:a0b2f3b4dd2f spectrogram-export

Start work on spectrogram export code
author Chris Cannam
date Mon, 06 Jan 2020 14:46:25 +0000
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