BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
ColorMap.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/View/PlotUtil/ColorMap.cpp
6 //! @brief Implements class ColorMap
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
22 
23 namespace {
24 
25 const int replot_update_interval = 10;
26 const int colorbar_width_logz = 50;
27 const int colorbar_width = 80;
28 
29 } // namespace
30 
31 ColorMap::ColorMap(QWidget* parent)
32  : ScientificPlot(parent, PLOT_TYPE::Plot2D)
33  , m_customPlot(new QCustomPlot)
34  , m_colorMap(nullptr)
35  , m_colorScale(nullptr)
36  , m_updateTimer(new UpdateTimer(replot_update_interval, this))
37  , m_colorBarLayout(new QCPLayoutGrid)
38  , m_block_update(true)
39 {
40  initColorMap();
41 
42  auto* vlayout = new QVBoxLayout(this);
43  vlayout->setMargin(0);
44  vlayout->setSpacing(0);
45  vlayout->addWidget(m_customPlot);
46  m_customPlot->setAttribute(Qt::WA_NoMousePropagation, false);
47  setLayout(vlayout);
48 
50  // setFixedColorMapMargins();
51 }
52 
54 {
55  QCPRange xrange = m_customPlot->xAxis->range();
56  QCPRange yrange = m_customPlot->yAxis->range();
57  double left = xrange.lower;
58  double right = xrange.upper;
59  double top = yrange.upper;
60  double bottom = yrange.lower;
61 
62  return QRectF(xAxisCoordToPixel(left), yAxisCoordToPixel(top),
63  xAxisCoordToPixel(right) - xAxisCoordToPixel(left),
64  yAxisCoordToPixel(bottom) - yAxisCoordToPixel(top));
65 }
66 
67 PlotEventInfo ColorMap::eventInfo(double xpos, double ypos) const
68 {
69  PlotEventInfo result(plotType());
70  if (!intensityItem())
71  return result;
72 
73  int nx(0), ny(0);
74  m_colorMap->data()->coordToCell(xpos, ypos, &nx, &ny);
75 
76  result.setX(xpos);
77  result.setY(ypos);
78  result.setNx(nx);
79  result.setNy(ny);
80 
81  result.setInAxesRange(axesRangeContains(xpos, ypos));
82  result.setValue(m_colorMap->data()->cell(result.nx(), result.ny()));
83  result.setLogValueAxis(intensityItem()->isLogz());
84 
85  return result;
86 }
87 
88 //! sets logarithmic scale
89 void ColorMap::setLogz(bool logz)
90 {
91  m_colorBarLayout->setMinimumSize(logz ? colorbar_width_logz : colorbar_width, 10);
93 }
94 
95 //! reset all axes min,max to initial value
97 {
99 }
100 
102 {
105  replot();
106 }
107 
108 //! updates color map depending on IntensityDataItem properties
109 void ColorMap::onPropertyChanged(const QString& property_name)
110 {
111  if (m_block_update)
112  return;
113 
114  if (IntensityDataItem::isGradientPropertyName(property_name)) {
116  replot();
117  } else if (IntensityDataItem::isInterpolatedPropertyName(property_name)) {
118  m_colorMap->setInterpolate(intensityItem()->isInterpolated());
119  replot();
120  } else if (DataItem::isAxesUnitsPropertyName(property_name)) {
122  replot();
123  }
124 }
125 
126 void ColorMap::onAxisPropertyChanged(const QString& axisName, const QString& propertyName)
127 {
128  if (m_block_update)
129  return;
130 
131  if (BasicAxisItem::isTitlePropertyName(propertyName)
134  replot();
135  }
136 
138  if (BasicAxisItem::isBoundsPropertiesName(propertyName)) {
139  setAxesRangeConnected(false);
141  setAxesRangeConnected(true);
142  replot();
143  }
144  } else if (IntensityDataItem::isYaxisPropertyName(axisName)) {
145  if (BasicAxisItem::isBoundsPropertiesName(propertyName)) {
146  setAxesRangeConnected(false);
148  setAxesRangeConnected(true);
149  replot();
150  }
151  }
152 
153  else if (IntensityDataItem::isZaxisPropertyName(axisName)) {
154  if (BasicAxisItem::isBoundsPropertiesName(propertyName)) {
156  replot();
157  } else if (AmplitudeAxisItem::isLogScalePropertyName(propertyName)) {
158  setLogz(intensityItem()->isLogz());
159  replot();
160  } else if (BasicAxisItem::isVisibilityPropertyName(propertyName)) {
161  setColorScaleVisible(intensityItem()->zAxisItem()->visibilityValue());
162  replot();
163  }
164  }
165 }
166 
167 //! Propagate zmin, zmax back to IntensityDataItem
168 void ColorMap::onDataRangeChanged(QCPRange newRange)
169 {
170  m_block_update = true;
171  intensityItem()->setLowerAndUpperZ(newRange.lower, newRange.upper);
172  m_block_update = false;
173 }
174 
175 //! Propagate xmin, xmax back to IntensityDataItem
176 void ColorMap::onXaxisRangeChanged(QCPRange newRange)
177 {
178  m_block_update = true;
179  intensityItem()->setLowerX(newRange.lower);
180  intensityItem()->setUpperX(newRange.upper);
181  m_block_update = false;
182 }
183 
184 //! Propagate ymin, ymax back to IntensityDataItem
185 void ColorMap::onYaxisRangeChanged(QCPRange newRange)
186 {
187  m_block_update = true;
188  intensityItem()->setLowerY(newRange.lower);
189  intensityItem()->setUpperY(newRange.upper);
190  m_block_update = false;
191 }
192 
193 //! Schedule replot for later execution by onTimeReplot() slot.
194 
196 {
198 }
199 
200 //! Replots ColorMap.
201 
203 {
204  m_customPlot->replot();
205 }
206 
208 {
210 
212  [this](const QString& name) { onPropertyChanged(name); }, this);
213 
215  [this](SessionItem* item, const QString name) {
216  if (item->modelType() == BasicAxisItem::M_TYPE
217  || item->modelType() == AmplitudeAxisItem::M_TYPE)
219  },
220  this);
221 
222  intensityItem()->mapper()->setOnValueChange([this]() { onIntensityModified(); }, this);
223 
224  setConnected(true);
225 }
226 
228 {
229  setConnected(false);
230 }
231 
232 //! creates and initializes the color map
234 {
235  m_colorMap = new QCPColorMap(m_customPlot->xAxis, m_customPlot->yAxis);
236  m_colorScale = new QCPColorScale(m_customPlot);
237  m_colorMap->setColorScale(m_colorScale);
238 
239  m_colorBarLayout->addElement(0, 0, m_colorScale);
240  m_colorBarLayout->setMinimumSize(colorbar_width_logz, 10);
241  auto base_size = GUI::Util::Style::SizeOfLetterM(this).width() * 0.5;
242  m_colorBarLayout->setMargins(QMargins(base_size, 0, base_size, 0));
243 
244  m_colorScale->axis()->axisRect()->setMargins(QMargins(0, 0, 0, 0));
245  m_colorScale->axis()->axisRect()->setAutoMargins(QCP::msNone);
246 
248  m_colorScale->axis()->setTickLabelFont(
249  QFont(QFont().family(), GUI::Constants::plot_tick_label_size()));
250  m_customPlot->xAxis->setTickLabelFont(
251  QFont(QFont().family(), GUI::Constants::plot_tick_label_size()));
252  m_customPlot->yAxis->setTickLabelFont(
253  QFont(QFont().family(), GUI::Constants::plot_tick_label_size()));
254 
255  connect(m_customPlot, &QCustomPlot::afterReplot, this, &ColorMap::marginsChangedNotify);
256 }
257 
258 void ColorMap::setConnected(bool isConnected)
259 {
260  setAxesRangeConnected(isConnected);
261  setDataRangeConnected(isConnected);
262  setUpdateTimerConnected(isConnected);
263 }
264 
265 //! Connects/disconnects signals related to ColorMap's X,Y axes rectangle change.
266 
267 void ColorMap::setAxesRangeConnected(bool isConnected)
268 {
269  if (isConnected) {
270  connect(m_customPlot->xAxis,
271  static_cast<void (QCPAxis::*)(const QCPRange&)>(&QCPAxis::rangeChanged), this,
272  &ColorMap::onXaxisRangeChanged, Qt::UniqueConnection);
273 
274  connect(m_customPlot->yAxis,
275  static_cast<void (QCPAxis::*)(const QCPRange&)>(&QCPAxis::rangeChanged), this,
276  &ColorMap::onYaxisRangeChanged, Qt::UniqueConnection);
277  } else {
278  disconnect(m_customPlot->xAxis,
279  static_cast<void (QCPAxis::*)(const QCPRange&)>(&QCPAxis::rangeChanged), this,
281 
282  disconnect(m_customPlot->yAxis,
283  static_cast<void (QCPAxis::*)(const QCPRange&)>(&QCPAxis::rangeChanged), this,
285  }
286 }
287 
288 //! Connects/disconnects signals related to ColorMap's Z-axis (min,max) change.
289 
290 void ColorMap::setDataRangeConnected(bool isConnected)
291 {
292  if (isConnected)
293  connect(m_colorMap, &QCPColorMap::dataRangeChanged, this, &ColorMap::onDataRangeChanged,
294  Qt::UniqueConnection);
295  else
296  disconnect(m_colorMap, &QCPColorMap::dataRangeChanged, this, &ColorMap::onDataRangeChanged);
297 }
298 
299 void ColorMap::setUpdateTimerConnected(bool isConnected)
300 {
301  if (isConnected)
303  Qt::UniqueConnection);
304  else
306 }
307 
308 //! to make fixed margins for whole colormap (change in axes labels wont affect axes rectangle)
310 {
312 }
313 
314 //! Sets initial state of ColorMap to match given intensity item.
315 
317 {
318  ASSERT(intensityItem);
319 
320  m_block_update = true;
321 
328 
329  replot();
330 
331  m_block_update = false;
332 }
333 
334 //! Sets (xmin,xmax,nbins) and (ymin,ymax,nbins) of ColorMap from intensity item.
335 
337 {
338  m_customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
339  m_customPlot->axisRect()->setupFullAxesBox(true);
340  m_colorMap->data()->setSize(item->xSize(), item->ySize());
341  m_colorMap->data()->setRange(GUI::View::RangeUtils::itemXrange(item),
343 }
344 
345 //! Sets zoom range of X,Y axes as in intensity item.
346 
348 {
349  setAxesRangeConnected(false);
350  m_customPlot->xAxis->setRange(item->getLowerX(), item->getUpperX());
351  m_customPlot->yAxis->setRange(item->getLowerY(), item->getUpperY());
352  setAxesRangeConnected(true);
353 }
354 
355 //! Sets X,Y axes labels from item
356 
358 {
359  auto* xaxis = item->xAxisItem();
360  if (xaxis->isTitleVisible())
361  m_customPlot->xAxis->setLabel(item->getXaxisTitle());
362  else
363  m_customPlot->xAxis->setLabel(QString());
364 
365  m_colorScale->setMargins(QMargins(0, 0, 0, 0));
366 
367  auto* yaxis = item->yAxisItem();
368  if (yaxis->isTitleVisible())
369  m_customPlot->yAxis->setLabel(item->getYaxisTitle());
370  else
371  m_customPlot->yAxis->setLabel(QString());
372 }
373 
374 //! Sets the intensity values to ColorMap.
375 
377 {
378  auto* data = item->getDatafield();
379  if (!data) {
380  m_colorMap->data()->clear();
381  return;
382  }
383 
384  int nx(item->xSize()); // outside of the loop because of slow retrieval
385  int ny(item->ySize());
386  for (int ix = 0; ix < nx; ++ix)
387  for (int iy = 0; iy < ny; ++iy)
388  m_colorMap->data()->setCell(ix, iy, (*data)[iy + ny * ix]);
389 }
390 
391 //! Sets the appearance of color scale (visibility, gradient type) from intensity item.
392 
394 {
397  m_colorMap->setInterpolate(intensityItem()->isInterpolated());
398  // make sure the axis rect and color scale synchronize their bottom and top margins (so they
399  // line up):
400  auto* marginGroup = new QCPMarginGroup(m_customPlot);
401  m_customPlot->axisRect()->setMarginGroup(QCP::msBottom | QCP::msTop, marginGroup);
402  m_colorScale->setMarginGroup(QCP::msBottom | QCP::msTop, marginGroup);
403 }
404 
406 {
407  setDataRangeConnected(false);
408  m_colorMap->setDataRange(GUI::View::RangeUtils::itemDataZoom(item));
409  setLogz(item->isLogz());
410  setDataRangeConnected(true);
411 }
412 
413 void ColorMap::setColorScaleVisible(bool visibility_flag)
414 {
415  m_colorBarLayout->setVisible(visibility_flag);
416  if (visibility_flag) {
417  // add it to the right of the main axis rect
418  if (!m_customPlot->plotLayout()->hasElement(0, 1))
419  m_customPlot->plotLayout()->addElement(0, 1, m_colorBarLayout);
420  } else {
421  m_customPlot->plotLayout()->take(m_colorBarLayout);
422  m_customPlot->plotLayout()->simplify();
423  }
424 }
425 
426 //! Calculates left, right margins around color map to report to projection plot.
427 
429 {
430  QMargins axesMargins = m_customPlot->axisRect()->margins();
431  // QMargins colorBarMargins = m_colorScale->margins();
432  // QMargins colorScaleMargins = m_colorScale->axis()->axisRect()->margins();
433 
434  double left = axesMargins.left();
435  // double right = axesMargins.right() + colorBarMargins.right() + m_colorScale->barWidth()
436  // + colorScaleMargins.right() + m_colorBarLayout->rect().width();
437 
438  double right = axesMargins.right() + m_colorBarLayout->rect().width();
439 
440  emit marginsChanged(left, right);
441 }
442 
444 {
445  return const_cast<IntensityDataItem*>(static_cast<const ColorMap*>(this)->intensityItem());
446 }
447 
449 {
450  return dynamic_cast<const IntensityDataItem*>(currentItem());
451 }
Defines various axis items.
Defines class ColorMap.
Defines class IntensityDataItem.
Defines various constants for plotting.
Defines the class PlotEventInfo.
Defines RangeUtils namespace.
Defines class UpdateTimer.
static bool isLogScalePropertyName(const QString &name)
Definition: AxesItems.cpp:193
static constexpr auto M_TYPE
Definition: AxesItems.h:81
static bool isVisibilityPropertyName(const QString &name)
Definition: AxesItems.cpp:131
static bool isBoundsPropertiesName(const QString &name)
either lower bound or upper bound property name
Definition: AxesItems.cpp:75
bool visibilityValue() const
Definition: AxesItems.cpp:121
static bool isTitlePropertyName(const QString &name)
Definition: AxesItems.cpp:95
static constexpr auto M_TYPE
Definition: AxesItems.h:35
static bool isTitleVisiblePropertyName(const QString &name)
Definition: AxesItems.cpp:105
The ColorMap class presents 2D intensity data from IntensityDataItem as color map.
Definition: ColorMap.h:36
QCPColorMap * m_colorMap
Definition: ColorMap.h:101
UpdateTimer * m_updateTimer
Definition: ColorMap.h:103
QRectF viewportRectangleInWidgetCoordinates()
Returns rectangle representing current axes zoom state in widget coordinates.
Definition: ColorMap.cpp:53
void setAxesRangeFromItem(IntensityDataItem *item)
Sets (xmin,xmax,nbins) and (ymin,ymax,nbins) of ColorMap from intensity item.
Definition: ColorMap.cpp:336
QCPLayoutGrid * m_colorBarLayout
Definition: ColorMap.h:104
bool m_block_update
Definition: ColorMap.h:106
ColorMap(QWidget *parent=nullptr)
Definition: ColorMap.cpp:31
void setUpdateTimerConnected(bool isConnected)
Definition: ColorMap.cpp:299
PlotEventInfo eventInfo(double xpos, double ypos) const override
Returns PlotEventInfo corresponding to given axes coordinates.
Definition: ColorMap.cpp:67
void setConnected(bool isConnected)
Definition: ColorMap.cpp:258
void setFixedColorMapMargins()
to make fixed margins for whole colormap (change in axes labels wont affect axes rectangle)
Definition: ColorMap.cpp:309
void replot()
Schedule replot for later execution by onTimeReplot() slot.
Definition: ColorMap.cpp:195
void unsubscribeFromItem() override
Definition: ColorMap.cpp:227
void onYaxisRangeChanged(QCPRange newRange)
Propagate ymin, ymax back to IntensityDataItem.
Definition: ColorMap.cpp:185
void initColorMap()
creates and initializes the color map
Definition: ColorMap.cpp:233
void marginsChanged(double left, double right)
void setAxesRangeConnected(bool isConnected)
Connects/disconnects signals related to ColorMap's X,Y axes rectangle change.
Definition: ColorMap.cpp:267
void setAxesLabelsFromItem(IntensityDataItem *item)
Sets X,Y axes labels from item.
Definition: ColorMap.cpp:357
void setDataRangeFromItem(IntensityDataItem *item)
Definition: ColorMap.cpp:405
void setColorMapFromItem(IntensityDataItem *intensityItem)
Sets initial state of ColorMap to match given intensity item.
Definition: ColorMap.cpp:316
void setDataRangeConnected(bool isConnected)
Connects/disconnects signals related to ColorMap's Z-axis (min,max) change.
Definition: ColorMap.cpp:290
void onPropertyChanged(const QString &property_name)
updates color map depending on IntensityDataItem properties
Definition: ColorMap.cpp:109
void onAxisPropertyChanged(const QString &axisName, const QString &propertyName)
Definition: ColorMap.cpp:126
void onTimeToReplot()
Replots ColorMap.
Definition: ColorMap.cpp:202
void subscribeToItem() override
Definition: ColorMap.cpp:207
QCPColorScale * m_colorScale
Definition: ColorMap.h:102
void onDataRangeChanged(QCPRange newRange)
Propagate zmin, zmax back to IntensityDataItem.
Definition: ColorMap.cpp:168
void setColorScaleAppearanceFromItem(IntensityDataItem *item)
Sets the appearance of color scale (visibility, gradient type) from intensity item.
Definition: ColorMap.cpp:393
IntensityDataItem * intensityItem()
Definition: ColorMap.cpp:443
void setDataFromItem(IntensityDataItem *item)
Sets the intensity values to ColorMap.
Definition: ColorMap.cpp:376
void resetView()
reset all axes min,max to initial value
Definition: ColorMap.cpp:96
void setLogz(bool logz)
sets logarithmic scale
Definition: ColorMap.cpp:89
void onIntensityModified()
Definition: ColorMap.cpp:101
void onXaxisRangeChanged(QCPRange newRange)
Propagate xmin, xmax back to IntensityDataItem.
Definition: ColorMap.cpp:176
QCustomPlot * m_customPlot
Definition: ColorMap.h:100
void setColorScaleVisible(bool visibility_flag)
Definition: ColorMap.cpp:413
void marginsChangedNotify()
Calculates left, right margins around color map to report to projection plot.
Definition: ColorMap.cpp:428
void setAxesZoomFromItem(IntensityDataItem *item)
Sets zoom range of X,Y axes as in intensity item.
Definition: ColorMap.cpp:347
static bool isAxesUnitsPropertyName(const QString &name)
Definition: DataItem.cpp:113
Datafield * getDatafield()
Definition: DataItem.h:41
void setLowerX(double value)
const AmplitudeAxisItem * zAxisItem() const
QString getXaxisTitle() const
void setLowerY(double value)
void setLowerAndUpperZ(double zmin, double zmax)
static bool isXaxisPropertyName(const QString &name)
double getLowerY() const
Returns lower and upper zoom ranges of y-axis.
static bool isZaxisPropertyName(const QString &name)
void resetView()
Set axes viewport to original data.
const BasicAxisItem * yAxisItem() const
void setUpperX(double value)
QString getYaxisTitle() const
static bool isInterpolatedPropertyName(const QString &name)
double getUpperY() const
const BasicAxisItem * xAxisItem() const
static bool isYaxisPropertyName(const QString &name)
double getUpperX() const
void setUpperY(double value)
double getLowerX() const
Returns lower and upper zoom ranges of x-axis.
static bool isGradientPropertyName(const QString &name)
void setOnPropertyChange(std::function< void(QString)> f, const void *caller=nullptr)
Definition: ModelMapper.cpp:39
void setOnValueChange(std::function< void(void)> f, const void *caller=nullptr)
Definition: ModelMapper.cpp:34
void setOnChildPropertyChange(std::function< void(SessionItem *, QString)> f, const void *caller=nullptr)
Calls back on child property change, report childItem and property name.
Definition: ModelMapper.cpp:53
Contains parameters of mouse position in 1D or 2D plot.
Definition: PlotEventInfo.h:26
void setNx(int nx)
Definition: PlotEventInfo.h:49
void setNy(int ny)
Definition: PlotEventInfo.h:51
int ny() const
Definition: PlotEventInfo.h:52
void setInAxesRange(bool flag)
Definition: PlotEventInfo.h:35
void setX(double x)
Definition: PlotEventInfo.h:41
void setY(double y)
Definition: PlotEventInfo.h:43
void setValue(double value)
Definition: PlotEventInfo.h:46
void setLogValueAxis(bool flag)
Definition: PlotEventInfo.h:38
int nx() const
Definition: PlotEventInfo.h:50
Common interface for plot-descriptor interaction.
PLOT_TYPE plotType() const
Returns the type of current plot.
bool axesRangeContains(double xpos, double ypos) const
Returns true if axes rectangle contains given in axes coordinates.
double yAxisCoordToPixel(double axis_coordinate) const
void setMouseTrackingEnabled(bool enable)
Tracks move events (used when showing profile histograms and printing status string)
double xAxisCoordToPixel(double axis_coordinate) const
transform axes coordinates to CustomPlot widget coordinates
SessionItem * currentItem()
Base class for a GUI data item.
Definition: SessionItem.h:204
QString itemName() const
Get item name, return display name if no name is set.
ModelMapper * mapper()
Returns the current model mapper of this item. Creates new one if necessary.
QString modelType() const
Get model type.
The UpdateTimer class accumulates update requests during certain period of time, and at the end of th...
Definition: UpdateTimer.h:27
void scheduleUpdate()
Definition: UpdateTimer.cpp:42
void timeToUpdate()
int plot_tick_label_size()
Definition: PlotConstants.h:23
int plot_colorbar_size()
Definition: PlotConstants.h:33
QString const & name(EShape k)
Definition: particles.cpp:20
QSize SizeOfLetterM(const QWidget *widget=nullptr)
Returns size of largest letter of default system font.
Definition: StyleUtils.cpp:113
QCPRange itemYrange(const IntensityDataItem *item)
Returns y-axis range.
Definition: RangeUtils.cpp:89
QCPColorGradient itemGradient(const IntensityDataItem *item)
Definition: RangeUtils.cpp:74
QCPRange itemZoomY(const IntensityDataItem *item)
Returns y-axis vizible range (zoom).
Definition: RangeUtils.cpp:94
void setLogz(QCPColorScale *scale, bool isLogz)
Definition: RangeUtils.cpp:110
void setDefaultMargins(QCustomPlot *customPlot)
Sets default margins for axes rectangle plot.
Definition: RangeUtils.cpp:138
QCPRange itemXrange(const IntensityDataItem *item)
Returns x-axis range.
Definition: RangeUtils.cpp:79
QCPRange itemDataZoom(const IntensityDataItem *item)
Returns z-axis visible range (zoom).
Definition: RangeUtils.cpp:105
QCPRange itemZoomX(const IntensityDataItem *item)
Returns x-axis vizible range (zoom).
Definition: RangeUtils.cpp:84