annotate layer/Colour3DPlotLayer.cpp @ 1593:32171776fcc9 sensible-delimited-data-strings

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