1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-28 05:34:47 +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 s() const {return _s;}
qreal t() const {return _t;} qreal t() const {return _t;}
qreal y() const {return _y;} qreal y() const {return _y;}
qreal x(GraphType type) const {return (type == Distance) ? _s : _t;}
void setS(qreal s) {_s = s;} void setS(qreal s) {_s = s;}
void setT(qreal t) {_t = t;} void setT(qreal t) {_t = t;}

View File

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

View File

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