##// END OF EJS Templates
Fix zooming in crash...
Titta Heikkala -
r2603:1e4c29e111d4
parent child
Show More
@@ -1,192 +1,198
1 /****************************************************************************
1 /****************************************************************************
2 **
2 **
3 ** Copyright (C) 2013 Digia Plc
3 ** Copyright (C) 2013 Digia Plc
4 ** All rights reserved.
4 ** All rights reserved.
5 ** For any questions to Digia, please use contact form at http://qt.digia.com
5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 **
6 **
7 ** This file is part of the Qt Enterprise Charts Add-on.
7 ** This file is part of the Qt Enterprise Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Enterprise licenses may use this file in
10 ** Licensees holding valid Qt Enterprise licenses may use this file in
11 ** accordance with the Qt Enterprise License Agreement provided with the
11 ** accordance with the Qt Enterprise License Agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.
13 ** a written agreement between you and Digia.
14 **
14 **
15 ** If you have questions regarding the use of this file, please use
15 ** If you have questions regarding the use of this file, please use
16 ** contact form at http://qt.digia.com
16 ** contact form at http://qt.digia.com
17 ** $QT_END_LICENSE$
17 ** $QT_END_LICENSE$
18 **
18 **
19 ****************************************************************************/
19 ****************************************************************************/
20
20
21 #include "areachartitem_p.h"
21 #include "areachartitem_p.h"
22 #include "qareaseries.h"
22 #include "qareaseries.h"
23 #include "qareaseries_p.h"
23 #include "qareaseries_p.h"
24 #include "qlineseries.h"
24 #include "qlineseries.h"
25 #include "chartpresenter_p.h"
25 #include "chartpresenter_p.h"
26 #include "abstractdomain_p.h"
26 #include "abstractdomain_p.h"
27 #include <QPainter>
27 #include <QPainter>
28 #include <QGraphicsSceneMouseEvent>
28 #include <QGraphicsSceneMouseEvent>
29 #include <QDebug>
29 #include <QDebug>
30
30
31
31
32 QTCOMMERCIALCHART_BEGIN_NAMESPACE
32 QTCOMMERCIALCHART_BEGIN_NAMESPACE
33
33
34 AreaChartItem::AreaChartItem(QAreaSeries *areaSeries, QGraphicsItem* item)
34 AreaChartItem::AreaChartItem(QAreaSeries *areaSeries, QGraphicsItem* item)
35 : ChartItem(areaSeries->d_func(),item),
35 : ChartItem(areaSeries->d_func(),item),
36 m_series(areaSeries),
36 m_series(areaSeries),
37 m_upper(0),
37 m_upper(0),
38 m_lower(0),
38 m_lower(0),
39 m_pointsVisible(false)
39 m_pointsVisible(false)
40 {
40 {
41 setAcceptHoverEvents(true);
41 setAcceptHoverEvents(true);
42 setZValue(ChartPresenter::LineChartZValue);
42 setZValue(ChartPresenter::LineChartZValue);
43 if (m_series->upperSeries())
43 if (m_series->upperSeries())
44 m_upper = new AreaBoundItem(this, m_series->upperSeries());
44 m_upper = new AreaBoundItem(this, m_series->upperSeries());
45 if (m_series->lowerSeries())
45 if (m_series->lowerSeries())
46 m_lower = new AreaBoundItem(this, m_series->lowerSeries());
46 m_lower = new AreaBoundItem(this, m_series->lowerSeries());
47
47
48 QObject::connect(m_series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
48 QObject::connect(m_series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
49 QObject::connect(m_series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
49 QObject::connect(m_series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
50 QObject::connect(m_series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
50 QObject::connect(m_series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
51 QObject::connect(this, SIGNAL(clicked(QPointF)), areaSeries, SIGNAL(clicked(QPointF)));
51 QObject::connect(this, SIGNAL(clicked(QPointF)), areaSeries, SIGNAL(clicked(QPointF)));
52 QObject::connect(this, SIGNAL(hovered(QPointF,bool)), areaSeries, SIGNAL(hovered(QPointF,bool)));
52 QObject::connect(this, SIGNAL(hovered(QPointF,bool)), areaSeries, SIGNAL(hovered(QPointF,bool)));
53
53
54 handleUpdated();
54 handleUpdated();
55 }
55 }
56
56
57 AreaChartItem::~AreaChartItem()
57 AreaChartItem::~AreaChartItem()
58 {
58 {
59 delete m_upper;
59 delete m_upper;
60 delete m_lower;
60 delete m_lower;
61 }
61 }
62
62
63 void AreaChartItem::setPresenter(ChartPresenter *presenter)
63 void AreaChartItem::setPresenter(ChartPresenter *presenter)
64 {
64 {
65 if (m_upper)
65 if (m_upper)
66 m_upper->setPresenter(presenter);
66 m_upper->setPresenter(presenter);
67 if (m_lower) {
67 if (m_lower) {
68 m_lower->setPresenter(presenter);
68 m_lower->setPresenter(presenter);
69 }
69 }
70 ChartItem::setPresenter(presenter);
70 ChartItem::setPresenter(presenter);
71 }
71 }
72
72
73 QRectF AreaChartItem::boundingRect() const
73 QRectF AreaChartItem::boundingRect() const
74 {
74 {
75 return m_rect;
75 return m_rect;
76 }
76 }
77
77
78 QPainterPath AreaChartItem::shape() const
78 QPainterPath AreaChartItem::shape() const
79 {
79 {
80 return m_path;
80 return m_path;
81 }
81 }
82
82
83 void AreaChartItem::updatePath()
83 void AreaChartItem::updatePath()
84 {
84 {
85 QPainterPath path;
85 QPainterPath path;
86 QRectF rect(QPointF(0,0),domain()->size());
86 QRectF rect(QPointF(0,0),domain()->size());
87
87
88 path = m_upper->path();
88 path = m_upper->path();
89
89
90 if (m_lower) {
90 if (m_lower) {
91 // Note: Polarcharts always draw area correctly only when both series have equal width or are
91 // Note: Polarcharts always draw area correctly only when both series have equal width or are
92 // fully displayed. If one series is partally off-chart, the connecting line between
92 // fully displayed. If one series is partally off-chart, the connecting line between
93 // the series does not attach to the end of the partially hidden series but to the point
93 // the series does not attach to the end of the partially hidden series but to the point
94 // where it intersects the axis line. The problem is especially noticeable when one of the series
94 // where it intersects the axis line. The problem is especially noticeable when one of the series
95 // is entirely off-chart, in which case the connecting line connects two ends of the
95 // is entirely off-chart, in which case the connecting line connects two ends of the
96 // visible series.
96 // visible series.
97 // This happens because we get the paths from linechart, which omits off-chart segments.
97 // This happens because we get the paths from linechart, which omits off-chart segments.
98 // To properly fix, linechart would need to provide true full path, in right, left, and the rest
98 // To properly fix, linechart would need to provide true full path, in right, left, and the rest
99 // portions to enable proper clipping. However, combining those to single visually unified area
99 // portions to enable proper clipping. However, combining those to single visually unified area
100 // would be a nightmare, since they would have to be painted separately.
100 // would be a nightmare, since they would have to be painted separately.
101 path.connectPath(m_lower->path().toReversed());
101 path.connectPath(m_lower->path().toReversed());
102 } else {
102 } else {
103 QPointF first = path.pointAtPercent(0);
103 QPointF first = path.pointAtPercent(0);
104 QPointF last = path.pointAtPercent(1);
104 QPointF last = path.pointAtPercent(1);
105 if (presenter()->chartType() == QChart::ChartTypeCartesian) {
105 if (presenter()->chartType() == QChart::ChartTypeCartesian) {
106 path.lineTo(last.x(), rect.bottom());
106 path.lineTo(last.x(), rect.bottom());
107 path.lineTo(first.x(), rect.bottom());
107 path.lineTo(first.x(), rect.bottom());
108 } else { // polar
108 } else { // polar
109 path.lineTo(rect.center());
109 path.lineTo(rect.center());
110 }
110 }
111 }
111 }
112 path.closeSubpath();
112 path.closeSubpath();
113 prepareGeometryChange();
113
114 m_path = path;
114 // Only zoom in if the bounding rect of the path fits inside int limits. QWidget::update() uses
115 m_rect = path.boundingRect();
115 // a region that has to be compatible with QRect.
116 update();
116 if (path.boundingRect().height() <= INT_MAX
117 && path.boundingRect().width() <= INT_MAX) {
118 prepareGeometryChange();
119 m_path = path;
120 m_rect = path.boundingRect();
121 update();
122 }
117 }
123 }
118
124
119 void AreaChartItem::handleUpdated()
125 void AreaChartItem::handleUpdated()
120 {
126 {
121 setVisible(m_series->isVisible());
127 setVisible(m_series->isVisible());
122 m_pointsVisible = m_series->pointsVisible();
128 m_pointsVisible = m_series->pointsVisible();
123 m_linePen = m_series->pen();
129 m_linePen = m_series->pen();
124 m_brush = m_series->brush();
130 m_brush = m_series->brush();
125 m_pointPen = m_series->pen();
131 m_pointPen = m_series->pen();
126 m_pointPen.setWidthF(2 * m_pointPen.width());
132 m_pointPen.setWidthF(2 * m_pointPen.width());
127 setOpacity(m_series->opacity());
133 setOpacity(m_series->opacity());
128 update();
134 update();
129 }
135 }
130
136
131 void AreaChartItem::handleDomainUpdated()
137 void AreaChartItem::handleDomainUpdated()
132 {
138 {
133 if (m_upper) {
139 if (m_upper) {
134 AbstractDomain* d = m_upper->domain();
140 AbstractDomain* d = m_upper->domain();
135 d->setSize(domain()->size());
141 d->setSize(domain()->size());
136 d->setRange(domain()->minX(),domain()->maxX(),domain()->minY(),domain()->maxY());
142 d->setRange(domain()->minX(),domain()->maxX(),domain()->minY(),domain()->maxY());
137 m_upper->handleDomainUpdated();
143 m_upper->handleDomainUpdated();
138 }
144 }
139
145
140 if (m_lower) {
146 if (m_lower) {
141 AbstractDomain* d = m_lower->domain();
147 AbstractDomain* d = m_lower->domain();
142 d->setSize(domain()->size());
148 d->setSize(domain()->size());
143 d->setRange(domain()->minX(),domain()->maxX(),domain()->minY(),domain()->maxY());
149 d->setRange(domain()->minX(),domain()->maxX(),domain()->minY(),domain()->maxY());
144 m_lower->handleDomainUpdated();
150 m_lower->handleDomainUpdated();
145 }
151 }
146 }
152 }
147
153
148 void AreaChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
154 void AreaChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
149 {
155 {
150 Q_UNUSED(widget)
156 Q_UNUSED(widget)
151 Q_UNUSED(option)
157 Q_UNUSED(option)
152 painter->save();
158 painter->save();
153 painter->setPen(m_linePen);
159 painter->setPen(m_linePen);
154 painter->setBrush(m_brush);
160 painter->setBrush(m_brush);
155 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
161 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
156 if (presenter()->chartType() == QChart::ChartTypePolar)
162 if (presenter()->chartType() == QChart::ChartTypePolar)
157 painter->setClipRegion(QRegion(clipRect.toRect(), QRegion::Ellipse));
163 painter->setClipRegion(QRegion(clipRect.toRect(), QRegion::Ellipse));
158 else
164 else
159 painter->setClipRect(clipRect);
165 painter->setClipRect(clipRect);
160 painter->drawPath(m_path);
166 painter->drawPath(m_path);
161 if (m_pointsVisible) {
167 if (m_pointsVisible) {
162 painter->setPen(m_pointPen);
168 painter->setPen(m_pointPen);
163 painter->drawPoints(m_upper->geometryPoints());
169 painter->drawPoints(m_upper->geometryPoints());
164 if (m_lower)
170 if (m_lower)
165 painter->drawPoints(m_lower->geometryPoints());
171 painter->drawPoints(m_lower->geometryPoints());
166 }
172 }
167 painter->restore();
173 painter->restore();
168 }
174 }
169
175
170 void AreaChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
176 void AreaChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
171 {
177 {
172 emit clicked(m_upper->domain()->calculateDomainPoint(event->pos()));
178 emit clicked(m_upper->domain()->calculateDomainPoint(event->pos()));
173 ChartItem::mousePressEvent(event);
179 ChartItem::mousePressEvent(event);
174 }
180 }
175
181
176 void AreaChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
182 void AreaChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
177 {
183 {
178 emit hovered(domain()->calculateDomainPoint(event->pos()), true);
184 emit hovered(domain()->calculateDomainPoint(event->pos()), true);
179 event->accept();
185 event->accept();
180 // QGraphicsItem::hoverEnterEvent(event);
186 // QGraphicsItem::hoverEnterEvent(event);
181 }
187 }
182
188
183 void AreaChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
189 void AreaChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
184 {
190 {
185 emit hovered(domain()->calculateDomainPoint(event->pos()), false);
191 emit hovered(domain()->calculateDomainPoint(event->pos()), false);
186 event->accept();
192 event->accept();
187 // QGraphicsItem::hoverEnterEvent(event);
193 // QGraphicsItem::hoverEnterEvent(event);
188 }
194 }
189
195
190 #include "moc_areachartitem_p.cpp"
196 #include "moc_areachartitem_p.cpp"
191
197
192 QTCOMMERCIALCHART_END_NAMESPACE
198 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,372 +1,385
1 /****************************************************************************
1 /****************************************************************************
2 **
2 **
3 ** Copyright (C) 2013 Digia Plc
3 ** Copyright (C) 2013 Digia Plc
4 ** All rights reserved.
4 ** All rights reserved.
5 ** For any questions to Digia, please use contact form at http://qt.digia.com
5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 **
6 **
7 ** This file is part of the Qt Enterprise Charts Add-on.
7 ** This file is part of the Qt Enterprise Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Enterprise licenses may use this file in
10 ** Licensees holding valid Qt Enterprise licenses may use this file in
11 ** accordance with the Qt Enterprise License Agreement provided with the
11 ** accordance with the Qt Enterprise License Agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.
13 ** a written agreement between you and Digia.
14 **
14 **
15 ** If you have questions regarding the use of this file, please use
15 ** If you have questions regarding the use of this file, please use
16 ** contact form at http://qt.digia.com
16 ** contact form at http://qt.digia.com
17 ** $QT_END_LICENSE$
17 ** $QT_END_LICENSE$
18 **
18 **
19 ****************************************************************************/
19 ****************************************************************************/
20
20
21 #include "linechartitem_p.h"
21 #include "linechartitem_p.h"
22 #include "qlineseries.h"
22 #include "qlineseries.h"
23 #include "qlineseries_p.h"
23 #include "qlineseries_p.h"
24 #include "chartpresenter_p.h"
24 #include "chartpresenter_p.h"
25 #include "polardomain_p.h"
25 #include "polardomain_p.h"
26 #include <QPainter>
26 #include <QPainter>
27 #include <QGraphicsSceneMouseEvent>
27 #include <QGraphicsSceneMouseEvent>
28
28
29 QTCOMMERCIALCHART_BEGIN_NAMESPACE
29 QTCOMMERCIALCHART_BEGIN_NAMESPACE
30
30
31 const qreal mouseEventMinWidth(12);
31 const qreal mouseEventMinWidth(12);
32
32
33 LineChartItem::LineChartItem(QLineSeries *series, QGraphicsItem *item)
33 LineChartItem::LineChartItem(QLineSeries *series, QGraphicsItem *item)
34 : XYChart(series,item),
34 : XYChart(series,item),
35 m_series(series),
35 m_series(series),
36 m_pointsVisible(false),
36 m_pointsVisible(false),
37 m_chartType(QChart::ChartTypeUndefined)
37 m_chartType(QChart::ChartTypeUndefined)
38 {
38 {
39 setAcceptHoverEvents(true);
39 setAcceptHoverEvents(true);
40 setZValue(ChartPresenter::LineChartZValue);
40 setZValue(ChartPresenter::LineChartZValue);
41 QObject::connect(series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
41 QObject::connect(series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
42 QObject::connect(series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
42 QObject::connect(series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
43 QObject::connect(series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
43 QObject::connect(series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
44 handleUpdated();
44 handleUpdated();
45 }
45 }
46
46
47 QRectF LineChartItem::boundingRect() const
47 QRectF LineChartItem::boundingRect() const
48 {
48 {
49 return m_rect;
49 return m_rect;
50 }
50 }
51
51
52 QPainterPath LineChartItem::shape() const
52 QPainterPath LineChartItem::shape() const
53 {
53 {
54 return m_shapePath;
54 return m_shapePath;
55 }
55 }
56
56
57 void LineChartItem::updateGeometry()
57 void LineChartItem::updateGeometry()
58 {
58 {
59 m_points = geometryPoints();
59 m_points = geometryPoints();
60 const QVector<QPointF> &points = m_points;
60 const QVector<QPointF> &points = m_points;
61
61
62 if (points.size() == 0) {
62 if (points.size() == 0) {
63 prepareGeometryChange();
63 prepareGeometryChange();
64 m_fullPath = QPainterPath();
64 m_fullPath = QPainterPath();
65 m_linePath = QPainterPath();
65 m_linePath = QPainterPath();
66 m_rect = QRect();
66 m_rect = QRect();
67 return;
67 return;
68 }
68 }
69
69
70 QPainterPath linePath;
70 QPainterPath linePath;
71 QPainterPath fullPath;
71 QPainterPath fullPath;
72 // Use worst case scenario to determine required margin.
72 // Use worst case scenario to determine required margin.
73 qreal margin = m_linePen.width() * 1.42;
73 qreal margin = m_linePen.width() * 1.42;
74
74
75 // Area series use component line series that aren't necessarily added to the chart themselves,
75 // Area series use component line series that aren't necessarily added to the chart themselves,
76 // so check if chart type is forced before trying to obtain it from the chart.
76 // so check if chart type is forced before trying to obtain it from the chart.
77 QChart::ChartType chartType = m_chartType;
77 QChart::ChartType chartType = m_chartType;
78 if (chartType == QChart::ChartTypeUndefined)
78 if (chartType == QChart::ChartTypeUndefined)
79 chartType = m_series->chart()->chartType();
79 chartType = m_series->chart()->chartType();
80
80
81 // For polar charts, we need special handling for angular (horizontal)
81 // For polar charts, we need special handling for angular (horizontal)
82 // points that are off-grid.
82 // points that are off-grid.
83 if (chartType == QChart::ChartTypePolar) {
83 if (chartType == QChart::ChartTypePolar) {
84 QPainterPath linePathLeft;
84 QPainterPath linePathLeft;
85 QPainterPath linePathRight;
85 QPainterPath linePathRight;
86 QPainterPath *currentSegmentPath = 0;
86 QPainterPath *currentSegmentPath = 0;
87 QPainterPath *previousSegmentPath = 0;
87 QPainterPath *previousSegmentPath = 0;
88 qreal minX = domain()->minX();
88 qreal minX = domain()->minX();
89 qreal maxX = domain()->maxX();
89 qreal maxX = domain()->maxX();
90 qreal minY = domain()->minY();
90 qreal minY = domain()->minY();
91 QPointF currentSeriesPoint = m_series->at(0);
91 QPointF currentSeriesPoint = m_series->at(0);
92 QPointF currentGeometryPoint = points.at(0);
92 QPointF currentGeometryPoint = points.at(0);
93 QPointF previousGeometryPoint = points.at(0);
93 QPointF previousGeometryPoint = points.at(0);
94 int size = m_linePen.width();
94 int size = m_linePen.width();
95 bool pointOffGrid = false;
95 bool pointOffGrid = false;
96 bool previousPointWasOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
96 bool previousPointWasOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
97
97
98 qreal domainRadius = domain()->size().height() / 2.0;
98 qreal domainRadius = domain()->size().height() / 2.0;
99 const QPointF centerPoint(domainRadius, domainRadius);
99 const QPointF centerPoint(domainRadius, domainRadius);
100
100
101 if (!previousPointWasOffGrid) {
101 if (!previousPointWasOffGrid) {
102 fullPath.moveTo(points.at(0));
102 fullPath.moveTo(points.at(0));
103 if (m_pointsVisible && currentSeriesPoint.y() >= minY) {
103 if (m_pointsVisible && currentSeriesPoint.y() >= minY) {
104 // Do not draw ellipses for points below minimum Y.
104 // Do not draw ellipses for points below minimum Y.
105 linePath.addEllipse(points.at(0), size, size);
105 linePath.addEllipse(points.at(0), size, size);
106 fullPath.addEllipse(points.at(0), size, size);
106 fullPath.addEllipse(points.at(0), size, size);
107 linePath.moveTo(points.at(0));
107 linePath.moveTo(points.at(0));
108 fullPath.moveTo(points.at(0));
108 fullPath.moveTo(points.at(0));
109 }
109 }
110 }
110 }
111
111
112 qreal leftMarginLine = centerPoint.x() - margin;
112 qreal leftMarginLine = centerPoint.x() - margin;
113 qreal rightMarginLine = centerPoint.x() + margin;
113 qreal rightMarginLine = centerPoint.x() + margin;
114 qreal horizontal = centerPoint.y();
114 qreal horizontal = centerPoint.y();
115
115
116 // See ScatterChartItem::updateGeometry() for explanation why seriesLastIndex is needed
116 // See ScatterChartItem::updateGeometry() for explanation why seriesLastIndex is needed
117 const int seriesLastIndex = m_series->count() - 1;
117 const int seriesLastIndex = m_series->count() - 1;
118
118
119 for (int i = 1; i < points.size(); i++) {
119 for (int i = 1; i < points.size(); i++) {
120 // Interpolating line fragments would be ugly when thick pen is used,
120 // Interpolating line fragments would be ugly when thick pen is used,
121 // so we work around it by utilizing three separate
121 // so we work around it by utilizing three separate
122 // paths for line segments and clip those with custom regions at paint time.
122 // paths for line segments and clip those with custom regions at paint time.
123 // "Right" path contains segments that cross the axis line with visible point on the
123 // "Right" path contains segments that cross the axis line with visible point on the
124 // right side of the axis line, as well as segments that have one point within the margin
124 // right side of the axis line, as well as segments that have one point within the margin
125 // on the right side of the axis line and another point on the right side of the chart.
125 // on the right side of the axis line and another point on the right side of the chart.
126 // "Left" path contains points with similarly on the left side.
126 // "Left" path contains points with similarly on the left side.
127 // "Full" path contains rest of the points.
127 // "Full" path contains rest of the points.
128 // This doesn't yield perfect results always. E.g. when segment covers more than 90
128 // This doesn't yield perfect results always. E.g. when segment covers more than 90
129 // degrees and both of the points are within the margin, one in the top half and one in the
129 // degrees and both of the points are within the margin, one in the top half and one in the
130 // bottom half of the chart, the bottom one gets clipped incorrectly.
130 // bottom half of the chart, the bottom one gets clipped incorrectly.
131 // However, this should be rare occurrence in any sensible chart.
131 // However, this should be rare occurrence in any sensible chart.
132 currentSeriesPoint = m_series->at(qMin(seriesLastIndex, i));
132 currentSeriesPoint = m_series->at(qMin(seriesLastIndex, i));
133 currentGeometryPoint = points.at(i);
133 currentGeometryPoint = points.at(i);
134 pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
134 pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
135
135
136 // Draw something unless both off-grid
136 // Draw something unless both off-grid
137 if (!pointOffGrid || !previousPointWasOffGrid) {
137 if (!pointOffGrid || !previousPointWasOffGrid) {
138 QPointF intersectionPoint;
138 QPointF intersectionPoint;
139 qreal y;
139 qreal y;
140 if (pointOffGrid != previousPointWasOffGrid) {
140 if (pointOffGrid != previousPointWasOffGrid) {
141 if (currentGeometryPoint.x() == previousGeometryPoint.x()) {
141 if (currentGeometryPoint.x() == previousGeometryPoint.x()) {
142 y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) / 2.0;
142 y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) / 2.0;
143 } else {
143 } else {
144 qreal ratio = (centerPoint.x() - currentGeometryPoint.x()) / (currentGeometryPoint.x() - previousGeometryPoint.x());
144 qreal ratio = (centerPoint.x() - currentGeometryPoint.x()) / (currentGeometryPoint.x() - previousGeometryPoint.x());
145 y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) * ratio;
145 y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) * ratio;
146 }
146 }
147 intersectionPoint = QPointF(centerPoint.x(), y);
147 intersectionPoint = QPointF(centerPoint.x(), y);
148 }
148 }
149
149
150 bool dummyOk; // We know points are ok, but this is needed
150 bool dummyOk; // We know points are ok, but this is needed
151 qreal currentAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(currentSeriesPoint.x(), dummyOk);
151 qreal currentAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(currentSeriesPoint.x(), dummyOk);
152 qreal previousAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(m_series->at(i - 1).x(), dummyOk);
152 qreal previousAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(m_series->at(i - 1).x(), dummyOk);
153
153
154 if ((qAbs(currentAngle - previousAngle) > 180.0)) {
154 if ((qAbs(currentAngle - previousAngle) > 180.0)) {
155 // If the angle between two points is over 180 degrees (half X range),
155 // If the angle between two points is over 180 degrees (half X range),
156 // any direct segment between them becomes meaningless.
156 // any direct segment between them becomes meaningless.
157 // In this case two line segments are drawn instead, from previous
157 // In this case two line segments are drawn instead, from previous
158 // point to the center and from center to current point.
158 // point to the center and from center to current point.
159 if ((previousAngle < 0.0 || (previousAngle <= 180.0 && previousGeometryPoint.x() < rightMarginLine))
159 if ((previousAngle < 0.0 || (previousAngle <= 180.0 && previousGeometryPoint.x() < rightMarginLine))
160 && previousGeometryPoint.y() < horizontal) {
160 && previousGeometryPoint.y() < horizontal) {
161 currentSegmentPath = &linePathRight;
161 currentSegmentPath = &linePathRight;
162 } else if ((previousAngle > 360.0 || (previousAngle > 180.0 && previousGeometryPoint.x() > leftMarginLine))
162 } else if ((previousAngle > 360.0 || (previousAngle > 180.0 && previousGeometryPoint.x() > leftMarginLine))
163 && previousGeometryPoint.y() < horizontal) {
163 && previousGeometryPoint.y() < horizontal) {
164 currentSegmentPath = &linePathLeft;
164 currentSegmentPath = &linePathLeft;
165 } else if (previousAngle > 0.0 && previousAngle < 360.0) {
165 } else if (previousAngle > 0.0 && previousAngle < 360.0) {
166 currentSegmentPath = &linePath;
166 currentSegmentPath = &linePath;
167 } else {
167 } else {
168 currentSegmentPath = 0;
168 currentSegmentPath = 0;
169 }
169 }
170
170
171 if (currentSegmentPath) {
171 if (currentSegmentPath) {
172 if (previousSegmentPath != currentSegmentPath)
172 if (previousSegmentPath != currentSegmentPath)
173 currentSegmentPath->moveTo(previousGeometryPoint);
173 currentSegmentPath->moveTo(previousGeometryPoint);
174 if (previousPointWasOffGrid)
174 if (previousPointWasOffGrid)
175 fullPath.moveTo(intersectionPoint);
175 fullPath.moveTo(intersectionPoint);
176
176
177 currentSegmentPath->lineTo(centerPoint);
177 currentSegmentPath->lineTo(centerPoint);
178 fullPath.lineTo(centerPoint);
178 fullPath.lineTo(centerPoint);
179 }
179 }
180
180
181 previousSegmentPath = currentSegmentPath;
181 previousSegmentPath = currentSegmentPath;
182
182
183 if ((currentAngle < 0.0 || (currentAngle <= 180.0 && currentGeometryPoint.x() < rightMarginLine))
183 if ((currentAngle < 0.0 || (currentAngle <= 180.0 && currentGeometryPoint.x() < rightMarginLine))
184 && currentGeometryPoint.y() < horizontal) {
184 && currentGeometryPoint.y() < horizontal) {
185 currentSegmentPath = &linePathRight;
185 currentSegmentPath = &linePathRight;
186 } else if ((currentAngle > 360.0 || (currentAngle > 180.0 &&currentGeometryPoint.x() > leftMarginLine))
186 } else if ((currentAngle > 360.0 || (currentAngle > 180.0 &&currentGeometryPoint.x() > leftMarginLine))
187 && currentGeometryPoint.y() < horizontal) {
187 && currentGeometryPoint.y() < horizontal) {
188 currentSegmentPath = &linePathLeft;
188 currentSegmentPath = &linePathLeft;
189 } else if (currentAngle > 0.0 && currentAngle < 360.0) {
189 } else if (currentAngle > 0.0 && currentAngle < 360.0) {
190 currentSegmentPath = &linePath;
190 currentSegmentPath = &linePath;
191 } else {
191 } else {
192 currentSegmentPath = 0;
192 currentSegmentPath = 0;
193 }
193 }
194
194
195 if (currentSegmentPath) {
195 if (currentSegmentPath) {
196 if (previousSegmentPath != currentSegmentPath)
196 if (previousSegmentPath != currentSegmentPath)
197 currentSegmentPath->moveTo(centerPoint);
197 currentSegmentPath->moveTo(centerPoint);
198 if (!previousSegmentPath)
198 if (!previousSegmentPath)
199 fullPath.moveTo(centerPoint);
199 fullPath.moveTo(centerPoint);
200
200
201 currentSegmentPath->lineTo(currentGeometryPoint);
201 currentSegmentPath->lineTo(currentGeometryPoint);
202 if (pointOffGrid)
202 if (pointOffGrid)
203 fullPath.lineTo(intersectionPoint);
203 fullPath.lineTo(intersectionPoint);
204 else
204 else
205 fullPath.lineTo(currentGeometryPoint);
205 fullPath.lineTo(currentGeometryPoint);
206 }
206 }
207 } else {
207 } else {
208 if (previousAngle < 0.0 || currentAngle < 0.0
208 if (previousAngle < 0.0 || currentAngle < 0.0
209 || ((previousAngle <= 180.0 && currentAngle <= 180.0)
209 || ((previousAngle <= 180.0 && currentAngle <= 180.0)
210 && ((previousGeometryPoint.x() < rightMarginLine && previousGeometryPoint.y() < horizontal)
210 && ((previousGeometryPoint.x() < rightMarginLine && previousGeometryPoint.y() < horizontal)
211 || (currentGeometryPoint.x() < rightMarginLine && currentGeometryPoint.y() < horizontal)))) {
211 || (currentGeometryPoint.x() < rightMarginLine && currentGeometryPoint.y() < horizontal)))) {
212 currentSegmentPath = &linePathRight;
212 currentSegmentPath = &linePathRight;
213 } else if (previousAngle > 360.0 || currentAngle > 360.0
213 } else if (previousAngle > 360.0 || currentAngle > 360.0
214 || ((previousAngle > 180.0 && currentAngle > 180.0)
214 || ((previousAngle > 180.0 && currentAngle > 180.0)
215 && ((previousGeometryPoint.x() > leftMarginLine && previousGeometryPoint.y() < horizontal)
215 && ((previousGeometryPoint.x() > leftMarginLine && previousGeometryPoint.y() < horizontal)
216 || (currentGeometryPoint.x() > leftMarginLine && currentGeometryPoint.y() < horizontal)))) {
216 || (currentGeometryPoint.x() > leftMarginLine && currentGeometryPoint.y() < horizontal)))) {
217 currentSegmentPath = &linePathLeft;
217 currentSegmentPath = &linePathLeft;
218 } else {
218 } else {
219 currentSegmentPath = &linePath;
219 currentSegmentPath = &linePath;
220 }
220 }
221
221
222 if (currentSegmentPath != previousSegmentPath)
222 if (currentSegmentPath != previousSegmentPath)
223 currentSegmentPath->moveTo(previousGeometryPoint);
223 currentSegmentPath->moveTo(previousGeometryPoint);
224 if (previousPointWasOffGrid)
224 if (previousPointWasOffGrid)
225 fullPath.moveTo(intersectionPoint);
225 fullPath.moveTo(intersectionPoint);
226
226
227 if (pointOffGrid)
227 if (pointOffGrid)
228 fullPath.lineTo(intersectionPoint);
228 fullPath.lineTo(intersectionPoint);
229 else
229 else
230 fullPath.lineTo(currentGeometryPoint);
230 fullPath.lineTo(currentGeometryPoint);
231 currentSegmentPath->lineTo(currentGeometryPoint);
231 currentSegmentPath->lineTo(currentGeometryPoint);
232 }
232 }
233 } else {
233 } else {
234 currentSegmentPath = 0;
234 currentSegmentPath = 0;
235 }
235 }
236
236
237 previousPointWasOffGrid = pointOffGrid;
237 previousPointWasOffGrid = pointOffGrid;
238 if (m_pointsVisible && !pointOffGrid && currentSeriesPoint.y() >= minY) {
238 if (m_pointsVisible && !pointOffGrid && currentSeriesPoint.y() >= minY) {
239 linePath.addEllipse(points.at(i), size, size);
239 linePath.addEllipse(points.at(i), size, size);
240 fullPath.addEllipse(points.at(i), size, size);
240 fullPath.addEllipse(points.at(i), size, size);
241 linePath.moveTo(points.at(i));
241 linePath.moveTo(points.at(i));
242 fullPath.moveTo(points.at(i));
242 fullPath.moveTo(points.at(i));
243 }
243 }
244 previousSegmentPath = currentSegmentPath;
244 previousSegmentPath = currentSegmentPath;
245 previousGeometryPoint = currentGeometryPoint;
245 previousGeometryPoint = currentGeometryPoint;
246 }
246 }
247 m_linePathPolarRight = linePathRight;
247 m_linePathPolarRight = linePathRight;
248 m_linePathPolarLeft = linePathLeft;
248 m_linePathPolarLeft = linePathLeft;
249 // Note: This construction of m_fullpath is not perfect. The partial segments that are
249 // Note: This construction of m_fullpath is not perfect. The partial segments that are
250 // outside left/right clip regions at axis boundary still generate hover/click events,
250 // outside left/right clip regions at axis boundary still generate hover/click events,
251 // because shape doesn't get clipped. It doesn't seem possible to do sensibly.
251 // because shape doesn't get clipped. It doesn't seem possible to do sensibly.
252 } else { // not polar
252 } else { // not polar
253 linePath.moveTo(points.at(0));
253 linePath.moveTo(points.at(0));
254 if (m_pointsVisible) {
254 if (m_pointsVisible) {
255 int size = m_linePen.width();
255 int size = m_linePen.width();
256 linePath.addEllipse(points.at(0), size, size);
256 linePath.addEllipse(points.at(0), size, size);
257 linePath.moveTo(points.at(0));
257 linePath.moveTo(points.at(0));
258 for (int i = 1; i < points.size(); i++) {
258 for (int i = 1; i < points.size(); i++) {
259 linePath.lineTo(points.at(i));
259 linePath.lineTo(points.at(i));
260 linePath.addEllipse(points.at(i), size, size);
260 linePath.addEllipse(points.at(i), size, size);
261 linePath.moveTo(points.at(i));
261 linePath.moveTo(points.at(i));
262 }
262 }
263 } else {
263 } else {
264 for (int i = 1; i < points.size(); i++)
264 for (int i = 1; i < points.size(); i++)
265 linePath.lineTo(points.at(i));
265 linePath.lineTo(points.at(i));
266 }
266 }
267 fullPath = linePath;
267 fullPath = linePath;
268 }
268 }
269
269
270 QPainterPathStroker stroker;
270 QPainterPathStroker stroker;
271 // QPainter::drawLine does not respect join styles, for example BevelJoin becomes MiterJoin.
271 // QPainter::drawLine does not respect join styles, for example BevelJoin becomes MiterJoin.
272 // This is why we are prepared for the "worst case" scenario, i.e. use always MiterJoin and
272 // This is why we are prepared for the "worst case" scenario, i.e. use always MiterJoin and
273 // multiply line width with square root of two when defining shape and bounding rectangle.
273 // multiply line width with square root of two when defining shape and bounding rectangle.
274 stroker.setWidth(margin);
274 stroker.setWidth(margin);
275 stroker.setJoinStyle(Qt::MiterJoin);
275 stroker.setJoinStyle(Qt::MiterJoin);
276 stroker.setCapStyle(Qt::SquareCap);
276 stroker.setCapStyle(Qt::SquareCap);
277 stroker.setMiterLimit(m_linePen.miterLimit());
277 stroker.setMiterLimit(m_linePen.miterLimit());
278
278
279 prepareGeometryChange();
279 QPainterPath checkShapePath = stroker.createStroke(fullPath);
280
281 // Only zoom in if the bounding rects of the paths fit inside int limits. QWidget::update() uses
282 // a region that has to be compatible with QRect.
283 if (checkShapePath.boundingRect().height() <= INT_MAX
284 && checkShapePath.boundingRect().width() <= INT_MAX
285 && linePath.boundingRect().height() <= INT_MAX
286 && linePath.boundingRect().width() <= INT_MAX
287 && fullPath.boundingRect().height() <= INT_MAX
288 && fullPath.boundingRect().width() <= INT_MAX) {
289 prepareGeometryChange();
280
290
281 m_linePath = linePath;
291 m_linePath = linePath;
282 m_fullPath = fullPath;
292 m_fullPath = fullPath;
283 m_shapePath = stroker.createStroke(fullPath);
293 m_shapePath = checkShapePath;
284
294
285 m_rect = m_shapePath.boundingRect();
295 m_rect = m_shapePath.boundingRect();
296 } else {
297 update();
298 }
286 }
299 }
287
300
288 void LineChartItem::handleUpdated()
301 void LineChartItem::handleUpdated()
289 {
302 {
290 // If points visiblity has changed, a geometry update is needed.
303 // If points visiblity has changed, a geometry update is needed.
291 // Also, if pen changes when points are visible, geometry update is needed.
304 // Also, if pen changes when points are visible, geometry update is needed.
292 bool doGeometryUpdate =
305 bool doGeometryUpdate =
293 (m_pointsVisible != m_series->pointsVisible())
306 (m_pointsVisible != m_series->pointsVisible())
294 || (m_series->pointsVisible() && (m_linePen != m_series->pen()));
307 || (m_series->pointsVisible() && (m_linePen != m_series->pen()));
295 setVisible(m_series->isVisible());
308 setVisible(m_series->isVisible());
296 setOpacity(m_series->opacity());
309 setOpacity(m_series->opacity());
297 m_pointsVisible = m_series->pointsVisible();
310 m_pointsVisible = m_series->pointsVisible();
298 m_linePen = m_series->pen();
311 m_linePen = m_series->pen();
299 if (doGeometryUpdate)
312 if (doGeometryUpdate)
300 updateGeometry();
313 updateGeometry();
301 update();
314 update();
302 }
315 }
303
316
304 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
317 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
305 {
318 {
306 Q_UNUSED(widget)
319 Q_UNUSED(widget)
307 Q_UNUSED(option)
320 Q_UNUSED(option)
308
321
309 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
322 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
310
323
311 painter->save();
324 painter->save();
312 painter->setPen(m_linePen);
325 painter->setPen(m_linePen);
313 bool alwaysUsePath = false;
326 bool alwaysUsePath = false;
314
327
315 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
328 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
316 qreal halfWidth = domain()->size().width() / 2.0;
329 qreal halfWidth = domain()->size().width() / 2.0;
317 QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
330 QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
318 QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
331 QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
319 QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
332 QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
320 QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
333 QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
321 QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
334 QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
322 painter->setClipRegion(clipRegionLeft);
335 painter->setClipRegion(clipRegionLeft);
323 painter->drawPath(m_linePathPolarLeft);
336 painter->drawPath(m_linePathPolarLeft);
324 painter->setClipRegion(clipRegionRight);
337 painter->setClipRegion(clipRegionRight);
325 painter->drawPath(m_linePathPolarRight);
338 painter->drawPath(m_linePathPolarRight);
326 painter->setClipRegion(fullPolarClipRegion);
339 painter->setClipRegion(fullPolarClipRegion);
327 alwaysUsePath = true; // required for proper clipping
340 alwaysUsePath = true; // required for proper clipping
328 } else {
341 } else {
329 painter->setClipRect(clipRect);
342 painter->setClipRect(clipRect);
330 }
343 }
331
344
332 if (m_pointsVisible) {
345 if (m_pointsVisible) {
333 painter->setBrush(m_linePen.color());
346 painter->setBrush(m_linePen.color());
334 painter->drawPath(m_linePath);
347 painter->drawPath(m_linePath);
335 } else {
348 } else {
336 painter->setBrush(QBrush(Qt::NoBrush));
349 painter->setBrush(QBrush(Qt::NoBrush));
337 if (m_linePen.style() != Qt::SolidLine || alwaysUsePath) {
350 if (m_linePen.style() != Qt::SolidLine || alwaysUsePath) {
338 // If pen style is not solid line, always fall back to path painting
351 // If pen style is not solid line, always fall back to path painting
339 // to ensure proper continuity of the pattern
352 // to ensure proper continuity of the pattern
340 painter->drawPath(m_linePath);
353 painter->drawPath(m_linePath);
341 } else {
354 } else {
342 for (int i(1); i < m_points.size(); i++)
355 for (int i(1); i < m_points.size(); i++)
343 painter->drawLine(m_points.at(i - 1), m_points.at(i));
356 painter->drawLine(m_points.at(i - 1), m_points.at(i));
344 }
357 }
345 }
358 }
346
359
347 painter->restore();
360 painter->restore();
348 }
361 }
349
362
350 void LineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
363 void LineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
351 {
364 {
352 emit XYChart::clicked(domain()->calculateDomainPoint(event->pos()));
365 emit XYChart::clicked(domain()->calculateDomainPoint(event->pos()));
353 QGraphicsItem::mousePressEvent(event);
366 QGraphicsItem::mousePressEvent(event);
354 }
367 }
355
368
356 void LineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
369 void LineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
357 {
370 {
358 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
371 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
359 // event->accept();
372 // event->accept();
360 QGraphicsItem::hoverEnterEvent(event);
373 QGraphicsItem::hoverEnterEvent(event);
361 }
374 }
362
375
363 void LineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
376 void LineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
364 {
377 {
365 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
378 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
366 // event->accept();
379 // event->accept();
367 QGraphicsItem::hoverEnterEvent(event);
380 QGraphicsItem::hoverEnterEvent(event);
368 }
381 }
369
382
370 #include "moc_linechartitem_p.cpp"
383 #include "moc_linechartitem_p.cpp"
371
384
372 QTCOMMERCIALCHART_END_NAMESPACE
385 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,206 +1,211
1 /****************************************************************************
1 /****************************************************************************
2 **
2 **
3 ** Copyright (C) 2013 Digia Plc
3 ** Copyright (C) 2013 Digia Plc
4 ** All rights reserved.
4 ** All rights reserved.
5 ** For any questions to Digia, please use contact form at http://qt.digia.com
5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 **
6 **
7 ** This file is part of the Qt Enterprise Charts Add-on.
7 ** This file is part of the Qt Enterprise Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Enterprise licenses may use this file in
10 ** Licensees holding valid Qt Enterprise licenses may use this file in
11 ** accordance with the Qt Enterprise License Agreement provided with the
11 ** accordance with the Qt Enterprise License Agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.
13 ** a written agreement between you and Digia.
14 **
14 **
15 ** If you have questions regarding the use of this file, please use
15 ** If you have questions regarding the use of this file, please use
16 ** contact form at http://qt.digia.com
16 ** contact form at http://qt.digia.com
17 ** $QT_END_LICENSE$
17 ** $QT_END_LICENSE$
18 **
18 **
19 ****************************************************************************/
19 ****************************************************************************/
20
20
21 #include "scatterchartitem_p.h"
21 #include "scatterchartitem_p.h"
22 #include "qscatterseries.h"
22 #include "qscatterseries.h"
23 #include "qscatterseries_p.h"
23 #include "qscatterseries_p.h"
24 #include "chartpresenter_p.h"
24 #include "chartpresenter_p.h"
25 #include "abstractdomain_p.h"
25 #include "abstractdomain_p.h"
26 #include "qchart.h"
26 #include "qchart.h"
27 #include <QPainter>
27 #include <QPainter>
28 #include <QGraphicsScene>
28 #include <QGraphicsScene>
29 #include <QDebug>
29 #include <QDebug>
30 #include <QGraphicsSceneMouseEvent>
30 #include <QGraphicsSceneMouseEvent>
31
31
32 QTCOMMERCIALCHART_BEGIN_NAMESPACE
32 QTCOMMERCIALCHART_BEGIN_NAMESPACE
33
33
34 ScatterChartItem::ScatterChartItem(QScatterSeries *series, QGraphicsItem *item)
34 ScatterChartItem::ScatterChartItem(QScatterSeries *series, QGraphicsItem *item)
35 : XYChart(series,item),
35 : XYChart(series,item),
36 m_series(series),
36 m_series(series),
37 m_items(this),
37 m_items(this),
38 m_visible(true),
38 m_visible(true),
39 m_shape(QScatterSeries::MarkerShapeRectangle),
39 m_shape(QScatterSeries::MarkerShapeRectangle),
40 m_size(15)
40 m_size(15)
41 {
41 {
42 QObject::connect(m_series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
42 QObject::connect(m_series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
43 QObject::connect(m_series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
43 QObject::connect(m_series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
44 QObject::connect(m_series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
44 QObject::connect(m_series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
45
45
46 setZValue(ChartPresenter::ScatterSeriesZValue);
46 setZValue(ChartPresenter::ScatterSeriesZValue);
47 setFlags(QGraphicsItem::ItemClipsChildrenToShape);
47 setFlags(QGraphicsItem::ItemClipsChildrenToShape);
48
48
49 handleUpdated();
49 handleUpdated();
50
50
51 m_items.setHandlesChildEvents(false);
51 m_items.setHandlesChildEvents(false);
52 }
52 }
53
53
54 QRectF ScatterChartItem::boundingRect() const
54 QRectF ScatterChartItem::boundingRect() const
55 {
55 {
56 return m_rect;
56 return m_rect;
57 }
57 }
58
58
59 void ScatterChartItem::createPoints(int count)
59 void ScatterChartItem::createPoints(int count)
60 {
60 {
61 for (int i = 0; i < count; ++i) {
61 for (int i = 0; i < count; ++i) {
62
62
63 QGraphicsItem *item = 0;
63 QGraphicsItem *item = 0;
64
64
65 switch (m_shape) {
65 switch (m_shape) {
66 case QScatterSeries::MarkerShapeCircle: {
66 case QScatterSeries::MarkerShapeCircle: {
67 item = new CircleMarker(0, 0, m_size, m_size, this);
67 item = new CircleMarker(0, 0, m_size, m_size, this);
68 const QRectF &rect = item->boundingRect();
68 const QRectF &rect = item->boundingRect();
69 item->setPos(-rect.width() / 2, -rect.height() / 2);
69 item->setPos(-rect.width() / 2, -rect.height() / 2);
70 break;
70 break;
71 }
71 }
72 case QScatterSeries::MarkerShapeRectangle:
72 case QScatterSeries::MarkerShapeRectangle:
73 item = new RectangleMarker(0, 0, m_size, m_size, this);
73 item = new RectangleMarker(0, 0, m_size, m_size, this);
74 item->setPos(-m_size / 2, -m_size / 2);
74 item->setPos(-m_size / 2, -m_size / 2);
75 break;
75 break;
76 default:
76 default:
77 qWarning() << "Unsupported marker type";
77 qWarning() << "Unsupported marker type";
78 break;
78 break;
79 }
79 }
80 m_items.addToGroup(item);
80 m_items.addToGroup(item);
81 }
81 }
82 }
82 }
83
83
84 void ScatterChartItem::deletePoints(int count)
84 void ScatterChartItem::deletePoints(int count)
85 {
85 {
86 QList<QGraphicsItem *> items = m_items.childItems();
86 QList<QGraphicsItem *> items = m_items.childItems();
87
87
88 for (int i = 0; i < count; ++i) {
88 for (int i = 0; i < count; ++i) {
89 QGraphicsItem *item = items.takeLast();
89 QGraphicsItem *item = items.takeLast();
90 m_markerMap.remove(item);
90 m_markerMap.remove(item);
91 delete(item);
91 delete(item);
92 }
92 }
93 }
93 }
94
94
95 void ScatterChartItem::markerSelected(QGraphicsItem *marker)
95 void ScatterChartItem::markerSelected(QGraphicsItem *marker)
96 {
96 {
97 emit XYChart::clicked(m_markerMap[marker]);
97 emit XYChart::clicked(m_markerMap[marker]);
98 }
98 }
99
99
100 void ScatterChartItem::markerHovered(QGraphicsItem *marker, bool state)
100 void ScatterChartItem::markerHovered(QGraphicsItem *marker, bool state)
101 {
101 {
102 emit XYChart::hovered(m_markerMap[marker], state);
102 emit XYChart::hovered(m_markerMap[marker], state);
103 }
103 }
104
104
105 void ScatterChartItem::updateGeometry()
105 void ScatterChartItem::updateGeometry()
106 {
106 {
107
107
108 const QVector<QPointF>& points = geometryPoints();
108 const QVector<QPointF>& points = geometryPoints();
109
109
110 if (points.size() == 0) {
110 if (points.size() == 0) {
111 deletePoints(m_items.childItems().count());
111 deletePoints(m_items.childItems().count());
112 return;
112 return;
113 }
113 }
114
114
115 int diff = m_items.childItems().size() - points.size();
115 int diff = m_items.childItems().size() - points.size();
116
116
117 if (diff > 0)
117 if (diff > 0)
118 deletePoints(diff);
118 deletePoints(diff);
119 else if (diff < 0)
119 else if (diff < 0)
120 createPoints(-diff);
120 createPoints(-diff);
121
121
122 if (diff != 0)
122 if (diff != 0)
123 handleUpdated();
123 handleUpdated();
124
124
125 QList<QGraphicsItem *> items = m_items.childItems();
125 QList<QGraphicsItem *> items = m_items.childItems();
126
126
127 QRectF clipRect(QPointF(0,0),domain()->size());
127 QRectF clipRect(QPointF(0,0),domain()->size());
128
128
129 QVector<bool> offGridStatus = offGridStatusVector();
129 // Only zoom in if the clipRect fits inside int limits. QWidget::update() uses
130 const int seriesLastIndex = m_series->count() - 1;
130 // a region that has to be compatible with QRect.
131
131 if (clipRect.height() <= INT_MAX
132 for (int i = 0; i < points.size(); i++) {
132 && clipRect.width() <= INT_MAX) {
133 QGraphicsItem *item = items.at(i);
133 QVector<bool> offGridStatus = offGridStatusVector();
134 const QPointF &point = points.at(i);
134 const int seriesLastIndex = m_series->count() - 1;
135 const QRectF &rect = item->boundingRect();
135
136 // During remove animation series may have different number of points,
136 for (int i = 0; i < points.size(); i++) {
137 // so ensure we don't go over the index. Animation handling itself ensures that
137 QGraphicsItem *item = items.at(i);
138 // if there is actually no points in the series, then it won't generate a fake point,
138 const QPointF &point = points.at(i);
139 // so we can be assured there is always at least one point in m_series here.
139 const QRectF &rect = item->boundingRect();
140 // Note that marker map values can be technically incorrect during the animation,
140 // During remove animation series may have different number of points,
141 // if it was caused by an insert, but this shouldn't be a problem as the points are
141 // so ensure we don't go over the index. Animation handling itself ensures that
142 // fake anyway. After remove animation stops, geometry is updated to correct one.
142 // if there is actually no points in the series, then it won't generate a fake point,
143 m_markerMap[item] = m_series->at(qMin(seriesLastIndex, i));
143 // so we can be assured there is always at least one point in m_series here.
144 item->setPos(point.x() - rect.width() / 2, point.y() - rect.height() / 2);
144 // Note that marker map values can be technically incorrect during the animation,
145
145 // if it was caused by an insert, but this shouldn't be a problem as the points are
146 if (!m_visible || offGridStatus.at(i))
146 // fake anyway. After remove animation stops, geometry is updated to correct one.
147 item->setVisible(false);
147 m_markerMap[item] = m_series->at(qMin(seriesLastIndex, i));
148 else
148 item->setPos(point.x() - rect.width() / 2, point.y() - rect.height() / 2);
149 item->setVisible(true);
149
150 }
150 if (!m_visible || offGridStatus.at(i))
151 item->setVisible(false);
152 else
153 item->setVisible(true);
154 }
151
155
152 prepareGeometryChange();
156 prepareGeometryChange();
153 m_rect = clipRect;
157 m_rect = clipRect;
158 }
154 }
159 }
155
160
156 void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
161 void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
157 {
162 {
158 Q_UNUSED(painter)
163 Q_UNUSED(painter)
159 Q_UNUSED(option)
164 Q_UNUSED(option)
160 Q_UNUSED(widget)
165 Q_UNUSED(widget)
161 }
166 }
162
167
163 void ScatterChartItem::setPen(const QPen &pen)
168 void ScatterChartItem::setPen(const QPen &pen)
164 {
169 {
165 foreach (QGraphicsItem *item , m_items.childItems())
170 foreach (QGraphicsItem *item , m_items.childItems())
166 static_cast<QAbstractGraphicsShapeItem*>(item)->setPen(pen);
171 static_cast<QAbstractGraphicsShapeItem*>(item)->setPen(pen);
167 }
172 }
168
173
169 void ScatterChartItem::setBrush(const QBrush &brush)
174 void ScatterChartItem::setBrush(const QBrush &brush)
170 {
175 {
171 foreach (QGraphicsItem *item , m_items.childItems())
176 foreach (QGraphicsItem *item , m_items.childItems())
172 static_cast<QAbstractGraphicsShapeItem*>(item)->setBrush(brush);
177 static_cast<QAbstractGraphicsShapeItem*>(item)->setBrush(brush);
173 }
178 }
174
179
175 void ScatterChartItem::handleUpdated()
180 void ScatterChartItem::handleUpdated()
176 {
181 {
177 int count = m_items.childItems().count();
182 int count = m_items.childItems().count();
178
183
179 if (count == 0)
184 if (count == 0)
180 return;
185 return;
181
186
182 bool recreate = m_visible != m_series->isVisible()
187 bool recreate = m_visible != m_series->isVisible()
183 || m_size != m_series->markerSize()
188 || m_size != m_series->markerSize()
184 || m_shape != m_series->markerShape();
189 || m_shape != m_series->markerShape();
185
190
186 m_visible = m_series->isVisible();
191 m_visible = m_series->isVisible();
187 m_size = m_series->markerSize();
192 m_size = m_series->markerSize();
188 m_shape = m_series->markerShape();
193 m_shape = m_series->markerShape();
189 setOpacity(m_series->opacity());
194 setOpacity(m_series->opacity());
190
195
191 if (recreate) {
196 if (recreate) {
192 deletePoints(count);
197 deletePoints(count);
193 createPoints(count);
198 createPoints(count);
194
199
195 // Updating geometry is now safe, because it won't call handleUpdated unless it creates/deletes points
200 // Updating geometry is now safe, because it won't call handleUpdated unless it creates/deletes points
196 updateGeometry();
201 updateGeometry();
197 }
202 }
198
203
199 setPen(m_series->pen());
204 setPen(m_series->pen());
200 setBrush(m_series->brush());
205 setBrush(m_series->brush());
201 update();
206 update();
202 }
207 }
203
208
204 #include "moc_scatterchartitem_p.cpp"
209 #include "moc_scatterchartitem_p.cpp"
205
210
206 QTCOMMERCIALCHART_END_NAMESPACE
211 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,459 +1,468
1 /****************************************************************************
1 /****************************************************************************
2 **
2 **
3 ** Copyright (C) 2013 Digia Plc
3 ** Copyright (C) 2013 Digia Plc
4 ** All rights reserved.
4 ** All rights reserved.
5 ** For any questions to Digia, please use contact form at http://qt.digia.com
5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 **
6 **
7 ** This file is part of the Qt Enterprise Charts Add-on.
7 ** This file is part of the Qt Enterprise Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Enterprise licenses may use this file in
10 ** Licensees holding valid Qt Enterprise licenses may use this file in
11 ** accordance with the Qt Enterprise License Agreement provided with the
11 ** accordance with the Qt Enterprise License Agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.
13 ** a written agreement between you and Digia.
14 **
14 **
15 ** If you have questions regarding the use of this file, please use
15 ** If you have questions regarding the use of this file, please use
16 ** contact form at http://qt.digia.com
16 ** contact form at http://qt.digia.com
17 ** $QT_END_LICENSE$
17 ** $QT_END_LICENSE$
18 **
18 **
19 ****************************************************************************/
19 ****************************************************************************/
20
20
21 #include "splinechartitem_p.h"
21 #include "splinechartitem_p.h"
22 #include "qsplineseries_p.h"
22 #include "qsplineseries_p.h"
23 #include "chartpresenter_p.h"
23 #include "chartpresenter_p.h"
24 #include "splineanimation_p.h"
24 #include "splineanimation_p.h"
25 #include "polardomain_p.h"
25 #include "polardomain_p.h"
26 #include <QPainter>
26 #include <QPainter>
27 #include <QGraphicsSceneMouseEvent>
27 #include <QGraphicsSceneMouseEvent>
28
28
29 QTCOMMERCIALCHART_BEGIN_NAMESPACE
29 QTCOMMERCIALCHART_BEGIN_NAMESPACE
30
30
31 SplineChartItem::SplineChartItem(QSplineSeries *series, QGraphicsItem *item)
31 SplineChartItem::SplineChartItem(QSplineSeries *series, QGraphicsItem *item)
32 : XYChart(series,item),
32 : XYChart(series,item),
33 m_series(series),
33 m_series(series),
34 m_pointsVisible(false),
34 m_pointsVisible(false),
35 m_animation(0)
35 m_animation(0)
36 {
36 {
37 setAcceptHoverEvents(true);
37 setAcceptHoverEvents(true);
38 setZValue(ChartPresenter::SplineChartZValue);
38 setZValue(ChartPresenter::SplineChartZValue);
39 QObject::connect(m_series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
39 QObject::connect(m_series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
40 QObject::connect(series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
40 QObject::connect(series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
41 QObject::connect(series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
41 QObject::connect(series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
42 handleUpdated();
42 handleUpdated();
43 }
43 }
44
44
45 QRectF SplineChartItem::boundingRect() const
45 QRectF SplineChartItem::boundingRect() const
46 {
46 {
47 return m_rect;
47 return m_rect;
48 }
48 }
49
49
50 QPainterPath SplineChartItem::shape() const
50 QPainterPath SplineChartItem::shape() const
51 {
51 {
52 return m_fullPath;
52 return m_fullPath;
53 }
53 }
54
54
55 void SplineChartItem::setAnimation(SplineAnimation *animation)
55 void SplineChartItem::setAnimation(SplineAnimation *animation)
56 {
56 {
57 m_animation = animation;
57 m_animation = animation;
58 XYChart::setAnimation(animation);
58 XYChart::setAnimation(animation);
59 }
59 }
60
60
61 ChartAnimation *SplineChartItem::animation() const
61 ChartAnimation *SplineChartItem::animation() const
62 {
62 {
63 return m_animation;
63 return m_animation;
64 }
64 }
65
65
66 void SplineChartItem::setControlGeometryPoints(QVector<QPointF>& points)
66 void SplineChartItem::setControlGeometryPoints(QVector<QPointF>& points)
67 {
67 {
68 m_controlPoints = points;
68 m_controlPoints = points;
69 }
69 }
70
70
71 QVector<QPointF> SplineChartItem::controlGeometryPoints() const
71 QVector<QPointF> SplineChartItem::controlGeometryPoints() const
72 {
72 {
73 return m_controlPoints;
73 return m_controlPoints;
74 }
74 }
75
75
76 void SplineChartItem::updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index)
76 void SplineChartItem::updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index)
77 {
77 {
78 QVector<QPointF> controlPoints;
78 QVector<QPointF> controlPoints;
79 if (newPoints.count() >= 2)
79 if (newPoints.count() >= 2)
80 controlPoints = calculateControlPoints(newPoints);
80 controlPoints = calculateControlPoints(newPoints);
81
81
82 if (m_animation)
82 if (m_animation)
83 m_animation->setup(oldPoints, newPoints, m_controlPoints, controlPoints, index);
83 m_animation->setup(oldPoints, newPoints, m_controlPoints, controlPoints, index);
84
84
85 m_points = newPoints;
85 m_points = newPoints;
86 m_controlPoints = controlPoints;
86 m_controlPoints = controlPoints;
87 setDirty(false);
87 setDirty(false);
88
88
89 if (m_animation)
89 if (m_animation)
90 presenter()->startAnimation(m_animation);
90 presenter()->startAnimation(m_animation);
91 else
91 else
92 updateGeometry();
92 updateGeometry();
93 }
93 }
94
94
95 void SplineChartItem::updateGeometry()
95 void SplineChartItem::updateGeometry()
96 {
96 {
97 const QVector<QPointF> &points = m_points;
97 const QVector<QPointF> &points = m_points;
98 const QVector<QPointF> &controlPoints = m_controlPoints;
98 const QVector<QPointF> &controlPoints = m_controlPoints;
99
99
100 if ((points.size() < 2) || (controlPoints.size() < 2)) {
100 if ((points.size() < 2) || (controlPoints.size() < 2)) {
101 prepareGeometryChange();
101 prepareGeometryChange();
102 m_path = QPainterPath();
102 m_path = QPainterPath();
103 m_rect = QRect();
103 m_rect = QRect();
104 return;
104 return;
105 }
105 }
106
106
107 Q_ASSERT(points.count() * 2 - 2 == controlPoints.count());
107 Q_ASSERT(points.count() * 2 - 2 == controlPoints.count());
108
108
109 QPainterPath splinePath;
109 QPainterPath splinePath;
110 QPainterPath fullPath;
110 QPainterPath fullPath;
111 // Use worst case scenario to determine required margin.
111 // Use worst case scenario to determine required margin.
112 qreal margin = m_linePen.width() * 1.42;
112 qreal margin = m_linePen.width() * 1.42;
113
113
114 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
114 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
115 QPainterPath splinePathLeft;
115 QPainterPath splinePathLeft;
116 QPainterPath splinePathRight;
116 QPainterPath splinePathRight;
117 QPainterPath *currentSegmentPath = 0;
117 QPainterPath *currentSegmentPath = 0;
118 QPainterPath *previousSegmentPath = 0;
118 QPainterPath *previousSegmentPath = 0;
119 qreal minX = domain()->minX();
119 qreal minX = domain()->minX();
120 qreal maxX = domain()->maxX();
120 qreal maxX = domain()->maxX();
121 qreal minY = domain()->minY();
121 qreal minY = domain()->minY();
122 QPointF currentSeriesPoint = m_series->at(0);
122 QPointF currentSeriesPoint = m_series->at(0);
123 QPointF currentGeometryPoint = points.at(0);
123 QPointF currentGeometryPoint = points.at(0);
124 QPointF previousGeometryPoint = points.at(0);
124 QPointF previousGeometryPoint = points.at(0);
125 bool pointOffGrid = false;
125 bool pointOffGrid = false;
126 bool previousPointWasOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
126 bool previousPointWasOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
127 m_visiblePoints.clear();
127 m_visiblePoints.clear();
128 m_visiblePoints.reserve(points.size());
128 m_visiblePoints.reserve(points.size());
129
129
130 qreal domainRadius = domain()->size().height() / 2.0;
130 qreal domainRadius = domain()->size().height() / 2.0;
131 const QPointF centerPoint(domainRadius, domainRadius);
131 const QPointF centerPoint(domainRadius, domainRadius);
132
132
133 if (!previousPointWasOffGrid) {
133 if (!previousPointWasOffGrid) {
134 fullPath.moveTo(points.at(0));
134 fullPath.moveTo(points.at(0));
135 // Do not draw points for points below minimum Y.
135 // Do not draw points for points below minimum Y.
136 if (m_pointsVisible && currentSeriesPoint.y() >= minY)
136 if (m_pointsVisible && currentSeriesPoint.y() >= minY)
137 m_visiblePoints.append(currentGeometryPoint);
137 m_visiblePoints.append(currentGeometryPoint);
138 }
138 }
139
139
140 qreal leftMarginLine = centerPoint.x() - margin;
140 qreal leftMarginLine = centerPoint.x() - margin;
141 qreal rightMarginLine = centerPoint.x() + margin;
141 qreal rightMarginLine = centerPoint.x() + margin;
142 qreal horizontal = centerPoint.y();
142 qreal horizontal = centerPoint.y();
143
143
144 // See ScatterChartItem::updateGeometry() for explanation why seriesLastIndex is needed
144 // See ScatterChartItem::updateGeometry() for explanation why seriesLastIndex is needed
145 const int seriesLastIndex = m_series->count() - 1;
145 const int seriesLastIndex = m_series->count() - 1;
146
146
147 for (int i = 1; i < points.size(); i++) {
147 for (int i = 1; i < points.size(); i++) {
148 // Interpolating spline fragments accurately is not trivial, and would anyway be ugly
148 // Interpolating spline fragments accurately is not trivial, and would anyway be ugly
149 // when thick pen is used, so we work around it by utilizing three separate
149 // when thick pen is used, so we work around it by utilizing three separate
150 // paths for spline segments and clip those with custom regions at paint time.
150 // paths for spline segments and clip those with custom regions at paint time.
151 // "Right" path contains segments that cross the axis line with visible point on the
151 // "Right" path contains segments that cross the axis line with visible point on the
152 // right side of the axis line, as well as segments that have one point within the margin
152 // right side of the axis line, as well as segments that have one point within the margin
153 // on the right side of the axis line and another point on the right side of the chart.
153 // on the right side of the axis line and another point on the right side of the chart.
154 // "Left" path contains points with similarly on the left side.
154 // "Left" path contains points with similarly on the left side.
155 // "Full" path contains rest of the points.
155 // "Full" path contains rest of the points.
156 // This doesn't yield perfect results always. E.g. when segment covers more than 90
156 // This doesn't yield perfect results always. E.g. when segment covers more than 90
157 // degrees and both of the points are within the margin, one in the top half and one in the
157 // degrees and both of the points are within the margin, one in the top half and one in the
158 // bottom half of the chart, the bottom one gets clipped incorrectly.
158 // bottom half of the chart, the bottom one gets clipped incorrectly.
159 // However, this should be rare occurrence in any sensible chart.
159 // However, this should be rare occurrence in any sensible chart.
160 currentSeriesPoint = m_series->at(qMin(seriesLastIndex, i));
160 currentSeriesPoint = m_series->at(qMin(seriesLastIndex, i));
161 currentGeometryPoint = points.at(i);
161 currentGeometryPoint = points.at(i);
162 pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
162 pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
163
163
164 // Draw something unless both off-grid
164 // Draw something unless both off-grid
165 if (!pointOffGrid || !previousPointWasOffGrid) {
165 if (!pointOffGrid || !previousPointWasOffGrid) {
166 bool dummyOk; // We know points are ok, but this is needed
166 bool dummyOk; // We know points are ok, but this is needed
167 qreal currentAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(currentSeriesPoint.x(), dummyOk);
167 qreal currentAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(currentSeriesPoint.x(), dummyOk);
168 qreal previousAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(m_series->at(i - 1).x(), dummyOk);
168 qreal previousAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(m_series->at(i - 1).x(), dummyOk);
169
169
170 if ((qAbs(currentAngle - previousAngle) > 180.0)) {
170 if ((qAbs(currentAngle - previousAngle) > 180.0)) {
171 // If the angle between two points is over 180 degrees (half X range),
171 // If the angle between two points is over 180 degrees (half X range),
172 // any direct segment between them becomes meaningless.
172 // any direct segment between them becomes meaningless.
173 // In this case two line segments are drawn instead, from previous
173 // In this case two line segments are drawn instead, from previous
174 // point to the center and from center to current point.
174 // point to the center and from center to current point.
175 if ((previousAngle < 0.0 || (previousAngle <= 180.0 && previousGeometryPoint.x() < rightMarginLine))
175 if ((previousAngle < 0.0 || (previousAngle <= 180.0 && previousGeometryPoint.x() < rightMarginLine))
176 && previousGeometryPoint.y() < horizontal) {
176 && previousGeometryPoint.y() < horizontal) {
177 currentSegmentPath = &splinePathRight;
177 currentSegmentPath = &splinePathRight;
178 } else if ((previousAngle > 360.0 || (previousAngle > 180.0 && previousGeometryPoint.x() > leftMarginLine))
178 } else if ((previousAngle > 360.0 || (previousAngle > 180.0 && previousGeometryPoint.x() > leftMarginLine))
179 && previousGeometryPoint.y() < horizontal) {
179 && previousGeometryPoint.y() < horizontal) {
180 currentSegmentPath = &splinePathLeft;
180 currentSegmentPath = &splinePathLeft;
181 } else if (previousAngle > 0.0 && previousAngle < 360.0) {
181 } else if (previousAngle > 0.0 && previousAngle < 360.0) {
182 currentSegmentPath = &splinePath;
182 currentSegmentPath = &splinePath;
183 } else {
183 } else {
184 currentSegmentPath = 0;
184 currentSegmentPath = 0;
185 }
185 }
186
186
187 if (currentSegmentPath) {
187 if (currentSegmentPath) {
188 if (previousSegmentPath != currentSegmentPath)
188 if (previousSegmentPath != currentSegmentPath)
189 currentSegmentPath->moveTo(previousGeometryPoint);
189 currentSegmentPath->moveTo(previousGeometryPoint);
190 if (!previousSegmentPath)
190 if (!previousSegmentPath)
191 fullPath.moveTo(previousGeometryPoint);
191 fullPath.moveTo(previousGeometryPoint);
192
192
193 currentSegmentPath->lineTo(centerPoint);
193 currentSegmentPath->lineTo(centerPoint);
194 fullPath.lineTo(centerPoint);
194 fullPath.lineTo(centerPoint);
195 }
195 }
196
196
197 previousSegmentPath = currentSegmentPath;
197 previousSegmentPath = currentSegmentPath;
198
198
199 if ((currentAngle < 0.0 || (currentAngle <= 180.0 && currentGeometryPoint.x() < rightMarginLine))
199 if ((currentAngle < 0.0 || (currentAngle <= 180.0 && currentGeometryPoint.x() < rightMarginLine))
200 && currentGeometryPoint.y() < horizontal) {
200 && currentGeometryPoint.y() < horizontal) {
201 currentSegmentPath = &splinePathRight;
201 currentSegmentPath = &splinePathRight;
202 } else if ((currentAngle > 360.0 || (currentAngle > 180.0 &&currentGeometryPoint.x() > leftMarginLine))
202 } else if ((currentAngle > 360.0 || (currentAngle > 180.0 &&currentGeometryPoint.x() > leftMarginLine))
203 && currentGeometryPoint.y() < horizontal) {
203 && currentGeometryPoint.y() < horizontal) {
204 currentSegmentPath = &splinePathLeft;
204 currentSegmentPath = &splinePathLeft;
205 } else if (currentAngle > 0.0 && currentAngle < 360.0) {
205 } else if (currentAngle > 0.0 && currentAngle < 360.0) {
206 currentSegmentPath = &splinePath;
206 currentSegmentPath = &splinePath;
207 } else {
207 } else {
208 currentSegmentPath = 0;
208 currentSegmentPath = 0;
209 }
209 }
210
210
211 if (currentSegmentPath) {
211 if (currentSegmentPath) {
212 if (previousSegmentPath != currentSegmentPath)
212 if (previousSegmentPath != currentSegmentPath)
213 currentSegmentPath->moveTo(centerPoint);
213 currentSegmentPath->moveTo(centerPoint);
214 if (!previousSegmentPath)
214 if (!previousSegmentPath)
215 fullPath.moveTo(centerPoint);
215 fullPath.moveTo(centerPoint);
216
216
217 currentSegmentPath->lineTo(currentGeometryPoint);
217 currentSegmentPath->lineTo(currentGeometryPoint);
218 fullPath.lineTo(currentGeometryPoint);
218 fullPath.lineTo(currentGeometryPoint);
219 }
219 }
220 } else {
220 } else {
221 QPointF cp1 = controlPoints[2 * (i - 1)];
221 QPointF cp1 = controlPoints[2 * (i - 1)];
222 QPointF cp2 = controlPoints[(2 * i) - 1];
222 QPointF cp2 = controlPoints[(2 * i) - 1];
223
223
224 if (previousAngle < 0.0 || currentAngle < 0.0
224 if (previousAngle < 0.0 || currentAngle < 0.0
225 || ((previousAngle <= 180.0 && currentAngle <= 180.0)
225 || ((previousAngle <= 180.0 && currentAngle <= 180.0)
226 && ((previousGeometryPoint.x() < rightMarginLine && previousGeometryPoint.y() < horizontal)
226 && ((previousGeometryPoint.x() < rightMarginLine && previousGeometryPoint.y() < horizontal)
227 || (currentGeometryPoint.x() < rightMarginLine && currentGeometryPoint.y() < horizontal)))) {
227 || (currentGeometryPoint.x() < rightMarginLine && currentGeometryPoint.y() < horizontal)))) {
228 currentSegmentPath = &splinePathRight;
228 currentSegmentPath = &splinePathRight;
229 } else if (previousAngle > 360.0 || currentAngle > 360.0
229 } else if (previousAngle > 360.0 || currentAngle > 360.0
230 || ((previousAngle > 180.0 && currentAngle > 180.0)
230 || ((previousAngle > 180.0 && currentAngle > 180.0)
231 && ((previousGeometryPoint.x() > leftMarginLine && previousGeometryPoint.y() < horizontal)
231 && ((previousGeometryPoint.x() > leftMarginLine && previousGeometryPoint.y() < horizontal)
232 || (currentGeometryPoint.x() > leftMarginLine && currentGeometryPoint.y() < horizontal)))) {
232 || (currentGeometryPoint.x() > leftMarginLine && currentGeometryPoint.y() < horizontal)))) {
233 currentSegmentPath = &splinePathLeft;
233 currentSegmentPath = &splinePathLeft;
234 } else {
234 } else {
235 currentSegmentPath = &splinePath;
235 currentSegmentPath = &splinePath;
236 }
236 }
237
237
238 if (currentSegmentPath != previousSegmentPath)
238 if (currentSegmentPath != previousSegmentPath)
239 currentSegmentPath->moveTo(previousGeometryPoint);
239 currentSegmentPath->moveTo(previousGeometryPoint);
240 if (!previousSegmentPath)
240 if (!previousSegmentPath)
241 fullPath.moveTo(previousGeometryPoint);
241 fullPath.moveTo(previousGeometryPoint);
242
242
243 fullPath.cubicTo(cp1, cp2, currentGeometryPoint);
243 fullPath.cubicTo(cp1, cp2, currentGeometryPoint);
244 currentSegmentPath->cubicTo(cp1, cp2, currentGeometryPoint);
244 currentSegmentPath->cubicTo(cp1, cp2, currentGeometryPoint);
245 }
245 }
246 } else {
246 } else {
247 currentSegmentPath = 0;
247 currentSegmentPath = 0;
248 }
248 }
249
249
250 previousPointWasOffGrid = pointOffGrid;
250 previousPointWasOffGrid = pointOffGrid;
251 if (!pointOffGrid && m_pointsVisible && currentSeriesPoint.y() >= minY)
251 if (!pointOffGrid && m_pointsVisible && currentSeriesPoint.y() >= minY)
252 m_visiblePoints.append(currentGeometryPoint);
252 m_visiblePoints.append(currentGeometryPoint);
253 previousSegmentPath = currentSegmentPath;
253 previousSegmentPath = currentSegmentPath;
254 previousGeometryPoint = currentGeometryPoint;
254 previousGeometryPoint = currentGeometryPoint;
255 }
255 }
256
256
257 m_pathPolarRight = splinePathRight;
257 m_pathPolarRight = splinePathRight;
258 m_pathPolarLeft = splinePathLeft;
258 m_pathPolarLeft = splinePathLeft;
259 // Note: This construction of m_fullpath is not perfect. The partial segments that are
259 // Note: This construction of m_fullpath is not perfect. The partial segments that are
260 // outside left/right clip regions at axis boundary still generate hover/click events,
260 // outside left/right clip regions at axis boundary still generate hover/click events,
261 // because shape doesn't get clipped. It doesn't seem possible to do sensibly.
261 // because shape doesn't get clipped. It doesn't seem possible to do sensibly.
262 } else { // not polar
262 } else { // not polar
263 splinePath.moveTo(points.at(0));
263 splinePath.moveTo(points.at(0));
264 for (int i = 0; i < points.size() - 1; i++) {
264 for (int i = 0; i < points.size() - 1; i++) {
265 const QPointF &point = points.at(i + 1);
265 const QPointF &point = points.at(i + 1);
266 splinePath.cubicTo(controlPoints[2 * i], controlPoints[2 * i + 1], point);
266 splinePath.cubicTo(controlPoints[2 * i], controlPoints[2 * i + 1], point);
267 }
267 }
268 fullPath = splinePath;
268 fullPath = splinePath;
269 }
269 }
270 m_path = splinePath;
271
270
272 QPainterPathStroker stroker;
271 QPainterPathStroker stroker;
273 // The full path is comprised of three separate paths.
272 // The full path is comprised of three separate paths.
274 // This is why we are prepared for the "worst case" scenario, i.e. use always MiterJoin and
273 // This is why we are prepared for the "worst case" scenario, i.e. use always MiterJoin and
275 // multiply line width with square root of two when defining shape and bounding rectangle.
274 // multiply line width with square root of two when defining shape and bounding rectangle.
276 stroker.setWidth(margin);
275 stroker.setWidth(margin);
277 stroker.setJoinStyle(Qt::MiterJoin);
276 stroker.setJoinStyle(Qt::MiterJoin);
278 stroker.setCapStyle(Qt::SquareCap);
277 stroker.setCapStyle(Qt::SquareCap);
279 stroker.setMiterLimit(m_linePen.miterLimit());
278 stroker.setMiterLimit(m_linePen.miterLimit());
280
279
281 prepareGeometryChange();
280 // Only zoom in if the bounding rects of the path fit inside int limits. QWidget::update() uses
281 // a region that has to be compatible with QRect.
282 QPainterPath checkShapePath = stroker.createStroke(fullPath);
283 if (checkShapePath.boundingRect().height() <= INT_MAX
284 && checkShapePath.boundingRect().width() <= INT_MAX
285 && splinePath.boundingRect().height() <= INT_MAX
286 && splinePath.boundingRect().width() <= INT_MAX) {
287 m_path = splinePath;
282
288
283 m_fullPath = stroker.createStroke(fullPath);
289 prepareGeometryChange();
284 m_rect = m_fullPath.boundingRect();
290
291 m_fullPath = checkShapePath;
292 m_rect = m_fullPath.boundingRect();
293 }
285 }
294 }
286
295
287 /*!
296 /*!
288 Calculates control points which are needed by QPainterPath.cubicTo function to draw the cubic Bezier cureve between two points.
297 Calculates control points which are needed by QPainterPath.cubicTo function to draw the cubic Bezier cureve between two points.
289 */
298 */
290 QVector<QPointF> SplineChartItem::calculateControlPoints(const QVector<QPointF> &points)
299 QVector<QPointF> SplineChartItem::calculateControlPoints(const QVector<QPointF> &points)
291 {
300 {
292 QVector<QPointF> controlPoints;
301 QVector<QPointF> controlPoints;
293 controlPoints.resize(points.count() * 2 - 2);
302 controlPoints.resize(points.count() * 2 - 2);
294
303
295 int n = points.count() - 1;
304 int n = points.count() - 1;
296
305
297 if (n == 1) {
306 if (n == 1) {
298 //for n==1
307 //for n==1
299 controlPoints[0].setX((2 * points[0].x() + points[1].x()) / 3);
308 controlPoints[0].setX((2 * points[0].x() + points[1].x()) / 3);
300 controlPoints[0].setY((2 * points[0].y() + points[1].y()) / 3);
309 controlPoints[0].setY((2 * points[0].y() + points[1].y()) / 3);
301 controlPoints[1].setX(2 * controlPoints[0].x() - points[0].x());
310 controlPoints[1].setX(2 * controlPoints[0].x() - points[0].x());
302 controlPoints[1].setY(2 * controlPoints[0].y() - points[0].y());
311 controlPoints[1].setY(2 * controlPoints[0].y() - points[0].y());
303 return controlPoints;
312 return controlPoints;
304 }
313 }
305
314
306 // Calculate first Bezier control points
315 // Calculate first Bezier control points
307 // Set of equations for P0 to Pn points.
316 // Set of equations for P0 to Pn points.
308 //
317 //
309 // | 2 1 0 0 ... 0 0 0 ... 0 0 0 | | P1_1 | | P0 + 2 * P1 |
318 // | 2 1 0 0 ... 0 0 0 ... 0 0 0 | | P1_1 | | P0 + 2 * P1 |
310 // | 1 4 1 0 ... 0 0 0 ... 0 0 0 | | P1_2 | | 4 * P1 + 2 * P2 |
319 // | 1 4 1 0 ... 0 0 0 ... 0 0 0 | | P1_2 | | 4 * P1 + 2 * P2 |
311 // | 0 1 4 1 ... 0 0 0 ... 0 0 0 | | P1_3 | | 4 * P2 + 2 * P3 |
320 // | 0 1 4 1 ... 0 0 0 ... 0 0 0 | | P1_3 | | 4 * P2 + 2 * P3 |
312 // | . . . . . . . . . . . . | | ... | | ... |
321 // | . . . . . . . . . . . . | | ... | | ... |
313 // | 0 0 0 0 ... 1 4 1 ... 0 0 0 | * | P1_i | = | 4 * P(i-1) + 2 * Pi |
322 // | 0 0 0 0 ... 1 4 1 ... 0 0 0 | * | P1_i | = | 4 * P(i-1) + 2 * Pi |
314 // | . . . . . . . . . . . . | | ... | | ... |
323 // | . . . . . . . . . . . . | | ... | | ... |
315 // | 0 0 0 0 0 0 0 0 ... 1 4 1 | | P1_(n-1)| | 4 * P(n-2) + 2 * P(n-1) |
324 // | 0 0 0 0 0 0 0 0 ... 1 4 1 | | P1_(n-1)| | 4 * P(n-2) + 2 * P(n-1) |
316 // | 0 0 0 0 0 0 0 0 ... 0 2 7 | | P1_n | | 8 * P(n-1) + Pn |
325 // | 0 0 0 0 0 0 0 0 ... 0 2 7 | | P1_n | | 8 * P(n-1) + Pn |
317 //
326 //
318 QVector<qreal> vector;
327 QVector<qreal> vector;
319 vector.resize(n);
328 vector.resize(n);
320
329
321 vector[0] = points[0].x() + 2 * points[1].x();
330 vector[0] = points[0].x() + 2 * points[1].x();
322
331
323
332
324 for (int i = 1; i < n - 1; ++i)
333 for (int i = 1; i < n - 1; ++i)
325 vector[i] = 4 * points[i].x() + 2 * points[i + 1].x();
334 vector[i] = 4 * points[i].x() + 2 * points[i + 1].x();
326
335
327 vector[n - 1] = (8 * points[n - 1].x() + points[n].x()) / 2.0;
336 vector[n - 1] = (8 * points[n - 1].x() + points[n].x()) / 2.0;
328
337
329 QVector<qreal> xControl = firstControlPoints(vector);
338 QVector<qreal> xControl = firstControlPoints(vector);
330
339
331 vector[0] = points[0].y() + 2 * points[1].y();
340 vector[0] = points[0].y() + 2 * points[1].y();
332
341
333 for (int i = 1; i < n - 1; ++i)
342 for (int i = 1; i < n - 1; ++i)
334 vector[i] = 4 * points[i].y() + 2 * points[i + 1].y();
343 vector[i] = 4 * points[i].y() + 2 * points[i + 1].y();
335
344
336 vector[n - 1] = (8 * points[n - 1].y() + points[n].y()) / 2.0;
345 vector[n - 1] = (8 * points[n - 1].y() + points[n].y()) / 2.0;
337
346
338 QVector<qreal> yControl = firstControlPoints(vector);
347 QVector<qreal> yControl = firstControlPoints(vector);
339
348
340 for (int i = 0, j = 0; i < n; ++i, ++j) {
349 for (int i = 0, j = 0; i < n; ++i, ++j) {
341
350
342 controlPoints[j].setX(xControl[i]);
351 controlPoints[j].setX(xControl[i]);
343 controlPoints[j].setY(yControl[i]);
352 controlPoints[j].setY(yControl[i]);
344
353
345 j++;
354 j++;
346
355
347 if (i < n - 1) {
356 if (i < n - 1) {
348 controlPoints[j].setX(2 * points[i + 1].x() - xControl[i + 1]);
357 controlPoints[j].setX(2 * points[i + 1].x() - xControl[i + 1]);
349 controlPoints[j].setY(2 * points[i + 1].y() - yControl[i + 1]);
358 controlPoints[j].setY(2 * points[i + 1].y() - yControl[i + 1]);
350 } else {
359 } else {
351 controlPoints[j].setX((points[n].x() + xControl[n - 1]) / 2);
360 controlPoints[j].setX((points[n].x() + xControl[n - 1]) / 2);
352 controlPoints[j].setY((points[n].y() + yControl[n - 1]) / 2);
361 controlPoints[j].setY((points[n].y() + yControl[n - 1]) / 2);
353 }
362 }
354 }
363 }
355 return controlPoints;
364 return controlPoints;
356 }
365 }
357
366
358 QVector<qreal> SplineChartItem::firstControlPoints(const QVector<qreal>& vector)
367 QVector<qreal> SplineChartItem::firstControlPoints(const QVector<qreal>& vector)
359 {
368 {
360 QVector<qreal> result;
369 QVector<qreal> result;
361
370
362 int count = vector.count();
371 int count = vector.count();
363 result.resize(count);
372 result.resize(count);
364 result[0] = vector[0] / 2.0;
373 result[0] = vector[0] / 2.0;
365
374
366 QVector<qreal> temp;
375 QVector<qreal> temp;
367 temp.resize(count);
376 temp.resize(count);
368 temp[0] = 0;
377 temp[0] = 0;
369
378
370 qreal b = 2.0;
379 qreal b = 2.0;
371
380
372 for (int i = 1; i < count; i++) {
381 for (int i = 1; i < count; i++) {
373 temp[i] = 1 / b;
382 temp[i] = 1 / b;
374 b = (i < count - 1 ? 4.0 : 3.5) - temp[i];
383 b = (i < count - 1 ? 4.0 : 3.5) - temp[i];
375 result[i] = (vector[i] - result[i - 1]) / b;
384 result[i] = (vector[i] - result[i - 1]) / b;
376 }
385 }
377
386
378 for (int i = 1; i < count; i++)
387 for (int i = 1; i < count; i++)
379 result[count - i - 1] -= temp[count - i] * result[count - i];
388 result[count - i - 1] -= temp[count - i] * result[count - i];
380
389
381 return result;
390 return result;
382 }
391 }
383
392
384 //handlers
393 //handlers
385
394
386 void SplineChartItem::handleUpdated()
395 void SplineChartItem::handleUpdated()
387 {
396 {
388 setVisible(m_series->isVisible());
397 setVisible(m_series->isVisible());
389 setOpacity(m_series->opacity());
398 setOpacity(m_series->opacity());
390 m_pointsVisible = m_series->pointsVisible();
399 m_pointsVisible = m_series->pointsVisible();
391 m_linePen = m_series->pen();
400 m_linePen = m_series->pen();
392 m_pointPen = m_series->pen();
401 m_pointPen = m_series->pen();
393 m_pointPen.setWidthF(2 * m_pointPen.width());
402 m_pointPen.setWidthF(2 * m_pointPen.width());
394 update();
403 update();
395 }
404 }
396
405
397 //painter
406 //painter
398
407
399 void SplineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
408 void SplineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
400 {
409 {
401 Q_UNUSED(widget)
410 Q_UNUSED(widget)
402 Q_UNUSED(option)
411 Q_UNUSED(option)
403
412
404 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
413 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
405
414
406 painter->save();
415 painter->save();
407 painter->setPen(m_linePen);
416 painter->setPen(m_linePen);
408 painter->setBrush(Qt::NoBrush);
417 painter->setBrush(Qt::NoBrush);
409
418
410 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
419 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
411 qreal halfWidth = domain()->size().width() / 2.0;
420 qreal halfWidth = domain()->size().width() / 2.0;
412 QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
421 QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
413 QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
422 QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
414 QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
423 QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
415 QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
424 QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
416 QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
425 QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
417 painter->setClipRegion(clipRegionLeft);
426 painter->setClipRegion(clipRegionLeft);
418 painter->drawPath(m_pathPolarLeft);
427 painter->drawPath(m_pathPolarLeft);
419 painter->setClipRegion(clipRegionRight);
428 painter->setClipRegion(clipRegionRight);
420 painter->drawPath(m_pathPolarRight);
429 painter->drawPath(m_pathPolarRight);
421 painter->setClipRegion(fullPolarClipRegion);
430 painter->setClipRegion(fullPolarClipRegion);
422 } else {
431 } else {
423 painter->setClipRect(clipRect);
432 painter->setClipRect(clipRect);
424 }
433 }
425
434
426 painter->drawPath(m_path);
435 painter->drawPath(m_path);
427
436
428 if (m_pointsVisible) {
437 if (m_pointsVisible) {
429 painter->setPen(m_pointPen);
438 painter->setPen(m_pointPen);
430 if (m_series->chart()->chartType() == QChart::ChartTypePolar)
439 if (m_series->chart()->chartType() == QChart::ChartTypePolar)
431 painter->drawPoints(m_visiblePoints);
440 painter->drawPoints(m_visiblePoints);
432 else
441 else
433 painter->drawPoints(geometryPoints());
442 painter->drawPoints(geometryPoints());
434 }
443 }
435
444
436 painter->restore();
445 painter->restore();
437 }
446 }
438
447
439 void SplineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
448 void SplineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
440 {
449 {
441 emit XYChart::clicked(domain()->calculateDomainPoint(event->pos()));
450 emit XYChart::clicked(domain()->calculateDomainPoint(event->pos()));
442 QGraphicsItem::mousePressEvent(event);
451 QGraphicsItem::mousePressEvent(event);
443 }
452 }
444
453
445 void SplineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
454 void SplineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
446 {
455 {
447 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
456 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
448 QGraphicsItem::hoverEnterEvent(event);
457 QGraphicsItem::hoverEnterEvent(event);
449 }
458 }
450
459
451 void SplineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
460 void SplineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
452 {
461 {
453 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
462 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
454 QGraphicsItem::hoverLeaveEvent(event);
463 QGraphicsItem::hoverLeaveEvent(event);
455 }
464 }
456
465
457 #include "moc_splinechartitem_p.cpp"
466 #include "moc_splinechartitem_p.cpp"
458
467
459 QTCOMMERCIALCHART_END_NAMESPACE
468 QTCOMMERCIALCHART_END_NAMESPACE
General Comments 0
You need to be logged in to leave comments. Login now