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:
parent
cb52ad8bc5
commit
be3c101c07
@ -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;}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user