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