Spectrogram.cpp 35.3 KB
Newer Older
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
1
/***************************************************************************
2
3
        File                 : Spectrogram.cpp
        Project              : QtiPlot
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
4
--------------------------------------------------------------------
5
6
7
        Copyright            : (C) 2006 by Ion Vasilief
        Email (use @ for *)  : ion_vasilief*yahoo.fr
        Description          : QtiPlot's Spectrogram Class
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  This program is free software; you can redistribute it and/or modify   *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation; either version 2 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/
Roman Tolchenov's avatar
Roman Tolchenov committed
28
#include "MantidQtWidgets/Common/qwt_compat.h"
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
29
30
31
32
#include "Spectrogram.h"
#include <math.h>
#include <QPen>
#include <qwt_scale_widget.h>
Sofia Antony's avatar
Sofia Antony committed
33
34
#include <QColor>
#include <qwt_painter.h>
35
#include <qwt_scale_engine.h>
Sofia Antony's avatar
Sofia Antony committed
36
#include <QPainter>
Peterson, Peter's avatar
Peterson, Peter committed
37
#include <qwt_symbol.h>
38

39
#include "Mantid/MantidMatrix.h"
40
#include "Mantid/MantidMatrixFunction.h"
41
#include "MantidAPI/IMDIterator.h"
Simon Heybrock's avatar
Simon Heybrock committed
42
#include "MantidKernel/Strings.h"
43
#include "MantidKernel/make_unique.h"
Roman Tolchenov's avatar
Roman Tolchenov committed
44
45
46
#include "MantidQtWidgets/Common/PlotAxis.h"
#include "MantidQtWidgets/Common/QwtRasterDataMD.h"
#include "MantidQtWidgets/Common/SignalRange.h"
47

Roman Tolchenov's avatar
Roman Tolchenov committed
48
#include "MantidQtWidgets/Common/TSVSerialiser.h"
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
49

50
#include <numeric>
51

52
53
54
55
56
57
58
59
60
61
62
63
64
65
Spectrogram::Spectrogram()
    : QObject(), QwtPlotSpectrogram(), d_color_map_pen(false), d_matrix(0),
      d_funct(0), d_wsData(0), d_wsName(), color_axis(QwtPlot::yRight),
      color_map_policy(Default), color_map(QwtLinearColorMap()),
      d_show_labels(true), d_white_out_labels(false), d_labels_angle(0.0),
      d_selected_label(NULL), d_click_pos_x(0.), d_click_pos_y(0.),
      d_labels_x_offset(0), d_labels_y_offset(0),
      d_labels_align(Qt::AlignHCenter), m_nRows(0), m_nColumns(0),
      m_bIntensityChanged(false), d_color_map_autoscale(true) {}

Spectrogram::Spectrogram(const QString &wsName,
                         const Mantid::API::IMDWorkspace_const_sptr &workspace)
    : QObject(), QwtPlotSpectrogram(), d_matrix(NULL), d_funct(NULL),
      d_wsData(NULL), d_wsName(), color_axis(QwtPlot::yRight),
66
67
68
69
      color_map_policy(Default), d_show_labels(true), d_white_out_labels(true),
      d_labels_x_offset(0), d_labels_y_offset(0),
      d_labels_align(Qt::AlignHCenter), mColorMap(),
      d_color_map_autoscale(true) {
70
  d_wsData = dataFromWorkspace(workspace);
71
  setData(*d_wsData);
72
  d_wsName = wsName.toStdString();
73

74
75
  double step =
      fabs(data().range().maxValue() - data().range().minValue()) / 5.0;
76
  QwtValueList contourLevels;
77
78
  for (double level = data().range().minValue() + step;
       level < data().range().maxValue(); level += step)
79
80
    contourLevels += level;
  setContourLevels(contourLevels);
81
82
83

  observePostDelete();
  observeADSClear();
84
  observeAfterReplace();
85
86
}

87
88
89
90
Spectrogram::Spectrogram(Matrix *m)
    : QObject(), QwtPlotSpectrogram(QString(m->objectName())), d_matrix(m),
      d_funct(0), d_wsData(NULL), d_wsName(), color_axis(QwtPlot::yRight),
      color_map_policy(Default), mColorMap(), d_color_map_autoscale(true) {
91
  setData(MatrixData(m));
92
93
  double step =
      fabs(data().range().maxValue() - data().range().minValue()) / 5.0;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
94

95
  QwtValueList contourLevels;
96
97
  for (double level = data().range().minValue() + step;
       level < data().range().maxValue(); level += step)
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
98
99
    contourLevels += level;

100
  setContourLevels(contourLevels);
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
101
102
}

103
104
105
106
107
108
Spectrogram::Spectrogram(Function2D *f, int nrows, int ncols, double left,
                         double top, double width, double height, double minz,
                         double maxz)
    : QObject(), QwtPlotSpectrogram(), d_matrix(0), d_funct(f), d_wsData(NULL),
      d_wsName(), color_axis(QwtPlot::yRight), color_map_policy(Default),
      color_map(QwtLinearColorMap()), d_color_map_autoscale(true) {
109
110
111
  setData(FunctionData(f, nrows, ncols, left, top, width, height, minz, maxz));
  double step =
      fabs(data().range().maxValue() - data().range().minValue()) / 5.0;
112

113
  QwtValueList contourLevels;
114
115
  for (double level = data().range().minValue() + step;
       level < data().range().maxValue(); level += step)
116
117
118
    contourLevels += level;

  setContourLevels(contourLevels);
119
120
}

121
122
123
124
125
126
127
128
129
130
Spectrogram::Spectrogram(Function2D *f, int nrows, int ncols,
                         QwtDoubleRect bRect, double minz, double maxz)
    : QObject(), QwtPlotSpectrogram(), d_color_map_pen(false), d_matrix(0),
      d_funct(f), d_wsData(NULL), d_wsName(), color_axis(QwtPlot::yRight),
      color_map_policy(Default), d_show_labels(true), d_white_out_labels(false),
      d_labels_angle(0.0), d_selected_label(NULL), d_labels_color(Qt::black),
      d_labels_x_offset(0), d_labels_y_offset(0),
      d_labels_align(Qt::AlignHCenter), d_labels_font(QFont()), mColorMap(),
      m_nRows(nrows), m_nColumns(ncols), mScaledValues(0),
      m_bIntensityChanged(false), d_color_map_autoscale(true) {
131
  setTitle("UserHelperFunction");
132
133
134
  setData(FunctionData(f, nrows, ncols, bRect, minz, maxz));
  double step =
      fabs(data().range().maxValue() - data().range().minValue()) / 5.0;
135
136

  QwtValueList contourLevels;
137
138
  for (double level = data().range().minValue() + step;
       level < data().range().maxValue(); level += step)
139
140
    contourLevels += level;
  setContourLevels(contourLevels);
Sofia Antony's avatar
Sofia Antony committed
141
}
142

143
Spectrogram::~Spectrogram() {
144
145
  observePostDelete(false);
  observeADSClear(false);
146
  observeAfterReplace(false);
Sofia Antony's avatar
Sofia Antony committed
147
}
148

149
150
151
152
/**
 * Called after a workspace has been deleted
 * @param wsName The name of the workspace that has been deleted
 */
