diff --git a/src/framebufferseries.cpp b/src/framebufferseries.cpp --- a/src/framebufferseries.cpp +++ b/src/framebufferseries.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2016 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -21,9 +21,19 @@ FrameBufferSeries::FrameBufferSeries(FrameBuffer* buffer) { + xAsIndex = true; + _xmin = 0; + _xmax = 1; _buffer = buffer; } +void FrameBufferSeries::setXAxis(bool asIndex, double xmin, double xmax) +{ + xAsIndex = asIndex; + _xmin = xmin; + _xmax = xmax; +} + size_t FrameBufferSeries::size() const { return _buffer->size(); @@ -31,10 +41,27 @@ size_t FrameBufferSeries::size() const QPointF FrameBufferSeries::sample(size_t i) const { - return QPointF(i, _buffer->sample(i)); + if (xAsIndex) + { + return QPointF(i, _buffer->sample(i)); + } + else + { + return QPointF(i * (_xmax - _xmin) / size() + _xmin, _buffer->sample(i)); + } } QRectF FrameBufferSeries::boundingRect() const { - return _buffer->boundingRect(); + if (xAsIndex) + { + return _buffer->boundingRect(); + } + else + { + auto rect = _buffer->boundingRect(); + rect.setLeft(_xmin); + rect.setRight(_xmax); + return rect; + } } diff --git a/src/framebufferseries.h b/src/framebufferseries.h --- a/src/framebufferseries.h +++ b/src/framebufferseries.h @@ -1,5 +1,5 @@ /* - Copyright © 2016 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -37,6 +37,9 @@ class FrameBufferSeries : public QwtSeri public: FrameBufferSeries(FrameBuffer* buffer); + /// Behavior of X axis + void setXAxis(bool asIndex, double xmin, double xmax); + // QwtSeriesData implementations size_t size() const; QPointF sample(size_t i) const; @@ -44,6 +47,9 @@ public: private: FrameBuffer* _buffer; + bool xAsIndex; + double _xmin; + double _xmax; }; #endif // FRAMEBUFFERSERIES_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -133,14 +133,18 @@ MainWindow::MainWindow(QWidget *parent) QObject::connect(&portControl, &PortControl::portToggled, this, &MainWindow::onPortToggled); + // plot control signals connect(&plotControlPanel, &PlotControlPanel::numOfSamplesChanged, this, &MainWindow::onNumOfSamplesChanged); connect(&plotControlPanel, &PlotControlPanel::numOfSamplesChanged, plotMan, &PlotManager::onNumOfSamplesChanged); - connect(&plotControlPanel, &PlotControlPanel::scaleChanged, - plotMan, &PlotManager::setAxis); + connect(&plotControlPanel, &PlotControlPanel::yScaleChanged, + plotMan, &PlotManager::setYAxis); + + connect(&plotControlPanel, &PlotControlPanel::xScaleChanged, + plotMan, &PlotManager::setXAxis); QObject::connect(ui->actionClear, SIGNAL(triggered(bool)), this, SLOT(clearPlot())); @@ -211,9 +215,12 @@ MainWindow::MainWindow(QWidget *parent) plotMan->addCurve(channelMan.channelName(i), channelMan.channelBuffer(i)); } - // init auto scale - plotMan->setAxis(plotControlPanel.autoScale(), - plotControlPanel.yMin(), plotControlPanel.yMax()); + // init scales + plotMan->setYAxis(plotControlPanel.autoScale(), + plotControlPanel.yMin(), plotControlPanel.yMax()); + plotMan->setXAxis(plotControlPanel.xAxisAsIndex(), + plotControlPanel.xMin(), plotControlPanel.xMax()); + plotMan->onNumOfSamplesChanged(numOfSamples); // Init sps (sample per second) counter spsLabel.setText("0sps"); diff --git a/src/plot.cpp b/src/plot.cpp --- a/src/plot.cpp +++ b/src/plot.cpp @@ -1,5 +1,5 @@ /* - Copyright © 2015 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -38,6 +38,7 @@ Plot::Plot(QWidget* parent) : { isAutoScaled = true; symbolSize = 0; + numOfSamples = 1; QObject::connect(&zoomer, &Zoomer::unzoomed, this, &Plot::unzoomed); @@ -78,7 +79,7 @@ Plot::~Plot() if (snapshotOverlay != NULL) delete snapshotOverlay; } -void Plot::setAxis(bool autoScaled, double yAxisMin, double yAxisMax) +void Plot::setYAxis(bool autoScaled, double yAxisMin, double yAxisMax) { this->isAutoScaled = autoScaled; @@ -92,8 +93,29 @@ void Plot::setAxis(bool autoScaled, doub resetAxes(); } +void Plot::setXAxis(double xMin, double xMax) +{ + _xMin = xMin; + _xMax = xMax; + + zoomer.zoom(0); // unzoom + + // set axis + setAxisScale(QwtPlot::xBottom, xMin, xMax); + replot(); // Note: if we don't replot here scale at startup isn't set correctly + + // reset zoom base + auto base = zoomer.zoomBase(); + base.setLeft(xMin); + base.setRight(xMax); + zoomer.setZoomBase(base); + + onXScaleChanged(); +} + void Plot::resetAxes() { + // reset y axis if (isAutoScaled) { setAxisAutoScale(QwtPlot::yLeft); @@ -103,12 +125,13 @@ void Plot::resetAxes() setAxisScale(QwtPlot::yLeft, yMin, yMax); } + zoomer.setZoomBase(); + replot(); } void Plot::unzoomed() { - setAxisAutoScale(QwtPlot::xBottom); resetAxes(); onXScaleChanged(); } @@ -229,7 +252,10 @@ void Plot::onXScaleChanged() auto sw = axisWidget(QwtPlot::xBottom); auto paintDist = sw->scaleDraw()->scaleMap().pDist(); auto scaleDist = sw->scaleDraw()->scaleMap().sDist(); - int symDisPx = round(paintDist / scaleDist); + auto fullScaleDist = zoomer.zoomBase().width(); + auto zoomRate = fullScaleDist / scaleDist; + float samplesInView = numOfSamples / zoomRate; + int symDisPx = round(paintDist / samplesInView); if (symDisPx < SYMBOL_SHOW_AT_WIDTH) { @@ -273,8 +299,6 @@ void Plot::resizeEvent(QResizeEvent * ev void Plot::onNumOfSamplesChanged(unsigned value) { - auto currentBase = zoomer.zoomBase(); - currentBase.setWidth(value); - zoomer.setZoomBase(currentBase); + numOfSamples = value; onXScaleChanged(); } diff --git a/src/plot.h b/src/plot.h --- a/src/plot.h +++ b/src/plot.h @@ -1,5 +1,5 @@ /* - Copyright © 2016 Hasan Yavuz Özderya + Copyright © 2017 Hasan Yavuz Özderya This file is part of serialplot. @@ -52,7 +52,8 @@ public slots: void showDemoIndicator(bool show = true); void unzoom(); void darkBackground(bool enabled = true); - void setAxis(bool autoScaled, double yMin = 0, double yMax = 1); + void setYAxis(bool autoScaled, double yMin = 0, double yMax = 1); + void setXAxis(double xMin, double xMax); /** * Displays an animation for snapshot. @@ -70,6 +71,8 @@ protected: private: bool isAutoScaled; double yMin, yMax; + double _xMin, _xMax; + unsigned numOfSamples; int symbolSize; Zoomer zoomer; ScaleZoomer sZoomer; diff --git a/src/plotcontrolpanel.cpp b/src/plotcontrolpanel.cpp --- a/src/plotcontrolpanel.cpp +++ b/src/plotcontrolpanel.cpp @@ -61,6 +61,12 @@ PlotControlPanel::PlotControlPanel(QWidg ui->spYmax->setRange((-1) * std::numeric_limits::max(), std::numeric_limits::max()); + ui->spXmin->setRange((-1) * std::numeric_limits::max(), + std::numeric_limits::max()); + + ui->spXmax->setRange((-1) * std::numeric_limits::max(), + std::numeric_limits::max()); + // connect signals connect(ui->spNumOfSamples, SIGNAL(valueChanged(int)), this, SLOT(onNumOfSamples(int))); @@ -74,6 +80,15 @@ PlotControlPanel::PlotControlPanel(QWidg connect(ui->spYmin, SIGNAL(valueChanged(double)), this, SLOT(onYScaleChanged())); + connect(ui->cbIndex, &QCheckBox::toggled, + this, &PlotControlPanel::onIndexChecked); + + connect(ui->spXmax, SIGNAL(valueChanged(double)), + this, SLOT(onXScaleChanged())); + + connect(ui->spXmin, SIGNAL(valueChanged(double)), + this, SLOT(onXScaleChanged())); + // init scale range preset list for (int nbits = 8; nbits <= 24; nbits++) // signed binary formats { @@ -181,7 +196,7 @@ void PlotControlPanel::onAutoScaleChecke ui->spYmin->setEnabled(false); ui->spYmax->setEnabled(false); - emit scaleChanged(true); // autoscale + emit yScaleChanged(true); // autoscale } else { @@ -190,30 +205,45 @@ void PlotControlPanel::onAutoScaleChecke ui->spYmin->setEnabled(true); ui->spYmax->setEnabled(true); - emit scaleChanged(false, ui->spYmin->value(), ui->spYmax->value()); + emit yScaleChanged(false, ui->spYmin->value(), ui->spYmax->value()); } } void PlotControlPanel::onYScaleChanged() { - emit scaleChanged(false, ui->spYmin->value(), ui->spYmax->value()); + emit yScaleChanged(false, ui->spYmin->value(), ui->spYmax->value()); } -bool PlotControlPanel::autoScale() +bool PlotControlPanel::autoScale() const { return ui->cbAutoScale->isChecked(); } -double PlotControlPanel::yMax() +double PlotControlPanel::yMax() const { return ui->spYmax->value(); } -double PlotControlPanel::yMin() +double PlotControlPanel::yMin() const { return ui->spYmin->value(); } +bool PlotControlPanel::xAxisAsIndex() const +{ + return ui->cbIndex->isChecked(); +} + +double PlotControlPanel::xMax() const +{ + return ui->spXmax->value(); +} + +double PlotControlPanel::xMin() const +{ + return ui->spXmin->value(); +} + void PlotControlPanel::onRangeSelected() { Range r = ui->cbRangePresets->currentData().value(); @@ -222,6 +252,33 @@ void PlotControlPanel::onRangeSelected() ui->cbAutoScale->setChecked(false); } +void PlotControlPanel::onIndexChecked(bool checked) +{ + if (checked) + { + ui->lXmin->setEnabled(false); + ui->lXmax->setEnabled(false); + ui->spXmin->setEnabled(false); + ui->spXmax->setEnabled(false); + + emit xScaleChanged(true); // use index + } + else + { + ui->lXmin->setEnabled(true); + ui->lXmax->setEnabled(true); + ui->spXmin->setEnabled(true); + ui->spXmax->setEnabled(true); + + emit xScaleChanged(false, ui->spXmin->value(), ui->spXmax->value()); + } +} + +void PlotControlPanel::onXScaleChanged() +{ + emit xScaleChanged(false, ui->spXmin->value(), ui->spXmax->value()); +} + void PlotControlPanel::setChannelInfoModel(ChannelInfoModel* model) { ui->tvChannelInfo->setModel(model); @@ -299,6 +356,9 @@ void PlotControlPanel::saveSettings(QSet { settings->beginGroup(SettingGroup_Plot); settings->setValue(SG_Plot_NumOfSamples, numOfSamples()); + settings->setValue(SG_Plot_IndexAsX, xAxisAsIndex()); + settings->setValue(SG_Plot_XMax, xMax()); + settings->setValue(SG_Plot_XMin, xMin()); settings->setValue(SG_Plot_AutoScale, autoScale()); settings->setValue(SG_Plot_YMax, yMax()); settings->setValue(SG_Plot_YMin, yMin()); @@ -310,6 +370,10 @@ void PlotControlPanel::loadSettings(QSet settings->beginGroup(SettingGroup_Plot); ui->spNumOfSamples->setValue( settings->value(SG_Plot_NumOfSamples, numOfSamples()).toInt()); + ui->cbIndex->setChecked( + settings->value(SG_Plot_IndexAsX, xAxisAsIndex()).toBool()); + ui->spXmax->setValue(settings->value(SG_Plot_XMax, xMax()).toDouble()); + ui->spXmin->setValue(settings->value(SG_Plot_XMin, xMin()).toDouble()); ui->cbAutoScale->setChecked( settings->value(SG_Plot_AutoScale, autoScale()).toBool()); ui->spYmax->setValue(settings->value(SG_Plot_YMax, yMax()).toDouble()); diff --git a/src/plotcontrolpanel.h b/src/plotcontrolpanel.h --- a/src/plotcontrolpanel.h +++ b/src/plotcontrolpanel.h @@ -40,9 +40,12 @@ public: ~PlotControlPanel(); unsigned numOfSamples(); - bool autoScale(); - double yMax(); - double yMin(); + bool autoScale() const; + double yMax() const; + double yMin() const; + bool xAxisAsIndex() const; + double xMax() const; + double xMin() const; void setChannelInfoModel(ChannelInfoModel* model); @@ -53,7 +56,8 @@ public: signals: void numOfSamplesChanged(int value); - void scaleChanged(bool autoScaled, double yMin = 0, double yMax = 1); + void yScaleChanged(bool autoScaled, double yMin = 0, double yMax = 1); + void xScaleChanged(bool asIndex, double xMin = 0, double xMax = 1); private: Ui::PlotControlPanel *ui; @@ -74,6 +78,8 @@ private slots: void onAutoScaleChecked(bool checked); void onYScaleChanged(); void onRangeSelected(); + void onIndexChecked(bool checked); + void onXScaleChanged(); }; #endif // PLOTCONTROLPANEL_H diff --git a/src/plotcontrolpanel.ui b/src/plotcontrolpanel.ui --- a/src/plotcontrolpanel.ui +++ b/src/plotcontrolpanel.ui @@ -6,7 +6,7 @@ 0 0 - 590 + 706 187 @@ -157,6 +157,87 @@ + + + Index as X AXis + + + true + + + + + + + + + false + + + Xmin + + + + + + + false + + + + 75 + 0 + + + + + 75 + 16777215 + + + + lower limit of Y axis + + + 0.000000000000000 + + + + + + + false + + + Xmax + + + + + + + false + + + + 75 + 16777215 + + + + upper limit of Y axis + + + 1000.000000000000000 + + + 1000.000000000000000 + + + + + + Auto Scale Y Axis @@ -166,75 +247,85 @@ - - - - false - - - Ymax - - + + + + + + false + + + Ymin + + + + + + + false + + + + 75 + 0 + + + + + 75 + 16777215 + + + + lower limit of Y axis + + + 0.000000000000000 + + + + + + + false + + + Ymax + + + + + + + false + + + + 75 + 16777215 + + + + upper limit of Y axis + + + 1000.000000000000000 + + + 1000.000000000000000 + + + + - - - - false - - - - 75 - 16777215 - - - - upper limit of Y axis - - - 1000.000000000000000 - - - 1000.000000000000000 - - - - - - - false - - - Ymin - - - - - - - false - - - - 75 - 16777215 - - - - lower limit of Y axis - - - 0.000000000000000 - - - - + Select Range Preset: - + diff --git a/src/plotmanager.cpp b/src/plotmanager.cpp --- a/src/plotmanager.cpp +++ b/src/plotmanager.cpp @@ -17,7 +17,6 @@ along with serialplot. If not, see . */ -#include #include "qwt_symbol.h" #include "plot.h" @@ -40,6 +39,7 @@ PlotManager::PlotManager(QWidget* plotAr _yMax = 1; isDemoShown = false; _infoModel = infoModel; + _numOfSamples = 1; // initalize layout and single widget isMulti = false; @@ -251,7 +251,16 @@ Plot* PlotManager::addPlotWidget() plot->showMinorGrid(showMinorGridAction.isChecked()); plot->showLegend(showLegendAction.isChecked()); plot->showDemoIndicator(isDemoShown); - plot->setAxis(_autoScaled, _yMin, _yMax); + plot->setYAxis(_autoScaled, _yMin, _yMax); + + if (_xAxisAsIndex) + { + plot->setXAxis(0, _numOfSamples); + } + else + { + plot->setXAxis(_xMin, _xMax); + } return plot; } @@ -259,7 +268,9 @@ Plot* PlotManager::addPlotWidget() void PlotManager::addCurve(QString title, FrameBuffer* buffer) { auto curve = new QwtPlotCurve(title); - curve->setSamples(new FrameBufferSeries(buffer)); + auto series = new FrameBufferSeries(buffer); + series->setXAxis(_xAxisAsIndex, _xMin, _xMax); + curve->setSamples(series); _addCurve(curve); } @@ -404,17 +415,42 @@ void PlotManager::darkBackground(bool en } } -void PlotManager::setAxis(bool autoScaled, double yAxisMin, double yAxisMax) +void PlotManager::setYAxis(bool autoScaled, double yAxisMin, double yAxisMax) { _autoScaled = autoScaled; _yMin = yAxisMin; _yMax = yAxisMax; for (auto plot : plotWidgets) { - plot->setAxis(autoScaled, yAxisMin, yAxisMax); + plot->setYAxis(autoScaled, yAxisMin, yAxisMax); } } +void PlotManager::setXAxis(bool asIndex, double xMin, double xMax) +{ + _xAxisAsIndex = asIndex; + _xMin = xMin; + _xMax = xMax; + for (auto curve : curves) + { + // TODO: what happens when addCurve(QVector) is used? + FrameBufferSeries* series = static_cast(curve->data()); + series->setXAxis(asIndex, xMin, xMax); + } + for (auto plot : plotWidgets) + { + if (asIndex) + { + plot->setXAxis(0, _numOfSamples); + } + else + { + plot->setXAxis(xMin, xMax); + } + } + replot(); +} + void PlotManager::flashSnapshotOverlay() { for (auto plot : plotWidgets) @@ -425,9 +461,11 @@ void PlotManager::flashSnapshotOverlay() void PlotManager::onNumOfSamplesChanged(unsigned value) { + _numOfSamples = value; for (auto plot : plotWidgets) { plot->onNumOfSamplesChanged(value); + if (_xAxisAsIndex) plot->setXAxis(0, value); } } diff --git a/src/plotmanager.h b/src/plotmanager.h --- a/src/plotmanager.h +++ b/src/plotmanager.h @@ -65,7 +65,9 @@ public slots: /// Enable display of a "DEMO" label on each plot void showDemoIndicator(bool show = true); /// Set the Y axis - void setAxis(bool autoScaled, double yMin = 0, double yMax = 1); + void setYAxis(bool autoScaled, double yMin = 0, double yMax = 1); + /// Set the X axis + void setXAxis(bool asIndex, double xMin = 0 , double xMax = 1); /// Display an animation for snapshot void flashSnapshotOverlay(); /// Should be called to update zoom base @@ -83,6 +85,10 @@ private: bool _autoScaled; double _yMin; double _yMax; + bool _xAxisAsIndex; + double _xMin; + double _xMax; + unsigned _numOfSamples; // menu actions QAction showGridAction; diff --git a/src/setting_defines.h b/src/setting_defines.h --- a/src/setting_defines.h +++ b/src/setting_defines.h @@ -76,6 +76,9 @@ const char SG_Channels_Visible[] = "visi // plot settings keys const char SG_Plot_NumOfSamples[] = "numOfSamples"; +const char SG_Plot_IndexAsX[] = "indexAsX"; +const char SG_Plot_XMax[] = "xMax"; +const char SG_Plot_XMin[] = "xMin"; const char SG_Plot_AutoScale[] = "autoScale"; const char SG_Plot_YMax[] = "yMax"; const char SG_Plot_YMin[] = "yMin";