/*
Copyright © 2017 Hasan Yavuz Özderya
This file is part of serialplot.
serialplot 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 3 of the License, or
(at your option) any later version.
serialplot 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 serialplot. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include "plot.h"
#include "utils.h"
static const int SYMBOL_SHOW_AT_WIDTH = 5;
static const int SYMBOL_SIZE_MAX = 7;
Plot::Plot(QWidget* parent) :
QwtPlot(parent),
zoomer(this->canvas(), false),
sZoomer(this, &zoomer)
{
isAutoScaled = true;
symbolSize = 0;
numOfSamples = 1;
plotWidth = 1;
showSymbols = Plot::ShowSymbolsAuto;
QObject::connect(&zoomer, &Zoomer::unzoomed, this, &Plot::unzoomed);
zoomer.setZoomBase();
grid.attach(this);
legend.attach(this);
showGrid(false);
darkBackground(false);
snapshotOverlay = NULL;
connect(&zoomer, &QwtPlotZoomer::zoomed,
[this](const QRectF &rect)
{
onXScaleChanged();
});
connect(this, &QwtPlot::itemAttached,
[this](QwtPlotItem *plotItem, bool on)
{
if (symbolSize) updateSymbols();
});
// init demo indicator
QwtText demoText(" DEMO RUNNING "); // looks better with spaces
demoText.setColor(QColor("white"));
demoText.setBackgroundBrush(Qt::darkRed);
demoText.setBorderRadius(4);
demoText.setRenderFlags(Qt::AlignLeft | Qt::AlignBottom);
demoIndicator.setText(demoText);
demoIndicator.hide();
demoIndicator.attach(this);
// init no channels are visible indicator
QwtText noChannelText(" No Visible Channels ");
noChannelText.setColor(QColor("white"));
noChannelText.setBackgroundBrush(Qt::darkBlue);
noChannelText.setBorderRadius(4);
noChannelText.setRenderFlags(Qt::AlignHCenter | Qt::AlignVCenter);
noChannelIndicator.setText(noChannelText);
noChannelIndicator.hide();
noChannelIndicator.attach(this);
}
Plot::~Plot()
{
if (snapshotOverlay != NULL) delete snapshotOverlay;
}
void Plot::setYAxis(bool autoScaled, double yAxisMin, double yAxisMax)
{
this->isAutoScaled = autoScaled;
if (!autoScaled)
{
yMin = yAxisMin;
yMax = yAxisMax;
}
zoomer.zoom(0);
resetAxes();
}
void Plot::setXAxis(double xMin, double xMax)
{
_xMin = xMin;
_xMax = xMax;
zoomer.setXLimits(xMin, 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);
}
else
{
setAxisScale(QwtPlot::yLeft, yMin, yMax);
}
zoomer.setZoomBase();
replot();
}
void Plot::unzoomed()
{
resetAxes();
onXScaleChanged();
}
void Plot::showGrid(bool show)
{
grid.enableX(show);
grid.enableY(show);
replot();
}
void Plot::showMinorGrid(bool show)
{
grid.enableXMin(show);
grid.enableYMin(show);
replot();
}
void Plot::showLegend(bool show)
{
legend.setVisible(show);
replot();
}
void Plot::showDemoIndicator(bool show)
{
demoIndicator.setVisible(show);
replot();
}
void Plot::showNoChannel(bool show)
{
noChannelIndicator.setVisible(show);
replot();
}
void Plot::unzoom()
{
zoomer.zoom(0);
}
void Plot::darkBackground(bool enabled)
{
QColor gridColor;
if (enabled)
{
setCanvasBackground(QBrush(Qt::black));
gridColor.setHsvF(0, 0, 0.25);
grid.setPen(gridColor);
zoomer.setRubberBandPen(QPen(Qt::white));
zoomer.setTrackerPen(QPen(Qt::white));
sZoomer.setPickerPen(QPen(Qt::white));
legend.setTextPen(QPen(Qt::white));
}
else
{
setCanvasBackground(QBrush(Qt::white));
gridColor.setHsvF(0, 0, 0.80);
grid.setPen(gridColor);
zoomer.setRubberBandPen(QPen(Qt::black));
zoomer.setTrackerPen(QPen(Qt::black));
sZoomer.setPickerPen(QPen(Qt::black));
legend.setTextPen(QPen(Qt::black));
}
updateSymbols();
replot();
}
void Plot::flashSnapshotOverlay(bool light)
{
if (snapshotOverlay != NULL) delete snapshotOverlay;
QColor color;
if(light)
{
color = QColor(Qt::white);
}
else
{
color = QColor(Qt::black);
}
snapshotOverlay = new PlotSnapshotOverlay(this->canvas(), color);
connect(snapshotOverlay, &PlotSnapshotOverlay::done,
[this]()
{
delete snapshotOverlay;
snapshotOverlay = NULL;
});
}
void Plot::setSymbols(ShowSymbols shown)
{
showSymbols = shown;
if (showSymbols == Plot::ShowSymbolsAuto)
{
calcSymbolSize();
}
else if (showSymbols == Plot::ShowSymbolsShow)
{
symbolSize = SYMBOL_SIZE_MAX;
}
else
{
symbolSize = 0;
}
updateSymbols();
replot();
}
void Plot::onXScaleChanged()
{
if (showSymbols == Plot::ShowSymbolsAuto)
{
calcSymbolSize();
updateSymbols();
}
}
void Plot::calcSymbolSize()
{
auto sw = axisWidget(QwtPlot::xBottom);
auto paintDist = sw->scaleDraw()->scaleMap().pDist();
auto scaleDist = sw->scaleDraw()->scaleMap().sDist();
auto fullScaleDist = zoomer.zoomBase().width();
auto zoomRate = fullScaleDist / scaleDist;
float plotWidthNumSamp = abs(numOfSamples * plotWidth / (_xMax - _xMin));
float samplesInView = plotWidthNumSamp / zoomRate;
int symDisPx = round(paintDist / samplesInView);
if (symDisPx < SYMBOL_SHOW_AT_WIDTH)
{
symbolSize = 0;
}
else
{
symbolSize = std::min(SYMBOL_SIZE_MAX, symDisPx-SYMBOL_SHOW_AT_WIDTH+1);
}
}
void Plot::updateSymbols()
{
const QwtPlotItemList curves = itemList( QwtPlotItem::Rtti_PlotCurve );
if (curves.size() > 0)
{
for (int i = 0; i < curves.size(); i++)
{
QwtSymbol* symbol = NULL;
QwtPlotCurve* curve = static_cast(curves[i]);
if (symbolSize)
{
symbol = new QwtSymbol(QwtSymbol::Ellipse,
canvasBackground(),
curve->pen(),
QSize(symbolSize, symbolSize));
}
curve->setSymbol(symbol);
}
}
}
void Plot::resizeEvent(QResizeEvent * event)
{
QwtPlot::resizeEvent(event);
onXScaleChanged();
}
void Plot::setNumOfSamples(unsigned value)
{
numOfSamples = value;
onXScaleChanged();
}
void Plot::setPlotWidth(double width)
{
plotWidth = width;
zoomer.setHViewSize(width);
}