153
154
void Spectrogram::postDeleteHandle(const std::string &wsName) {
  if (wsName == d_wsName) {
155
156
157
158
159
160
161
162
    observePostDelete(false);
    emit removeMe(this);
  }
}

/**
 * Called after a the ADS has been cleared
 */
163
void Spectrogram::clearADSHandle() {
164
165
166
  observeADSClear(false);
  postDeleteHandle(d_wsName);
}
167

168
169
170
171
/**
 * @param wsName The name of the workspace that has been replaced
 * @param ws A pointer to the new workspace
 */
172
173
174
175
void Spectrogram::afterReplaceHandle(
    const std::string &wsName,
    const boost::shared_ptr<Mantid::API::Workspace> ws) {
  if (wsName == d_wsName) {
176
177
178
179
    updateData(boost::dynamic_pointer_cast<Mantid::API::IMDWorkspace>(ws));
  }
}

180
void Spectrogram::setContourLevels(const QwtValueList &levels) {
Peterson, Peter's avatar
Peterson, Peter committed
181
182
  QwtPlotSpectrogram::setContourLevels(levels);
  createLabels();
183
184
}

185
void Spectrogram::updateData(Matrix *m) {
186
  if (!m || !plot())
187
    return;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
188

189
  setData(MatrixData(m));
190
191
192
  postDataUpdate();
}

193
194
195
196
void Spectrogram::updateData(
    const Mantid::API::IMDWorkspace_const_sptr &workspace) {
  if (!workspace || !plot())
    return;
197

198
199
200
201
202
203
204
205
206
207
  std::unique_ptr<const QwtDoubleInterval> range = nullptr;
  if (d_wsData) {
    if (!d_color_map_autoscale) {
      // Find the color range the data should use
      range = Mantid::Kernel::make_unique<const QwtDoubleInterval>(
          d_wsData->range());
    }
    delete d_wsData;
  }
  d_wsData = dataFromWorkspace(workspace, range.get());
208
209
210
211
  setData(*d_wsData);
  postDataUpdate();
}

212
213
214
215
216
217
218
219
220
/**
 * Extracts data from workspace
 * @param workspace :: [input] Pointer to workspace
 * @param range :: [input] (optional) Data range - set null for full range
 * @returns Data
 */
MantidQt::API::QwtRasterDataMD *Spectrogram::dataFromWorkspace(
    const Mantid::API::IMDWorkspace_const_sptr &workspace,
    const QwtDoubleInterval *range) {
221
  auto *wsData = new MantidQt::API::QwtRasterDataMD();
222
223
224
225
226
227
  wsData->setWorkspace(workspace);
  wsData->setFastMode(false);
  wsData->setNormalization(Mantid::API::NoNormalization);
  wsData->setZerosAsNan(false);

  // colour range
228
229
230
231
232
233
234
  QwtDoubleInterval fullRange =
      MantidQt::API::SignalRange(*workspace).interval();
  if (range) {
    wsData->setRange(*range);
  } else {
    wsData->setRange(fullRange);
  }
235
236
237

  auto dim0 = workspace->getDimension(0);
  auto dim1 = workspace->getDimension(1);
238
  Mantid::coord_t minX(dim0->getMinimum()), maxX(dim0->getMaximum()),
239
      minY(dim1->getMinimum()), maxY(dim1->getMaximum());
240
241
242
  Mantid::coord_t dx(dim0->getBinWidth()), dy(dim1->getBinWidth());
  const Mantid::coord_t width = (maxX - minX) + dx;
  const Mantid::coord_t height = (maxY - minY) + dy;
243
  QwtDoubleRect bounds(minX - 0.5 * dx, minY - 0.5 * dy, width, height);
244
  wsData->setBoundingRect(bounds.normalized());
245
246
247
  return wsData;
}

