1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-30 22:51:16 +01:00

Fixed graph line distortion in PDF output

This commit is contained in:
Martin Tůma 2016-10-12 20:38:18 +02:00
parent cb52ad8bc5
commit be3c101c07
4 changed files with 124 additions and 79 deletions

View File

@ -15,6 +15,7 @@ public:
qreal s() const {return _s;}
qreal t() const {return _t;}
qreal y() const {return _y;}
qreal x(GraphType type) const {return (type == Distance) ? _s : _t;}
void setS(qreal s) {_s = s;}
void setT(qreal t) {_t = t;}

View File

@ -5,37 +5,6 @@
#define GRAPH_WIDTH 1
#define HOVER_WIDTH 2
static qreal yAtX(const QPainterPath &path, qreal x)
{
int low = 0;
int high = path.elementCount() - 1;
int mid = 0;
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;
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);
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();
}
static bool hasTime(const Graph &graph)
{
for (int i = 0; i < graph.count(); i++)
@ -49,20 +18,16 @@ GraphItem::GraphItem(const Graph &graph, QGraphicsItem *parent)
: QGraphicsObject(parent)
{
_id = 0;
_type = Distance;
_pen = QPen(Qt::black, GRAPH_WIDTH);
_pen.setCosmetic(true);
_distancePath.moveTo(graph.first().s(), -graph.first().y());
for (int i = 1; i < graph.size(); i++)
_distancePath.lineTo(graph.at(i).s(), -graph.at(i).y());
_type = Distance;
_graph = graph;
_sx = 1.0; _sy = 1.0;
_time = hasTime(_graph);
if (hasTime(graph)) {
_timePath.moveTo(graph.first().t(), -graph.first().y());
for (int i = 1; i < graph.size(); i++)
_timePath.lineTo(graph.at(i).t(), -graph.at(i).y());
}
updatePath();
updateBounds();
}
void GraphItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
@ -72,7 +37,7 @@ void GraphItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
Q_UNUSED(widget);
painter->setPen(_pen);
painter->drawPath((_type == Distance) ? _distancePath : _timePath);
painter->drawPath(_path);
/*
QPen p = QPen(QBrush(Qt::red), 0);
@ -84,7 +49,10 @@ void GraphItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
void GraphItem::setGraphType(GraphType type)
{
prepareGeometryChange();
_type = type;
updatePath();
updateBounds();
}
void GraphItem::setColor(const QColor &color)
@ -95,37 +63,62 @@ void GraphItem::setColor(const QColor &color)
qreal GraphItem::yAtX(qreal x)
{
return ::yAtX((_type == Distance) ? _distancePath : _timePath, x);
int low = 0;
int high = _graph.count() - 1;
int mid = 0;
Q_ASSERT(high > low);
Q_ASSERT(x >= _graph.at(low).x(_type) && x <= _graph.at(high).x(_type));
while (low <= high) {
mid = low + ((high - low) / 2);
const GraphPoint &p = _graph.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;
if (_graph.at(mid).x(_type) < x)
l = QLineF(_graph.at(mid).x(_type), _graph.at(mid).y(),
_graph.at(mid+1).x(_type), _graph.at(mid+1).y());
else
l = QLineF(_graph.at(mid-1).x(_type), _graph.at(mid-1).y(),
_graph.at(mid).x(_type), _graph.at(mid).y());
return -l.pointAt((x - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
}
qreal GraphItem::distanceAtTime(qreal time)
{
int low = 0;
int high = _timePath.elementCount() - 1;
int high = _graph.count() - 1;
int mid = 0;
Q_ASSERT(high > low);
Q_ASSERT(time >= _timePath.elementAt(low).x
&& time <= _timePath.elementAt(high).x);
Q_ASSERT(time >= _graph.at(low).t() && time <= _graph.at(high).t());
while (low <= high) {
mid = low + ((high - low) / 2);
const QPainterPath::Element &e = _timePath.elementAt(mid);
if (e.x > time)
const GraphPoint &p = _graph.at(mid);
if (p.t() > time)
high = mid - 1;
else if (e.x < time)
else if (p.t() < time)
low = mid + 1;
else
return _distancePath.elementAt(mid).x;
return _graph.at(mid).s();
}
QLineF l;
if (_timePath.elementAt(mid).x < time)
l = QLineF(_timePath.elementAt(mid).x, _distancePath.elementAt(mid).x,
_timePath.elementAt(mid+1).x, _distancePath.elementAt(mid+1).x);
if (_graph.at(mid).t() < time)
l = QLineF(_graph.at(mid).t(), _graph.at(mid).s(), _graph.at(mid+1).t(),
_graph.at(mid+1).s());
else
l = QLineF(_timePath.elementAt(mid-1).x, _distancePath.elementAt(mid-1).x,
_timePath.elementAt(mid).x, _distancePath.elementAt(mid).x);
l = QLineF(_graph.at(mid-1).t(), _graph.at(mid-1).s(),
_graph.at(mid).t(), _graph.at(mid).s());
return l.pointAt((time - l.p1().x()) / (l.p2().x() - l.p1().x())).y();
}
@ -133,15 +126,13 @@ qreal GraphItem::distanceAtTime(qreal time)
void GraphItem::emitSliderPositionChanged(qreal pos)
{
if (_type == Time) {
if (!_timePath.isEmpty()) {
if (pos <= _timePath.elementAt(_timePath.elementCount() - 1).x)
if (_time) {
if (pos <= _graph.last().t())
emit sliderPositionChanged(distanceAtTime(pos));
else
emit sliderPositionChanged(_distancePath.elementAt(
_distancePath.elementCount() - 1).x + 1);
emit sliderPositionChanged(_graph.last().s() + 1);
} else
emit sliderPositionChanged(_distancePath.elementAt(
_distancePath.elementCount() - 1).x + 1);
emit sliderPositionChanged(_graph.last().s() + 1);
} else
emit sliderPositionChanged(pos);
}
@ -158,3 +149,47 @@ void GraphItem::selected(bool selected)
update();
}
void GraphItem::setScale(qreal sx, qreal sy)
{
if (_sx == sx && _sy == sy)
return;
prepareGeometryChange();
_sx = sx; _sy = sy;
updatePath();
}
void GraphItem::updatePath()
{
_path = QPainterPath();
if (_type == Time && !_time)
return;
_path.moveTo(_graph.first().x(_type) * _sx, -_graph.first().y() * _sy);
for (int i = 1; i < _graph.size(); i++)
_path.lineTo(_graph.at(i).x(_type) * _sx, -_graph.at(i).y() * _sy);
}
void GraphItem::updateBounds()
{
if (_type == Time && !_time) {
_bounds = QRectF();
return;
}
qreal bottom, top, left, right;
QPointF p = QPointF(_graph.first().x(_type), -_graph.first().y());
bottom = p.y(); top = p.y(); left = p.x(); right = p.x();
for (int i = 1; i < _graph.size(); i++) {
p = QPointF(_graph.at(i).x(_type), -_graph.at(i).y());
bottom = qMax(bottom, p.y()); top = qMin(top, p.y());
right = qMax(right, p.x()); left = qMin(left, p.x());
}
_bounds = QRectF(QPointF(left, top), QPointF(right, bottom));
}

View File

@ -13,11 +13,13 @@ public:
GraphItem(const Graph &graph, QGraphicsItem *parent = 0);
QRectF boundingRect() const
{return (_type == Distance) ? _distancePath.boundingRect()
: _timePath.boundingRect();}
{return _path.boundingRect();}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
const QRectF &bounds() const {return _bounds;}
void setScale(qreal sx, qreal sy);
void setGraphType(GraphType type);
int id() const {return _id;}
void setId(int id) {_id = id;}
@ -34,10 +36,19 @@ public slots:
void selected(bool selected);
private:
void updatePath();
void updateBounds();
int _id;
QPen _pen;
QPainterPath _distancePath, _timePath;
Graph _graph;
GraphType _type;
bool _time;
QPainterPath _path;
QRectF _bounds;
qreal _sx, _sy;
};
#endif // GRAPHITEM_H

View File

@ -153,7 +153,7 @@ void GraphView::setGraphType(GraphType type)
for (int i = 0; i < _graphs.count(); i++) {
_graphs.at(i)->setGraphType(type);
if (_graphs.at(i)->scene() == _scene)
_bounds |= _graphs.at(i)->boundingRect();
_bounds |= _graphs.at(i)->bounds();
}
if (type == Distance)
@ -186,7 +186,7 @@ void GraphView::loadGraph(const Graph &graph, PathItem *path, int id)
if (!_hide.contains(id)) {
_visible.append(gi);
_scene->addItem(gi);
_bounds |= gi->boundingRect();
_bounds |= gi->bounds();
setXUnits();
}
}
@ -219,7 +219,7 @@ void GraphView::showGraph(bool show, int id)
else {
addItem(gi);
_visible.append(gi);
_bounds |= gi->boundingRect();
_bounds |= gi->bounds();
}
}
}
@ -241,8 +241,7 @@ void GraphView::redraw(const QSizeF &size)
QRectF r;
QSizeF mx, my;
RangeF rx, ry;
QTransform transform;
qreal xs, ys;
qreal sx, sy;
if (_visible.isEmpty() || _bounds.isNull()) {
@ -275,20 +274,19 @@ void GraphView::redraw(const QSizeF &size)
r.adjust(0, -(_minYRange/2 - r.height()/2), 0,
_minYRange/2 - r.height()/2);
xs = (size.width() - (my.width() + mx.width())) / r.width();
ys = (size.height() - (mx.height() + my.height())
sx = (size.width() - (my.width() + mx.width())) / r.width();
sy = (size.height() - (mx.height() + my.height())
- _info->boundingRect().height()) / r.height();
transform.scale(xs, ys);
for (int i = 0; i < _visible.size(); i++)
_visible.at(i)->setTransform(transform);
_visible.at(i)->setScale(sx, sy);
QPointF p(r.left() * xs, r.top() * ys);
QSizeF s(r.width() * xs, r.height() * ys);
QPointF p(r.left() * sx, r.top() * sy);
QSizeF s(r.width() * sx, r.height() * sy);
r = QRectF(p, s);
if (r.height() < _minYRange * ys)
r.adjust(0, -(_minYRange/2 * ys - r.height()/2), 0,
(_minYRange/2) * ys - r.height()/2);
if (r.height() < _minYRange * sy)
r.adjust(0, -(_minYRange/2 * sy - r.height()/2), 0,
(_minYRange/2) * sy - r.height()/2);
_xAxis->setSize(r.width());
_yAxis->setSize(r.height());
@ -368,7 +366,7 @@ void GraphView::updateSliderInfo()
if (!_sliderInfo->isVisible())
return;
QRectF br(_visible.first()->boundingRect());
QRectF br(_visible.first()->bounds());
if (br.height() < _minYRange)
br.adjust(0, -(_minYRange/2 - br.height()/2), 0,
_minYRange/2 - br.height()/2);