From 234f8b86911b6665d335572fba4f262887bf67ae Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Wed, 13 Apr 2016 10:21:18 +0200 Subject: [PATCH 1/5] [plotwidget] call suggested_plotter only once In PlotWidget::process(nix:DataArray), call the Plotter:suggested_plotter() and save a few cycles. --- plotwidget.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plotwidget.cpp b/plotwidget.cpp index ec46f13..d5c3394 100644 --- a/plotwidget.cpp +++ b/plotwidget.cpp @@ -59,19 +59,22 @@ void PlotWidget::delete_widgets_from_layout() { Plotter* PlotWidget::process(const nix::DataArray &array) { - if (Plotter::suggested_plotter(array) == PlotterType::Line) { + PlotterType suggestion = Plotter::suggested_plotter(array); + + if (suggestion == PlotterType::Line) { delete_widgets_from_layout(); LinePlotter *lp = new LinePlotter(); ui->scrollAreaWidgetContents->layout()->addWidget(lp); lp->draw(array); return lp; - } else if (Plotter::suggested_plotter(array) == PlotterType::Category) { + } else if (suggestion == PlotterType::Category) { delete_widgets_from_layout(); CategoryPlotter *cp = new CategoryPlotter(); ui->scrollAreaWidgetContents->layout()->addWidget(cp); cp->draw(array); return cp; } + return nullptr; } From 51aa42a788d23057044eae8223ebd47c0b07b5d9 Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Wed, 13 Apr 2016 10:21:40 +0200 Subject: [PATCH 2/5] [git] ignore .idea/ --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 877b938..45db4d1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ NixView.pro.user.18 NixView.pro.user.3.2-pre1 NixView.pro.user.5e84e8a *~ +.idea/ \ No newline at end of file From a67dfd30f988ecf1815d0335296e18d9130d7115 Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Wed, 13 Apr 2016 13:31:23 +0200 Subject: [PATCH 3/5] [imageplotter] new plotter for image/heatmap data --- plotter/imageplotter.cpp | 149 +++++++++++++++++++++++++++++++++++++++ plotter/imageplotter.h | 41 +++++++++++ plotter/imageplotter.ui | 66 +++++++++++++++++ 3 files changed, 256 insertions(+) create mode 100644 plotter/imageplotter.cpp create mode 100644 plotter/imageplotter.h create mode 100644 plotter/imageplotter.ui diff --git a/plotter/imageplotter.cpp b/plotter/imageplotter.cpp new file mode 100644 index 0000000..ee9c2bc --- /dev/null +++ b/plotter/imageplotter.cpp @@ -0,0 +1,149 @@ +#include "imageplotter.h" +#include "ui_imageplotter.h" +#include + + +ImagePlotter::ImagePlotter(QWidget *parent) : + QWidget(parent), ui(new Ui::ImagePlotter), cmap() { + ui->setupUi(this); + +} + +ImagePlotter::~ImagePlotter() { + delete ui; +} + +QCustomPlot *ImagePlotter::get_plot() { + return ui->plot; +} + +struct DimInfo { + bool valid; + QString unit; + QString label; + + QVector ticks; + QCPRange range; +}; + +static DimInfo get_dim_info(const nix::DataArray &array, nix::ndsize_t dim_index) { + nix::Dimension d = array.getDimension(dim_index); + DimInfo info; + info.valid = false; + + nix::NDSize shape = array.dataExtent(); + + if (d.dimensionType() == nix::DimensionType::Sample) { + nix::SampledDimension dim = d.asSampledDimension(); + info.valid = true; + info.label = dim.label().get_value_or("").c_str(); + info.unit = dim.unit().get_value_or("").c_str(); + double si = dim.samplingInterval(); + //todo: safe cast? + info.ticks.resize(static_cast(shape[dim_index-1])); + + double offset = dim.offset().get_value_or(0); + const int size = info.ticks.size(); + for (int i = 0; i < size; i++) { + info.ticks[i] = offset + i * si; + } + + } else if (d.dimensionType() == nix::DimensionType::Range) { + nix::RangeDimension dim = d.asRangeDimension(); + info.valid = true; + info.label = dim.label().get_value_or("").c_str(); + info.unit = dim.unit().get_value_or("").c_str(); + + info.ticks = QVector::fromStdVector(dim.ticks()); + } + + const int size = info.ticks.size(); + if (size > 0) { + info.range.lower = info.ticks[0]; + info.range.upper = info.ticks[size - 1]; + } + + return info; +} + +void ImagePlotter::draw(const nix::DataArray &array) { + if (array.dimensionCount() != 2) { + std::cerr << "ImagePlotter::draw can only draw 2D!" << std::endl; + return; + } + + DimInfo yi = get_dim_info(array, 1); // rows == y + DimInfo xi = get_dim_info(array, 2); // cols == x + + if (!(yi.valid && xi.valid)) { + std::cerr << "ImagePlotter::invalid dimensions found in array!" << std::endl; + return; + } + + QCustomPlot *plot = get_plot(); + + plot->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom); // this will also allow rescaling the color scale by dragging/zooming + plot->axisRect()->setupFullAxesBox(true); + plot->xAxis->setLabel(xi.label + " " + xi.unit); + plot->yAxis->setLabel(yi.label + " " + yi.unit); + + QCPColorMap *colorMap = new QCPColorMap(plot->xAxis, plot->yAxis); + plot->addPlottable(colorMap); + + int nx = xi.ticks.size(); + int ny = yi.ticks.size(); + + colorMap->data()->setSize(nx, ny); + colorMap->data()->setRange(xi.range, yi.range); + colorMap->setInterpolate(false); + + for (int xidx = 0; xidx < nx; xidx++) { + std::vector data(static_cast(nx)); + //lets fetch a whole row (should be fast if data is + // row-major, like hdf5) + //ny, the number of rows in data-array + //xidx, the current column the fetch + // -> fetch all rows at column [xidx] + // count offset + array.getData(data, {ny, 1}, {0, xidx}); + for (int yidx = 0; yidx < ny; yidx++) { + // data[ydix], the data point of the row[yidx], col[xidx] + colorMap->data()->setCell(xidx, yidx, data[yidx]); + } + } + + QCPColorScale *colorScale = new QCPColorScale(plot); + plot->plotLayout()->addElement(0, 1, colorScale); + colorScale->setType(QCPAxis::atRight); + colorMap->setColorScale(colorScale); + if (array.label()) { + colorScale->axis()->setLabel(array.label().get().c_str()); + } + colorMap->setGradient(QCPColorGradient::gpSpectrum); + colorMap->rescaleDataRange(); + + QCPMarginGroup *marginGroup = new QCPMarginGroup(plot); + plot->axisRect()->setMarginGroup(QCP::msBottom|QCP::msTop, marginGroup); + colorScale->setMarginGroup(QCP::msBottom|QCP::msTop, marginGroup); + + plot->rescaleAxes(); +} + + +void ImagePlotter::add_events(const QVector &x_data, const QVector &y_data, const QString &name, + bool y_scale) { + +} + +void ImagePlotter::add_segments(const QVector &positions, const QVector &extents, const QString &name) { + +} + +void ImagePlotter::set_label(const std::string &label) { + +} + +PlotterType ImagePlotter::plotter_type() const { + return PlotterType::Image; +} + diff --git a/plotter/imageplotter.h b/plotter/imageplotter.h new file mode 100644 index 0000000..5a941cb --- /dev/null +++ b/plotter/imageplotter.h @@ -0,0 +1,41 @@ +#ifndef IMAGEPLOTTER_H +#define IMAGEPLOTTER_H + +#include +#include "plotter.h" +#include +#include "colormap.hpp" + +namespace Ui { + class ImagePlotter; +} + +class ImagePlotter : public QWidget, public Plotter { +Q_OBJECT +public: + + virtual void add_events(const QVector &x_data, const QVector &y_data, const QString &name, + bool y_scale) override; + + virtual void add_segments(const QVector &positions, const QVector &extents, + const QString &name) override; + + virtual void set_label(const std::string &label) override; + + virtual PlotterType plotter_type() const override; + + +public: + explicit ImagePlotter(QWidget *parent = 0); + ~ImagePlotter(); + + void draw(const nix::DataArray &array); + +private: + Ui::ImagePlotter *ui; + ColorMap cmap; + + QCustomPlot* get_plot(); +}; + +#endif //IMAGEPLOTTER_H diff --git a/plotter/imageplotter.ui b/plotter/imageplotter.ui new file mode 100644 index 0000000..433528b --- /dev/null +++ b/plotter/imageplotter.ui @@ -0,0 +1,66 @@ + + + ImagePlotter + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + Form + + + false + + + + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + QCustomPlot + QWidget +
qcustomplot.h
+ 1 +
+
+ + +
From 449901db6ff2b4cb20ca5fd782c03bdcb9d3a4ad Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Wed, 13 Apr 2016 13:31:48 +0200 Subject: [PATCH 4/5] [plotwidget] integrate imageplotter --- plotter/plotter.h | 3 +-- plotwidget.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plotter/plotter.h b/plotter/plotter.h index 021b97b..70bfe24 100644 --- a/plotter/plotter.h +++ b/plotter/plotter.h @@ -234,8 +234,7 @@ class Plotter { if (array.getDimension(2).dimensionType() == nix::DimensionType::Set) { return PlotterType::Line; } else { - // handle 2D image/heatmap plotting not supported, yet TODO - return PlotterType::Unsupported; + return PlotterType::Image; } } else { if (array.getDimension(2).dimensionType() == nix::DimensionType::Sample || diff --git a/plotwidget.cpp b/plotwidget.cpp index d5c3394..d4f23a6 100644 --- a/plotwidget.cpp +++ b/plotwidget.cpp @@ -2,6 +2,7 @@ #include "ui_plotwidget.h" #include "plotter/lineplotter.h" #include "plotter/categoryplotter.h" +#include "plotter/imageplotter.h" #include "common/Common.hpp" PlotWidget::PlotWidget(QWidget *parent) : @@ -73,6 +74,12 @@ Plotter* PlotWidget::process(const nix::DataArray &array) { ui->scrollAreaWidgetContents->layout()->addWidget(cp); cp->draw(array); return cp; + } else if (suggestion == PlotterType::Image) { + delete_widgets_from_layout(); + ImagePlotter *ip = new ImagePlotter(); + ui->scrollAreaWidgetContents->layout()->addWidget(ip); + ip->draw(array); + return ip; } return nullptr; From c2d0ae6a3e24d7fda0a6f59f2fe118389ee61376 Mon Sep 17 00:00:00 2001 From: Christian Kellner Date: Wed, 13 Apr 2016 13:55:21 +0200 Subject: [PATCH 5/5] [imageplotter] use SampleDimension::axis() Simplifies the code by use NIX-provided function. --- plotter/imageplotter.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/plotter/imageplotter.cpp b/plotter/imageplotter.cpp index ee9c2bc..d9e841e 100644 --- a/plotter/imageplotter.cpp +++ b/plotter/imageplotter.cpp @@ -38,15 +38,7 @@ static DimInfo get_dim_info(const nix::DataArray &array, nix::ndsize_t dim_index info.valid = true; info.label = dim.label().get_value_or("").c_str(); info.unit = dim.unit().get_value_or("").c_str(); - double si = dim.samplingInterval(); - //todo: safe cast? - info.ticks.resize(static_cast(shape[dim_index-1])); - - double offset = dim.offset().get_value_or(0); - const int size = info.ticks.size(); - for (int i = 0; i < size; i++) { - info.ticks[i] = offset + i * si; - } + info.ticks = QVector::fromStdVector(dim.axis(shape[dim_index-1])); } else if (d.dimensionType() == nix::DimensionType::Range) { nix::RangeDimension dim = d.asRangeDimension();