248
void Spectrogram::postDataUpdate() {
249
  auto *plot = this->plot();
250
  setLevelsNumber(levels());
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
251

252
253
254
255
256
257
258
259
  // Auto-scale color bar
  if (d_color_map_autoscale) {
    QwtScaleWidget *colorAxis = plot->axisWidget(color_axis);
    if (colorAxis) {
      colorAxis->setColorMap(data().range(), colorMap());
    }
    plot->setAxisScale(color_axis, data().range().minValue(),
                       data().range().maxValue());
260
  }
261

262
  if (d_wsData) {
263
    auto workspace = d_wsData->getWorkspace();
264
    if (workspace) {
265
      using MantidQt::API::PlotAxis;
266
      plot->setAxisTitle(QwtPlot::xBottom, PlotAxis(*workspace, 0).title());
267
268
269
270
      plot->setAxisTitle(QwtPlot::yLeft, PlotAxis(*workspace, 1).title());
    }
  }

271
  plot->replot();
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
272
273
}

274
void Spectrogram::setLevelsNumber(int levels) {
275
276
277
  if (levels <= 0)
    return;

278
279
  double step = fabs(data().range().maxValue() - data().range().minValue()) /
                (double)levels;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
280

281
  QwtValueList contourLevels;
282
283
  for (double level = data().range().minValue() + step;
       level < data().range().maxValue(); level += step)
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
284
285
    contourLevels += level;

286
  setContourLevels(contourLevels);
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
287
288
}

289
bool Spectrogram::hasColorScale() {
290
291
292
  QwtPlot *plot = this->plot();
  if (!plot)
    return false;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
293

294
  if (!plot->axisEnabled(color_axis))
295
    return false;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
296

297
298
  QwtScaleWidget *colorAxis = plot->axisWidget(color_axis);
  return colorAxis->isColorBarEnabled();
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
299
300
}

301
void Spectrogram::showColorScale(int axis, bool on) {
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
  if (hasColorScale() == on && color_axis == axis)
    return;

  QwtPlot *plot = this->plot();
  if (!plot)
    return;

  QwtScaleWidget *colorAxis = plot->axisWidget(color_axis);
  colorAxis->setColorBarEnabled(false);

  color_axis = axis;

  // We must switch main and the color scale axes and their respective scales
  int xAxis = this->xAxis();
  int yAxis = this->yAxis();
317
  int oldMainAxis = yAxis;
318
  if (axis == QwtPlot::xBottom || axis == QwtPlot::xTop) {
319
320
    oldMainAxis = xAxis;
    xAxis = 5 - color_axis;
321
  } else if (axis == QwtPlot::yLeft || axis == QwtPlot::yRight) {
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
    oldMainAxis = yAxis;
    yAxis = 1 - color_axis;
  }

  // First we switch axes
  setAxis(xAxis, yAxis);

  // Next we switch axes scales
  QwtScaleDiv *scDiv = plot->axisScaleDiv(oldMainAxis);
  if (axis == QwtPlot::xBottom || axis == QwtPlot::xTop)
    plot->setAxisScale(xAxis, scDiv->lBound(), scDiv->hBound());
  else if (axis == QwtPlot::yLeft || color_axis == QwtPlot::yRight)
    plot->setAxisScale(yAxis, scDiv->lBound(), scDiv->hBound());

  colorAxis = plot->axisWidget(color_axis);
337
338
  plot->setAxisScale(color_axis, data().range().minValue(),
                     data().range().maxValue());
339
340
341
342
343
344
  colorAxis->setColorBarEnabled(on);
  colorAxis->setColorMap(data().range(), colorMap());
  if (!plot->axisEnabled(color_axis))
    plot->enableAxis(color_axis);
  colorAxis->show();
  plot->updateLayout();
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
345
346
}

347
int Spectrogram::colorBarWidth() {
348
349
350
  QwtPlot *plot = this->plot();
  if (!plot)
    return 0;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
351

352
353
  QwtScaleWidget *colorAxis = plot->axisWidget(color_axis);
  return colorAxis->colorBarWidth();
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
354
355
}

356
void Spectrogram::setColorBarWidth(int width) {
357
358
359
  QwtPlot *plot = this->plot();
  if (!plot)
    return;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
360

361
362
  QwtScaleWidget *colorAxis = plot->axisWidget(color_axis);
  colorAxis->setColorBarWidth(width);
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
363
364
}

