1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-10-06 23:03:22 +02:00
GPXSee/src/graphview.cpp

377 lines
8.2 KiB
C++
Raw Normal View History

2015-10-05 01:43:48 +02:00
#include <QGraphicsView>
2015-10-13 23:49:15 +02:00
#include <QGraphicsSceneMouseEvent>
2015-10-05 01:43:48 +02:00
#include <QEvent>
#include "config.h"
2016-02-12 10:09:17 +01:00
#include "axisitem.h"
2015-10-12 01:12:12 +02:00
#include "slideritem.h"
2015-10-17 01:33:02 +02:00
#include "sliderinfoitem.h"
2015-10-12 01:12:12 +02:00
#include "infoitem.h"
#include "graphview.h"
2015-10-05 01:43:48 +02:00
2015-10-17 01:33:02 +02:00
2015-10-05 01:43:48 +02:00
#define MARGIN 10.0
2015-10-13 23:49:15 +02:00
void Scene::mousePressEvent(QGraphicsSceneMouseEvent *e)
{
if (e->button() == Qt::LeftButton)
emit mouseClicked(e->scenePos());
QGraphicsScene::mousePressEvent(e);
}
GraphView::GraphView(QWidget *parent)
2015-10-05 01:43:48 +02:00
: QGraphicsView(parent)
{
2015-10-13 23:49:15 +02:00
_scene = new Scene(this);
2015-10-05 01:43:48 +02:00
setScene(_scene);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
2015-10-05 01:43:48 +02:00
_xAxis = new AxisItem(AxisItem::X);
_yAxis = new AxisItem(AxisItem::Y);
_slider = new SliderItem();
2015-10-12 01:12:12 +02:00
_slider->setZValue(2.0);
2015-10-13 23:49:15 +02:00
2015-10-05 01:43:48 +02:00
connect(_slider, SIGNAL(positionChanged(const QPointF&)), this,
SLOT(emitSliderPositionChanged(const QPointF&)));
2015-10-13 23:49:15 +02:00
connect(_scene, SIGNAL(mouseClicked(const QPointF&)), this,
SLOT(newSliderPosition(const QPointF&)));
2015-10-05 01:43:48 +02:00
2015-10-12 01:12:12 +02:00
_info = new InfoItem();
2015-10-17 01:33:02 +02:00
_sliderInfo = new SliderInfoItem(_slider);
_sliderInfo->setZValue(2.0);
2015-10-05 01:43:48 +02:00
_xScale = 1;
_yScale = 1;
2015-10-17 01:33:02 +02:00
_precision = 0;
2016-03-27 13:23:00 +02:00
_minYRange = 0.01;
2016-03-30 20:50:51 +02:00
_sliderPos = 0;
2015-10-05 01:43:48 +02:00
}
GraphView::~GraphView()
2015-10-05 01:43:48 +02:00
{
if (_xAxis->scene() != _scene)
delete _xAxis;
if (_yAxis->scene() != _scene)
delete _yAxis;
if (_slider->scene() != _scene)
delete _slider;
2015-10-12 01:12:12 +02:00
if (_info->scene() != _scene)
delete _info;
2015-10-05 01:43:48 +02:00
}
void GraphView::updateBounds(const QPointF &point)
2015-10-05 01:43:48 +02:00
{
2016-03-27 13:23:00 +02:00
if (point.x() < _bounds.left())
_bounds.setLeft(point.x());
if (point.x() > _bounds.right())
_bounds.setRight(point.x());
if (point.y() > _bounds.bottom())
_bounds.setBottom(point.y());
if (point.y() < _bounds.top())
_bounds.setTop(point.y());
2015-10-05 01:43:48 +02:00
}
void GraphView::createXLabel()
2015-10-12 01:12:12 +02:00
{
_xAxis->setLabel(QString("%1 [%2]").arg(_xLabel).arg(_xUnits));
}
void GraphView::createYLabel()
2015-10-12 01:12:12 +02:00
{
_yAxis->setLabel(QString("%1 [%2]").arg(_yLabel).arg(_yUnits));
}
void GraphView::setXLabel(const QString &label)
2015-10-12 01:12:12 +02:00
{
_xLabel = label;
createXLabel();
}
void GraphView::setYLabel(const QString &label)
2015-10-12 01:12:12 +02:00
{
_yLabel = label;
createYLabel();
}
void GraphView::setXUnits(const QString &units)
2015-10-12 01:12:12 +02:00
{
_xUnits = units;
createXLabel();
}
void GraphView::setYUnits(const QString &units)
2015-10-12 01:12:12 +02:00
{
_yUnits = units;
createYLabel();
}
void GraphView::setXScale(qreal scale)
2015-12-19 20:23:07 +01:00
{
_xScale = scale;
}
void GraphView::setYScale(qreal scale)
2015-12-19 20:23:07 +01:00
{
_yScale = scale;
}
void GraphView::loadData(const QVector<QPointF> &data)
2015-10-05 01:43:48 +02:00
{
QPainterPath path;
QGraphicsPathItem *pi;
if (data.size() < 2)
return;
2016-03-27 13:23:00 +02:00
if (!_graphs.size())
_bounds.moveTo(data.at(0));
2015-10-05 01:43:48 +02:00
updateBounds(data.at(0));
path.moveTo(data.at(0).x(), -data.at(0).y());
for (int i = 1; i < data.size(); i++) {
2016-03-19 09:06:43 +01:00
const QPointF &p = data.at(i);
path.lineTo(p.x(), -p.y());
updateBounds(p);
2015-10-05 01:43:48 +02:00
}
pi = new QGraphicsPathItem(path);
2016-03-03 19:45:04 +01:00
QBrush brush(_palette.color(), Qt::SolidPattern);
2015-10-22 10:27:27 +02:00
QPen pen(brush, 0);
pi->setPen(pen);
2015-10-05 01:43:48 +02:00
_scene->addItem(pi);
_graphs.append(pi);
2015-12-19 20:23:07 +01:00
}
void GraphView::redraw()
2015-12-19 20:23:07 +01:00
{
if (!_graphs.isEmpty())
redraw(viewport()->size() - QSizeF(MARGIN, MARGIN));
2015-10-05 01:43:48 +02:00
}
void GraphView::redraw(const QSizeF &size)
2015-10-05 01:43:48 +02:00
{
QRectF r;
QSizeF mx, my;
2016-03-27 13:23:00 +02:00
RangeF rx, ry;
2015-10-05 01:43:48 +02:00
QTransform transform;
2016-03-27 13:23:00 +02:00
qreal xs, ys;
2015-10-05 01:43:48 +02:00
if (_xAxis->scene() == _scene)
_scene->removeItem(_xAxis);
if (_yAxis->scene() == _scene)
_scene->removeItem(_yAxis);
if (_slider->scene() == _scene)
_scene->removeItem(_slider);
2015-10-12 01:12:12 +02:00
if (_info->scene() == _scene)
_scene->removeItem(_info);
2015-10-05 01:43:48 +02:00
for (int i = 0; i < _graphs.size(); i++)
_graphs.at(i)->resetTransform();
2016-03-27 13:23:00 +02:00
rx = RangeF(_bounds.left() * _xScale, _bounds.right() * _xScale);
ry = RangeF(_bounds.top() * _yScale, _bounds.bottom() * _yScale);
if (ry.size() < _minYRange)
ry.resize(_minYRange);
_xAxis->setRange(rx);
_yAxis->setRange(ry);
2015-10-05 01:43:48 +02:00
mx = _xAxis->margin();
my = _yAxis->margin();
2015-10-05 01:43:48 +02:00
r = _scene->itemsBoundingRect();
2016-03-27 13:23:00 +02:00
if (r.height() < _minYRange)
r.adjust(0, -(_minYRange/2 - r.height()/2), 0,
_minYRange/2 - r.height()/2);
2015-10-05 01:43:48 +02:00
xs = (size.width() - (my.width() + mx.width())) / r.width();
2015-10-12 01:12:12 +02:00
ys = (size.height() - (mx.height() + my.height())
- _info->boundingRect().height()) / r.height();
2015-10-05 01:43:48 +02:00
transform.scale(xs, ys);
for (int i = 0; i < _graphs.size(); i++)
_graphs.at(i)->setTransform(transform);
r = _scene->itemsBoundingRect();
2016-03-27 13:23:00 +02:00
if (r.height() < _minYRange * ys)
r.adjust(0, -(_minYRange/2 * ys - r.height()/2), 0,
(_minYRange/2) * ys - r.height()/2);
2015-10-05 01:43:48 +02:00
_xAxis->setSize(r.width());
_yAxis->setSize(r.height());
_xAxis->setPos(r.bottomLeft());
_yAxis->setPos(r.bottomLeft());
_scene->addItem(_xAxis);
_scene->addItem(_yAxis);
_slider->setArea(r);
2016-03-30 20:50:51 +02:00
_slider->setPos((_sliderPos / _bounds.width()) * _slider->area().width(),
r.bottom());
2015-10-05 01:43:48 +02:00
_scene->addItem(_slider);
2016-03-30 20:50:51 +02:00
updateSliderInfo();
2015-12-19 20:49:22 +01:00
2015-10-12 01:12:12 +02:00
r = _scene->itemsBoundingRect();
_info->setPos(r.topLeft() + QPointF(r.width()/2
- _info->boundingRect().width()/2, -_info->boundingRect().height()));
_scene->addItem(_info);
2015-10-05 01:43:48 +02:00
_scene->setSceneRect(_scene->itemsBoundingRect());
}
void GraphView::resizeEvent(QResizeEvent *)
2015-10-05 01:43:48 +02:00
{
if (!_graphs.empty())
2015-12-19 20:23:07 +01:00
redraw();
2015-10-05 01:43:48 +02:00
}
void GraphView::plot(QPainter *painter, const QRectF &target)
2015-10-05 01:43:48 +02:00
{
qreal ratio = target.width() / target.height();
QSizeF orig = _scene->sceneRect().size();
QSizeF canvas = QSizeF(orig.height() * ratio, orig.height());
2016-05-20 22:44:03 +02:00
setUpdatesEnabled(false);
redraw(canvas);
if (_slider->pos().x() == _slider->area().left())
_slider->hide();
2016-05-20 22:44:03 +02:00
_scene->render(painter, target, _scene->itemsBoundingRect());
_slider->show();
2016-05-20 23:47:37 +02:00
redraw();
2016-05-20 22:44:03 +02:00
setUpdatesEnabled(true);
2015-10-05 01:43:48 +02:00
}
void GraphView::clear()
2015-10-05 01:43:48 +02:00
{
if (_xAxis->scene() == _scene)
_scene->removeItem(_xAxis);
if (_yAxis->scene() == _scene)
_scene->removeItem(_yAxis);
if (_slider->scene() == _scene)
_scene->removeItem(_slider);
2015-10-12 01:12:12 +02:00
if (_info->scene() == _scene)
_scene->removeItem(_info);
2015-10-17 01:33:02 +02:00
2015-12-19 20:23:07 +01:00
_slider->clear();
2015-10-13 00:27:49 +02:00
_info->clear();
2015-10-05 01:43:48 +02:00
_scene->clear();
_graphs.clear();
2016-03-02 09:34:39 +01:00
_palette.reset();
2015-10-05 01:43:48 +02:00
2016-03-27 13:23:00 +02:00
_bounds = QRectF();
2016-03-30 20:50:51 +02:00
_sliderPos = 0;
2015-10-05 01:43:48 +02:00
_scene->setSceneRect(0, 0, 0, 0);
}
static qreal yAtX(const QPainterPath &path, qreal x)
{
int low = 0;
int high = path.elementCount() - 1;
int mid = 0;
2016-04-01 19:20:55 +02:00
Q_ASSERT(high > low);
Q_ASSERT(x >= path.elementAt(low).x && x <= path.elementAt(high).x);
while (low <= high) {
mid = low + ((high - low) / 2);
const QPainterPath::Element &e = path.elementAt(mid);
if (e.x > x)
high = mid - 1;
else if (e.x < x)
low = mid + 1;
else
return e.y;
}
QLineF l;
2016-04-01 19:20:55 +02:00
if (path.elementAt(mid).x < x)
l = QLineF(path.elementAt(mid).x, path.elementAt(mid).y,
path.elementAt(mid+1).x, path.elementAt(mid+1).y);
2016-04-01 19:20:55 +02:00
else
l = QLineF(path.elementAt(mid-1).x, path.elementAt(mid-1).y,
path.elementAt(mid).x, path.elementAt(mid).y);
return l.pointAt((x - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
}
2016-03-30 20:50:51 +02:00
void GraphView::updateSliderInfo()
2015-10-05 01:43:48 +02:00
{
2016-03-30 20:50:51 +02:00
_sliderInfo->setVisible(_graphs.size() == 1);
2015-10-17 01:33:02 +02:00
2016-04-01 19:20:55 +02:00
if (_graphs.size() != 1)
return;
2015-10-17 01:33:02 +02:00
const QPainterPath &path = _graphs.at(0)->path();
QRectF br = path.boundingRect();
2016-03-27 13:23:00 +02:00
if (br.height() < _minYRange)
br.adjust(0, -(_minYRange/2 - br.height()/2), 0,
_minYRange/2 - br.height()/2);
2016-03-30 20:50:51 +02:00
qreal y = yAtX(path, _sliderPos);
qreal r = (y - br.bottom()) / br.height();
2016-03-30 20:50:51 +02:00
qreal pos = (_sliderPos / _bounds.width()) * _slider->area().width();
SliderInfoItem::Side s = (pos + _sliderInfo->boundingRect().width()
> _slider->area().right()) ? SliderInfoItem::Left : SliderInfoItem::Right;
_sliderInfo->setSide(s);
2015-10-17 01:33:02 +02:00
_sliderInfo->setPos(QPointF(0, _slider->boundingRect().height() * r));
_sliderInfo->setText(QString::number(-y * _yScale, 'f', _precision));
2015-10-05 01:43:48 +02:00
}
2016-03-30 20:50:51 +02:00
void GraphView::emitSliderPositionChanged(const QPointF &pos)
2015-10-05 01:43:48 +02:00
{
2016-03-30 20:50:51 +02:00
if (_graphs.isEmpty())
return;
_sliderPos = (pos.x() / _slider->area().width()) * _bounds.width();
emit sliderPositionChanged(_sliderPos);
updateSliderInfo();
2015-10-05 01:43:48 +02:00
}
void GraphView::setSliderPosition(qreal pos)
2015-10-05 01:43:48 +02:00
{
2016-03-30 20:50:51 +02:00
_sliderPos = pos;
if (_graphs.isEmpty())
return;
2016-03-27 13:23:00 +02:00
if (pos > _bounds.right() || pos < _bounds.left())
_slider->setVisible(false);
else {
2016-03-27 13:23:00 +02:00
_slider->setPos((pos / _bounds.width()) * _slider->area().width(), 0);
_slider->setVisible(true);
}
2015-10-05 01:43:48 +02:00
}
2015-10-12 01:12:12 +02:00
void GraphView::newSliderPosition(const QPointF &pos)
2015-10-13 23:49:15 +02:00
{
if (_slider->area().contains(pos)) {
_slider->setPos(pos);
_slider->setVisible(true);
2015-10-13 23:49:15 +02:00
emitSliderPositionChanged(pos);
}
}
void GraphView::addInfo(const QString &key, const QString &value)
2015-10-12 01:12:12 +02:00
{
_info->insert(key, value);
}
2015-12-19 20:23:07 +01:00
void GraphView::clearInfo()
2015-12-19 20:23:07 +01:00
{
_info->clear();
}