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

318 lines
6.1 KiB
C++
Raw Normal View History

2016-09-19 00:56:10 +02:00
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
#include "popup.h"
2016-08-19 19:48:44 +02:00
#include "graphitem.h"
2016-09-19 00:56:10 +02:00
GraphItem::GraphItem(const Graph &graph, GraphType type, int width,
const QColor &color, Qt::PenStyle style, QGraphicsItem *parent)
: GraphicsItem(parent), _graph(graph), _type(type), _secondaryGraph(0)
2016-09-19 00:56:10 +02:00
{
2019-02-11 23:28:08 +01:00
Q_ASSERT(_graph.isValid());
_units = Metric;
_pen = QPen(color, width, style, Qt::FlatCap);
_sx = 0; _sy = 0;
2019-02-11 23:28:08 +01:00
_time = _graph.hasTime();
setZValue(2.0);
setAcceptHoverEvents(true);
2016-12-06 01:48:26 +01:00
updateBounds();
}
void GraphItem::updateShape()
{
QPainterPathStroker s;
s.setWidth(_pen.width() + 1);
_shape = s.createStroke(_path);
2016-09-19 00:56:10 +02:00
}
void GraphItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->setPen(_pen);
painter->drawPath(_path);
/*
QPen p = QPen(QBrush(Qt::red), 0);
painter->setPen(p);
painter->drawRect(boundingRect());
*/
2016-09-19 00:56:10 +02:00
}
2016-09-25 18:08:39 +02:00
void GraphItem::setGraphType(GraphType type)
{
2017-10-04 23:15:39 +02:00
if (type == _type)
return;
2016-09-25 18:08:39 +02:00
prepareGeometryChange();
2016-09-25 18:08:39 +02:00
_type = type;
updatePath();
updateBounds();
2016-09-25 18:08:39 +02:00
}
2016-08-19 19:48:44 +02:00
void GraphItem::setColor(const QColor &color)
{
2017-10-04 23:15:39 +02:00
if (_pen.color() == color)
return;
_pen.setColor(color);
update();
2016-09-19 00:56:10 +02:00
}
2016-12-06 01:48:26 +01:00
void GraphItem::setWidth(int width)
{
if (width == _pen.width())
2017-10-04 23:15:39 +02:00
return;
2016-12-06 01:48:26 +01:00
prepareGeometryChange();
_pen.setWidth(width);
updateShape();
2016-12-06 01:48:26 +01:00
}
2019-02-11 23:28:08 +01:00
const GraphSegment *GraphItem::segment(qreal x, GraphType type) const
{
for (int i = 0; i < _graph.size(); i++)
if (x <= _graph.at(i).last().x(type))
return &(_graph.at(i));
return 0;
}
2016-09-19 00:56:10 +02:00
qreal GraphItem::yAtX(qreal x)
{
2019-02-11 23:28:08 +01:00
const GraphSegment *seg = segment(x, _type);
if (!seg)
return NAN;
int low = 0;
2019-02-11 23:28:08 +01:00
int high = seg->count() - 1;
int mid = 0;
2019-02-11 23:28:08 +01:00
if (!(x >= seg->at(low).x(_type) && x <= seg->at(high).x(_type)))
return NAN;
while (low <= high) {
mid = low + ((high - low) / 2);
2019-02-11 23:28:08 +01:00
const GraphPoint &p = seg->at(mid);
if (p.x(_type) > x)
high = mid - 1;
else if (p.x(_type) < x)
low = mid + 1;
else
return -p.y();
}
QLineF l;
2019-02-11 23:28:08 +01:00
if (seg->at(mid).x(_type) < x)
l = QLineF(seg->at(mid).x(_type), seg->at(mid).y(),
seg->at(mid+1).x(_type), seg->at(mid+1).y());
else
2019-02-11 23:28:08 +01:00
l = QLineF(seg->at(mid-1).x(_type), seg->at(mid-1).y(),
seg->at(mid).x(_type), seg->at(mid).y());
return -l.pointAt((x - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
2016-09-19 00:56:10 +02:00
}
qreal GraphItem::distanceAtTime(qreal time)
{
2019-02-11 23:28:08 +01:00
const GraphSegment *seg = segment(time, Time);
if (!seg)
return NAN;
2016-09-19 00:56:10 +02:00
int low = 0;
2019-02-11 23:28:08 +01:00
int high = seg->count() - 1;
2016-09-19 00:56:10 +02:00
int mid = 0;
2019-02-11 23:28:08 +01:00
if (!(time >= seg->at(low).t() && time <= seg->at(high).t()))
return NAN;
2016-09-19 00:56:10 +02:00
while (low <= high) {
mid = low + ((high - low) / 2);
2019-02-11 23:28:08 +01:00
const GraphPoint &p = seg->at(mid);
if (p.t() > time)
2016-09-19 00:56:10 +02:00
high = mid - 1;
else if (p.t() < time)
2016-09-19 00:56:10 +02:00
low = mid + 1;
else
2019-02-11 23:28:08 +01:00
return seg->at(mid).s();
2016-09-19 00:56:10 +02:00
}
2016-09-27 01:48:45 +02:00
QLineF l;
2019-02-11 23:28:08 +01:00
if (seg->at(mid).t() < time)
l = QLineF(seg->at(mid).t(), seg->at(mid).s(), seg->at(mid+1).t(),
seg->at(mid+1).s());
2016-09-19 00:56:10 +02:00
else
2019-02-11 23:28:08 +01:00
l = QLineF(seg->at(mid-1).t(), seg->at(mid-1).s(),
seg->at(mid).t(), seg->at(mid).s());
2016-09-27 01:48:45 +02:00
return l.pointAt((time - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
2016-09-19 00:56:10 +02:00
}
void GraphItem::emitSliderPositionChanged(qreal pos)
{
2019-02-11 23:28:08 +01:00
if (_type == Time)
emit sliderPositionChanged(_time ? distanceAtTime(pos) : NAN);
else
2016-09-19 00:56:10 +02:00
emit sliderPositionChanged(pos);
2016-08-19 19:48:44 +02:00
}
void GraphItem::hover(bool hover)
{
if (hover) {
_pen.setWidth(_pen.width() + 1);
setZValue(zValue() + 1.0);
} else {
_pen.setWidth(_pen.width() - 1);
setZValue(zValue() - 1.0);
}
update();
}
void GraphItem::setScale(qreal sx, qreal sy)
{
if (_sx == sx && _sy == sy)
return;
prepareGeometryChange();
_sx = sx; _sy = sy;
updatePath();
}
void GraphItem::updatePath()
{
if (_sx == 0 && _sy == 0)
return;
prepareGeometryChange();
_path = QPainterPath();
2019-02-11 23:28:08 +01:00
if (!(_type == Time && !_time)) {
for (int i = 0; i < _graph.size(); i++) {
const GraphSegment &segment = _graph.at(i);
_path.moveTo(segment.first().x(_type) * _sx, -segment.first().y()
* _sy);
for (int i = 1; i < segment.size(); i++)
_path.lineTo(segment.at(i).x(_type) * _sx, -segment.at(i).y()
* _sy);
}
2019-02-11 23:28:08 +01:00
}
updateShape();
}
void GraphItem::updateBounds()
{
2017-05-22 18:42:23 +02:00
if (_type == Time && !_time) {
_bounds = QRectF();
return;
}
qreal bottom, top, left, right;
2019-02-11 23:28:08 +01:00
QPointF p = QPointF(_graph.first().first().x(_type),
-_graph.first().first().y());
bottom = p.y(); top = p.y(); left = p.x(); right = p.x();
2019-02-11 23:28:08 +01:00
for (int i = 0; i < _graph.size(); i++) {
const GraphSegment &segment = _graph.at(i);
for (int j = 0; j < segment.size(); j++) {
p = QPointF(segment.at(j).x(_type), -segment.at(j).y());
bottom = qMax(bottom, p.y()); top = qMin(top, p.y());
right = qMax(right, p.x()); left = qMin(left, p.x());
}
}
2019-10-27 22:51:36 +01:00
if (left == right)
_bounds = QRectF();
else
_bounds = QRectF(QPointF(left, top), QPointF(right, bottom));
}
2019-02-11 23:28:08 +01:00
qreal GraphItem::max() const
{
qreal ret = _graph.first().first().y();
for (int i = 0; i < _graph.size(); i++) {
const GraphSegment &segment = _graph.at(i);
for (int j = 0; j < segment.size(); j++) {
qreal y = segment.at(j).y();
if (y > ret)
ret = y;
}
}
return ret;
}
qreal GraphItem::min() const
{
qreal ret = _graph.first().first().y();
for (int i = 0; i < _graph.size(); i++) {
const GraphSegment &segment = _graph.at(i);
for (int j = 0; j < segment.size(); j++) {
qreal y = segment.at(j).y();
if (y < ret)
ret = y;
}
}
return ret;
}
qreal GraphItem::avg() const
{
qreal sum = 0;
for (int i = 0; i < _graph.size(); i++) {
const GraphSegment &segment = _graph.at(i);
for (int j = 1; j < segment.size(); j++)
sum += segment.at(j).y() * (segment.at(j).s() - segment.at(j-1).s());
}
return sum/_graph.last().last().s();
}
void GraphItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
_pen.setWidth(_pen.width() + 1);
setZValue(zValue() + 1.0);
update();
emit selected(true);
}
void GraphItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
Q_UNUSED(event);
_pen.setWidth(_pen.width() - 1);
setZValue(zValue() - 1.0);
update();
emit selected(false);
}
void GraphItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
Popup::show(event->screenPos(), info(), event->widget());
GraphicsItem::mousePressEvent(event);
}