365
Spectrogram *Spectrogram::copy() {
366
  Spectrogram *new_s;
367
368
369
370
371
372
373
374
375
376
377
378
  Matrix *m = matrix();
  if (m)
    new_s = new Spectrogram(matrix());
  else
    new_s =
        new Spectrogram(d_funct, m_nRows, m_nColumns, boundingRect(),
                        data().range().minValue(), data().range().maxValue());

  new_s->setDisplayMode(QwtPlotSpectrogram::ImageMode,
                        testDisplayMode(QwtPlotSpectrogram::ImageMode));
  new_s->setDisplayMode(QwtPlotSpectrogram::ContourMode,
                        testDisplayMode(QwtPlotSpectrogram::ContourMode));
379
380
381
  new_s->color_map_policy = color_map_policy;
  if (new_s->color_map_policy == GrayScale)
    new_s->setGrayScale();
382
383
  else
    new_s->setCustomColorMap(new_s->getColorMap());
384
385
386
387
388
389

  new_s->setAxis(xAxis(), yAxis());
  new_s->setDefaultContourPen(defaultContourPen());
  new_s->setLevelsNumber(levels());

  new_s->mutableColorMap().changeScaleType(getColorMap().getScaleType());
390
  new_s->mutableColorMap().setNthPower(getColorMap().getNthPower());
391
  return new_s;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
392
393
}

394
void Spectrogram::setGrayScale() {
395
396
  color_map = QwtLinearColorMap(Qt::black, Qt::white);
  setColorMap(color_map);
397
  // setColorMap(mColorMap);
398
  color_map_policy = GrayScale;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
399

400
401
402
  QwtPlot *plot = this->plot();
  if (!plot)
    return;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
403

404
  QwtScaleWidget *colorAxis = plot->axisWidget(color_axis);
405
  if (colorAxis) {
406
407
    colorAxis->setColorMap(data().range(), colorMap());
  }
Sofia Antony's avatar
Sofia Antony committed
408
}
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
409

410
void Spectrogram::setDefaultColorMap() {
411
  MantidColorMap map = getDefaultColorMap();
412

413
414
415
  mCurrentColorMap = map.getFilePath();
  mColorMap = map;
  setColorMap(map);
416

417
418
419
420
421
422
423
424
425
  color_map_policy = Default;

  QwtPlot *plot = this->plot();
  if (!plot)
    return;

  QwtScaleWidget *colorAxis = plot->axisWidget(color_axis);
  if (colorAxis)
    colorAxis->setColorMap(this->data().range(), this->colorMap());
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
426
}
427

428
MantidColorMap Spectrogram::getDefaultColorMap() {
429
430
431

  QSettings settings;
  settings.beginGroup("Mantid/2DPlotSpectrogram");
432
  // Load Colormap. If the file is invalid the default stored colour map is used
433
434
435
  QString lastColormapFile = settings.value("ColormapFile", "").toString();
  settings.endGroup();

436
437
  // if the file is not valid you will get the default
  MantidColorMap retColorMap(lastColormapFile, GraphOptions::Linear);
438
439
440
441

  return retColorMap;
}

442
void Spectrogram::loadColorMap(const QString &file) {
443
444
445
446
  mColorMap.loadMap(file);
  setMantidColorMap(mColorMap);
}

447
void Spectrogram::setCustomColorMap(const QwtColorMap &map) {
448
  setColorMap(map);
449
  // color_map = map;
450
451
452
453
454
455
  color_map_policy = Custom;
  QwtPlot *plot = this->plot();
  if (!plot)
    return;

  QwtScaleWidget *colorAxis = plot->axisWidget(color_axis);
456
  if (colorAxis) {
457
458
    colorAxis->setColorMap(this->data().range(), this->getColorMap());
  }
Sofia Antony's avatar
Sofia Antony committed
459
}
460

461
void Spectrogram::setCustomColorMap(const QwtLinearColorMap &map) {
462
463
464
  setColorMap(map);
  color_map = map;
  color_map_policy = Custom;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
465

466
467
468
  QwtPlot *plot = this->plot();
  if (!plot)
    return;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
469

470
  QwtScaleWidget *colorAxis = plot->axisWidget(color_axis);
471
  if (colorAxis) {
472
473
    colorAxis->setColorMap(this->data().range(), this->colorMap());
  }
Sofia Antony's avatar
Sofia Antony committed
474
}
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
475

476
QwtLinearColorMap Spectrogram::defaultColorMap() {
477
478
479
480
481
  QwtLinearColorMap colorMap(Qt::blue, Qt::red);
  colorMap.addColorStop(0.25, Qt::cyan);
  colorMap.addColorStop(0.5, Qt::green);
  colorMap.addColorStop(0.75, Qt::yellow);
  return colorMap;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
482
}
483

484
void Spectrogram::setColorMapPen(bool on) {
Peterson, Peter's avatar
Peterson, Peter committed
485
486
487
488
489
490
491
492
493
494
495
496
  if (d_color_map_pen == on)
    return;

  d_color_map_pen = on;
  if (on) {
    setDefaultContourPen(Qt::NoPen);
    d_pen_list.clear();
  }
}
/**
for creating contour line labels
 */
497
498
void Spectrogram::createLabels() {
  foreach (QwtPlotMarker *m, d_labels_list) {
Peterson, Peter's avatar
Peterson, Peter committed
499
500
501
502
503
504
    m->detach();
    delete m;
  }
  d_labels_list.clear();
  QwtValueList levels = contourLevels();
  const int numLevels = levels.size();
505
  for (int l = 0; l < numLevels; l++) {
Peterson, Peter's avatar
Peterson, Peter committed
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
    PlotMarker *m = new PlotMarker(l, d_labels_angle);
    QwtText t = QwtText(QString::number(levels[l]));
    t.setColor(d_labels_color);
    t.setFont(d_labels_font);

    if (d_white_out_labels)
      t.setBackgroundBrush(QBrush(Qt::white));
    else
      t.setBackgroundBrush(QBrush(Qt::transparent));
    m->setLabel(t);

    int x_axis = xAxis();
    int y_axis = yAxis();
    m->setAxis(x_axis, y_axis);

    QwtPlot *d_plot = plot();
    if (!d_plot)
      return;
524
525
    if (d_plot && d_show_labels) {
      m->attach(d_plot);
Peterson, Peter's avatar
Peterson, Peter committed
526
527
528
529
    }
    d_labels_list << m;
  }
}
530
531
void Spectrogram::showContourLineLabels(bool show) {
  if (show == d_show_labels) {
Peterson, Peter's avatar
Peterson, Peter committed
532
533
534
535
536
537
    return;
  }
  d_show_labels = show;
  QwtPlot *d_plot = plot();
  if (!d_plot)
    return;
538
539
540
541
  foreach (PlotMarker *m, d_labels_list) {
    if (d_show_labels) {
      m->attach(d_plot);
    } else
Peterson, Peter's avatar
Peterson, Peter committed
542
543
544
      m->detach();
  }
}
545

546
void Spectrogram::setLabelsFont(const QFont &font) {
547
548
549
550
551
  if (font == d_labels_font)
    return;

  d_labels_font = font;

552
  foreach (QwtPlotMarker *m, d_labels_list) {
553
554
555
556
557
558
    QwtText t = m->label();
    t.setFont(font);
    m->setLabel(t);
  }
}

559
560
bool Spectrogram::hasSelectedLabels() { return d_selected_label != NULL; }
void Spectrogram::selectLabel(bool on) {
Peterson, Peter's avatar
Peterson, Peter committed
561
562
563
  QwtPlot *d_plot = plot();
  if (!d_plot)
    return;
564
565
566
  if (on) {
    // d_plot->deselect();
    // d_plot->notifyFontChange(d_labels_font);
Peterson, Peter's avatar
Peterson, Peter committed
567
568
  }

569
  foreach (PlotMarker *m, d_labels_list) {
Peterson, Peter's avatar
Peterson, Peter committed
570
    QwtText t = m->label();
571
    if (t.text().isEmpty())
Peterson, Peter's avatar
Peterson, Peter committed
572
573
574
575
576
577
578
579
580
581
582
583
      return;

    if (d_selected_label && m == d_selected_label && on)
      t.setBackgroundPen(QPen(Qt::blue));
    else
      t.setBackgroundPen(QPen(Qt::NoPen));

    m->setLabel(t);
  }
  d_plot->replot();
}

584
bool Spectrogram::selectedLabels(const QPoint &pos) {
Peterson, Peter's avatar
Peterson, Peter committed
585
586
587
588
589
590
  d_selected_label = NULL;
  QwtPlot *d_plot = plot();
  if (!d_plot)
    return false;

  /*if (d_plot->hasActiveTool())
591
592
                return false;*/
  foreach (PlotMarker *m, d_labels_list) {
Peterson, Peter's avatar
Peterson, Peter committed
593
594
595
596
597
598
    int x = d_plot->transform(xAxis(), m->xValue());
    int y = d_plot->transform(yAxis(), m->yValue());

    QMatrix wm;
    wm.translate(x, y);
    wm.rotate(-d_labels_angle);
599
600
    if (wm.mapToPolygon(QRect(QPoint(0, 0), m->label().textSize()))
            .containsPoint(pos, Qt::OddEvenFill)) {
Peterson, Peter's avatar
Peterson, Peter committed
601
602
603
604
605
606
607
608
      d_selected_label = m;
      d_click_pos_x = d_plot->invTransform(xAxis(), pos.x());
      d_click_pos_y = d_plot->invTransform(yAxis(), pos.y());
      selectLabel(true);
      return true;
    }
  }
  return false;
Sofia Antony's avatar
Sofia Antony committed
609
}
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
610

Peterson, Peter's avatar
Peterson, Peter committed
611
612
613
/**
 * Returns a reference to the constant colormap
 */
614
615
const MantidColorMap &Spectrogram::getColorMap() const { return mColorMap; }
void Spectrogram::setMantidColorMap(const MantidColorMap &map) {
Peterson, Peter's avatar
Peterson, Peter committed
616
617
618
619
620
  setColorMap(map);
}
/**
 * Returns a reference to the colormap
 */
621
MantidColorMap &Spectrogram::mutableColorMap() { return mColorMap; }
Peterson, Peter's avatar
Peterson, Peter committed
622
623
624
/**
 * Save properties of the window a persistent store
 */
625
void Spectrogram::saveSettings() {
Peterson, Peter's avatar
Peterson, Peter committed
626
627
  QSettings settings;
  settings.beginGroup("Mantid/2DPlotSpectrogram");
628
629
  // settings.setValue("BackgroundColor",
  // mInstrumentDisplay->currentBackgroundColor());
Peterson, Peter's avatar
Peterson, Peter committed
630
  settings.setValue("ColormapFile", mCurrentColorMap);
631
  settings.setValue("ScaleType", getColorMap().getScaleType());
Peterson, Peter's avatar
Peterson, Peter committed
632
633
634
635
636
  settings.endGroup();
}
/**
 * This method loads the setting from QSettings
 */
637
638
void Spectrogram::loadSettings() {
  // Load Color
Peterson, Peter's avatar
Peterson, Peter committed
639
640
641
  QSettings settings;
  settings.beginGroup("Mantid/2DPlotSpectrogram");

642
  // Load Colormap. If the file is invalid the default stored colour map is used
Peterson, Peter's avatar
Peterson, Peter committed
643
644
645
646
  mCurrentColorMap = settings.value("ColormapFile", "").toString();
  // Set values from settings
  mutableColorMap().loadMap(mCurrentColorMap);

647
648
649
  GraphOptions::ScaleType type =
      (GraphOptions::ScaleType)settings.value("ScaleType", GraphOptions::Log10)
          .toUInt();
Peterson, Peter's avatar
Peterson, Peter committed
650
651
652
653
654
655
656

  mutableColorMap().changeScaleType(type);

  settings.endGroup();
}
/**
 * This method saves the selectcolrmap file name to membervaraible
Sofia Antony's avatar
Sofia Antony committed
657
 */
658
659
void Spectrogram::setColorMapFileName(QString colormapName) {
  mCurrentColorMap = colormapName;
Sofia Antony's avatar
Sofia Antony committed
660
}
661
662
QwtDoubleRect Spectrogram::boundingRect() const {
  return d_matrix ? d_matrix->boundingRect() : data().boundingRect();
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
663
}
664

665
666
667
double Spectrogram::getMinPositiveValue() const {
  // const QwtRasterData* r = &data();
  const SpectrogramData *d = dynamic_cast<const SpectrogramData *>(&data());
668
669
670
  return d ? d->getMinPositiveValue() : 1e-10;
}

671
void Spectrogram::setContourPenList(QList<QPen> lst) {
Peterson, Peter's avatar
Peterson, Peter committed
672
673
674
  d_pen_list = lst;
  setDefaultContourPen(Qt::NoPen);
  d_color_map_pen = false;
Sofia Antony's avatar
Sofia Antony committed
675
}
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
676

677
void Spectrogram::setContourLinePen(int index, const QPen &pen) {
678
679
680
681
  QwtValueList levels = contourLevels();
  if (index < 0 || index >= levels.size())
    return;

682
  if (d_pen_list.isEmpty()) {
683
    QPen p = defaultContourPen();
684
    for (int i = 0; i < levels.size(); i++) {
685
686
687
688
689
690
691
692
693
694
695
696
      if (p.style() == Qt::NoPen)
        d_pen_list << contourPen(levels[i]);
      else
        d_pen_list << p;
    }
  }

  d_pen_list[index] = pen;
  setDefaultContourPen(Qt::NoPen);
  d_color_map_pen = false;
}

697
698
699
double MatrixData::value(double x, double y) const {
  x += 0.5 * dx;
  y -= 0.5 * dy;
700

701
702
  int i = abs(int((y - y_start) / dy));
  int j = abs(int((x - x_start) / dx));
703

704
  if (d_m && i >= 0 && i < n_rows && j >= 0 && j < n_cols)
705
706
707
    return d_m[i][j];
  else
    return 0.0;
Roman Tolchenov's avatar
re #79  
Roman Tolchenov committed
708
}
709

710
double MatrixData::getMinPositiveValue() const {
Peterson, Peter's avatar
Peterson, Peter committed
711
  double zmin = DBL_MAX;
712
713
  for (int i = 0; i < n_rows; ++i) {
    for (int j = 0; i < n_cols; ++j) {
Peterson, Peter's avatar
Peterson, Peter committed
714
      double tmp = d_m[i][j];
715
      if (tmp > 0 && tmp < zmin) {
Peterson, Peter's avatar
Peterson, Peter committed
716
717
718
719
720
721
722
        zmin = tmp;
      }
    }
  }
  return zmin;
}

723
void Spectrogram::setLabelsRotation(double angle) {
Peterson, Peter's avatar
Peterson, Peter committed
724
725
726
727
728
  if (angle == d_labels_angle)
    return;

  d_labels_angle = angle;

729
  foreach (PlotMarker *m, d_labels_list)
730
    m->setAngle(angle);
Peterson, Peter's avatar
Peterson, Peter committed
731
732
}

733
void Spectrogram::setLabelsOffset(double x, double y) {
Peterson, Peter's avatar
Peterson, Peter committed
734
735
736
737
738
739
740
  if (x == d_labels_x_offset && y == d_labels_y_offset)
    return;

  d_labels_x_offset = x;
  d_labels_y_offset = y;
}

741
void Spectrogram::setLabelOffset(int index, double x, double y) {
Peterson, Peter's avatar
Peterson, Peter committed
742
743
744
745
746
747
748
749
750
751
  if (index < 0 || index >= d_labels_list.size())
    return;

  PlotMarker *m = d_labels_list[index];
  if (!m)
    return;

  m->setLabelOffset(x, y);
}

752
void Spectrogram::setLabelsWhiteOut(bool whiteOut) {
Peterson, Peter's avatar
Peterson, Peter committed
753
754
755
756
757
  if (whiteOut == d_white_out_labels)
    return;

  d_white_out_labels = whiteOut;

758
  foreach (QwtPlotMarker *m, d_labels_list) {
Peterson, Peter's avatar
Peterson, Peter committed
759
760
761
762
763
764
765
766
    QwtText t = m->label();
    if (whiteOut)
      t.setBackgroundBrush(QBrush(Qt::white));
    else
      t.setBackgroundBrush(QBrush(Qt::transparent));
    m->setLabel(t);
  }
}
767

768
769
770
void Spectrogram::drawContourLines(
    QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QwtRasterData::ContourLines &contourLines) const {
Peterson, Peter's avatar
Peterson, Peter committed
771

772
  // QwtPlotSpectrogram::drawContourLines(p, xMap, yMap, contourLines);
Peterson, Peter's avatar
Peterson, Peter committed
773
774
  QwtValueList levels = contourLevels();
  const int numLevels = (int)levels.size();
775
  for (int l = 0; l < numLevels; l++) {
Peterson, Peter's avatar
Peterson, Peter committed
776
777
778
    const double level = levels[l];

    QPen pen = defaultContourPen();
779
    if (pen.style() == Qt::NoPen)
Peterson, Peter's avatar
Peterson, Peter committed
780
781
      pen = contourPen(level);

782
    if (pen.style() == Qt::NoPen)
Peterson, Peter's avatar
Peterson, Peter committed
783
784
785
786
787
      continue;

    p->setPen(pen);

    const QPolygonF &lines = contourLines[level];
788
789
790
791
792
    for (int i = 0; i < (int)lines.size(); i += 2) {
      const QPointF p1(xMap.xTransform(lines[i].x()),
                       yMap.transform(lines[i].y()));
      const QPointF p2(xMap.xTransform(lines[i + 1].x()),
                       yMap.transform(lines[i + 1].y()));
Peterson, Peter's avatar
Peterson, Peter committed
793
794
795
796
797
798
799
800
801

      p->drawLine(p1, p2);
    }
  }

  if (d_show_labels)
    updateLabels(p, xMap, yMap, contourLines);
}

802
803
804
void Spectrogram::updateLabels(
    QPainter *p, const QwtScaleMap &xMap, const QwtScaleMap &yMap,
    const QwtRasterData::ContourLines &contourLines) const {
805
  (void)p;    // Avoid compiler warning
806
807
  (void)xMap; // Avoid compiler warning
  (void)yMap; // Avoid compiler warning
Peterson, Peter's avatar
Peterson, Peter committed
808

809
810
  if (d_labels_list.isEmpty())
    return;
811

Peterson, Peter's avatar
Peterson, Peter committed
812
813
814
815
816
817
818
819
  QwtPlot *d_plot = plot();
  if (!d_plot)
    return;

  QwtValueList levels = contourLevels();
  const int numLevels = levels.size();
  int x_axis = xAxis();
  int y_axis = yAxis();
820
  for (int l = 0; l < numLevels; l++) {
Peterson, Peter's avatar
Peterson, Peter committed
821
822
    const double level = levels[l];
    const QPolygonF &lines = contourLines[level];
823
824
825
    if (lines.isEmpty())
      continue;
    int i = (int)lines.size() / 2;
Peterson, Peter's avatar
Peterson, Peter committed
826
827
828
829
830

    PlotMarker *mrk = d_labels_list[l];
    if (!mrk)
      return;
    QSize size = mrk->label().textSize();
831
832
833
834
835
836
    int dx = static_cast<int>(
        (d_labels_x_offset)*0.01 *
        size.height()); // static_cast<int>((d_labels_x_offset +
                        // mrk->xLabelOffset())*0.01*size.height());
    int dy =
        -static_cast<int>(((d_labels_y_offset)*0.01 + 0.5) * size.height());
Peterson, Peter's avatar
Peterson, Peter committed
837
838
839
840
841
842
843

    double x = lines[i].x();
    double y = lines[i].y();
    int x2 = d_plot->transform(x_axis, x) + dx;
    int y2 = d_plot->transform(y_axis, y) + dy;

    mrk->setValue(d_plot->invTransform(x_axis, x2),
844
                  d_plot->invTransform(y_axis, y2));
Peterson, Peter's avatar
Peterson, Peter committed
845
846
847
848
849
  }
}
/**
     for setting the lables color on contour lines
 */
850
void Spectrogram::setLabelsColor(const QColor &c) {
Peterson, Peter's avatar
Peterson, Peter committed
851
852
853
854
855
  if (c == d_labels_color)
    return;

  d_labels_color = c;

856
  foreach (QwtPlotMarker *m, d_labels_list) {
Peterson, Peter's avatar
Peterson, Peter committed
857
858
859
860
861
862
863
864
    QwtText t = m->label();
    t.setColor(c);
    m->setLabel(t);
  }
}
/**
changes the intensity of the colors
 */
865
void Spectrogram::changeIntensity(double start, double end) {
866
  using namespace MantidQt::API;
867
868
  if (d_wsData) {
    d_wsData->setRange(QwtDoubleInterval(start, end));
869
    setData(*d_wsData);
870
871
872
  } else {
    setData(
        FunctionData(d_funct, m_nRows, m_nColumns, boundingRect(), start, end));
873
  }
Peterson, Peter's avatar
Peterson, Peter committed
874
875
876
877
}
/**
 sets the flag for intensity changes
 */
878
void Spectrogram::setIntensityChange(bool on) { m_bIntensityChanged = on; }
Peterson, Peter's avatar
Peterson, Peter committed
879
880
881
/**
returns true if intensity(minz and maxz) changed
 */
882
bool Spectrogram::isIntensityChanged() { return m_bIntensityChanged; }
Peterson, Peter's avatar
Peterson, Peter committed
883
884

/**
885
886
887
888
 * Override QwtPlotSpectrogram::renderImage to draw ragged spectrograms. It is
 * almost
 * a copy of QwtPlotSpectrogram::renderImage except that pixels of the image
 * that are
Peterson, Peter's avatar
Peterson, Peter committed
889
890
 * outside the boundaries of the histograms are set to a special colour (white).
 */
891
892
893
QImage Spectrogram::renderImage(const QwtScaleMap &xMap,
                                const QwtScaleMap &yMap,
                                const QwtDoubleRect &area) const {
894
  // Mantid workspace handled in QwtRasterDataMD
895
896
  if (d_wsData)
    return QwtPlotSpectrogram::renderImage(xMap, yMap, area);
897

898
  // Not Mantid function so just use base class
899
900
901
  auto *mantidFun = dynamic_cast<MantidMatrixFunction *>(d_funct);
  if (!mantidFun)
    return QwtPlotSpectrogram::renderImage(xMap, yMap, area);
902

903
  if (area.isEmpty())
904
    return QImage();
905

906
  QRect rect = transform(xMap, yMap, area);
907

908
  QImage image(rect.size(), this->colorMap().format() == QwtColorMap::RGB
909
910
                                ? QImage::Format_ARGB32
                                : QImage::Format_Indexed8);
911

912
  const QwtDoubleInterval intensityRange = data().range();
913
  if (!intensityRange.isValid())
914
    return image;
915

916
917
918
  if (this->colorMap().format() == QwtColorMap::RGB) {
    return QwtPlotSpectrogram::renderImage(xMap, yMap, area);
  } else if (this->colorMap().format() == QwtColorMap::Indexed) {
919
920
921
    // Modify the colour table so that the last colour is white and transparent
    // which will indicate no value
    QVector<QRgb> ctable = this->colorMap().colorTable(intensityRange);
922
    ctable.back() = qRgba(255, 255, 255, 0);
923
    image.setColorTable(ctable);
924

925
    image.fill(255);
926

927
    // image2matrix_yMap[image_row] ->  matrix_row or -1
928
929
930
931
932
933
934
935
936
937
938
939
940
    std::vector<int> image2matrix_yMap(rect.height(), -1);

    for (size_t row = 0; row < mantidFun->rows(); ++row) {
      double ymin, ymax;
      mantidFun->getRowYRange(row, ymin, ymax);
      int imax =
          yMap.transform(ymin) - rect.top(); // image row corresponding to ymin
      int imin =
          yMap.transform(ymax) - rect.top(); // image row corresponding to ymax
      if (imin < 0) {
        if (imax < 0)
          break;
        else {
941
          imin = 0;
942
        }
943
      }
944
945
      if (imax > rect.height() - 1) {
        if (imin > rect.height() - 1) {
946
          continue;
947
948
        } else {
          imax = rect.height() - 1;
949
        }
950
      }
951
952
      std::fill(image2matrix_yMap.begin() + imin,
                image2matrix_yMap.begin() + imax + 1, static_cast<int>(row));
953
    }
954

955
956
    int imageWidth = rect.width();
    int row0 = -2;
957
    for (size_t i = 0; i < image2matrix_yMap.size(); ++i) {
958
      int row = image2matrix_yMap[i];
959
      if (row < 0) {
960
961
        continue;
      }
962
      if (row == row0) {
963
        unsigned char *line = image.scanLine(static_cast<int>(i));
964
965
        unsigned char *line0 = image.scanLine(static_cast<int>(i - 1));
        std::copy(line0, line0 + imageWidth, line);
966
967
968
969
        continue;
      }
      row0 = row;

970
971
      double xmin, xmax;
      mantidFun->getRowXRange(row, xmin, xmax);
972
      int jmin = -1;
973
      if (std::isfinite(xmin) && std::isfinite(xmax)) {
974
975
        jmin = xMap.transform(xmin) - rect.left();
      } else {
976
977
        continue;
      }
978
979
      if (jmin < 0)
        jmin = 0;
980

981
      unsigned char *line = image.scanLine(static_cast<int>(i)) + jmin;
982
      const auto &X = mantidFun->getHistogramX(row);
983
      int col = 0;
984
985
986
987
988
      int nX = static_cast<int>(X.size()) - 1;
      for (int j = jmin; j < imageWidth; ++j) {
        double x = xMap.invTransform(j + rect.left());
        double x1 = X[col + 1];
        while (x1 < x) {
989
          ++col;
990
991
992
          if (col >= nX)
            break;
          x1 = X[col + 1];
993
        }
994
995
996
997