##// END OF EJS Templates
Refactor the new QXYSeries::pointAt() -> QXYSeries::at()...
Miikka Heikkinen -
r2491:d86f47a4910b
parent child
Show More
@@ -1,372 +1,372
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 Commercial Charts Add-on.
7 ** This file is part of the Qt Commercial Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Commercial licenses may use this file in
10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 ** accordance with the Qt Commercial License Agreement provided with the
11 ** accordance with the Qt Commercial 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->pointAt(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->pointAt(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->pointAt(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 prepareGeometryChange();
280
280
281 m_linePath = linePath;
281 m_linePath = linePath;
282 m_fullPath = fullPath;
282 m_fullPath = fullPath;
283 m_shapePath = stroker.createStroke(fullPath);
283 m_shapePath = stroker.createStroke(fullPath);
284
284
285 m_rect = m_shapePath.boundingRect();
285 m_rect = m_shapePath.boundingRect();
286 }
286 }
287
287
288 void LineChartItem::handleUpdated()
288 void LineChartItem::handleUpdated()
289 {
289 {
290 // If points visiblity has changed, a geometry update is needed.
290 // If points visiblity has changed, a geometry update is needed.
291 // Also, if pen changes when points are visible, geometry update is needed.
291 // Also, if pen changes when points are visible, geometry update is needed.
292 bool doGeometryUpdate =
292 bool doGeometryUpdate =
293 (m_pointsVisible != m_series->pointsVisible())
293 (m_pointsVisible != m_series->pointsVisible())
294 || (m_series->pointsVisible() && (m_linePen != m_series->pen()));
294 || (m_series->pointsVisible() && (m_linePen != m_series->pen()));
295 setVisible(m_series->isVisible());
295 setVisible(m_series->isVisible());
296 setOpacity(m_series->opacity());
296 setOpacity(m_series->opacity());
297 m_pointsVisible = m_series->pointsVisible();
297 m_pointsVisible = m_series->pointsVisible();
298 m_linePen = m_series->pen();
298 m_linePen = m_series->pen();
299 if (doGeometryUpdate)
299 if (doGeometryUpdate)
300 updateGeometry();
300 updateGeometry();
301 update();
301 update();
302 }
302 }
303
303
304 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
304 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
305 {
305 {
306 Q_UNUSED(widget)
306 Q_UNUSED(widget)
307 Q_UNUSED(option)
307 Q_UNUSED(option)
308
308
309 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
309 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
310
310
311 painter->save();
311 painter->save();
312 painter->setPen(m_linePen);
312 painter->setPen(m_linePen);
313 bool alwaysUsePath = false;
313 bool alwaysUsePath = false;
314
314
315 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
315 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
316 qreal halfWidth = domain()->size().width() / 2.0;
316 qreal halfWidth = domain()->size().width() / 2.0;
317 QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
317 QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
318 QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
318 QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
319 QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
319 QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
320 QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
320 QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
321 QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
321 QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
322 painter->setClipRegion(clipRegionLeft);
322 painter->setClipRegion(clipRegionLeft);
323 painter->drawPath(m_linePathPolarLeft);
323 painter->drawPath(m_linePathPolarLeft);
324 painter->setClipRegion(clipRegionRight);
324 painter->setClipRegion(clipRegionRight);
325 painter->drawPath(m_linePathPolarRight);
325 painter->drawPath(m_linePathPolarRight);
326 painter->setClipRegion(fullPolarClipRegion);
326 painter->setClipRegion(fullPolarClipRegion);
327 alwaysUsePath = true; // required for proper clipping
327 alwaysUsePath = true; // required for proper clipping
328 } else {
328 } else {
329 painter->setClipRect(clipRect);
329 painter->setClipRect(clipRect);
330 }
330 }
331
331
332 if (m_pointsVisible) {
332 if (m_pointsVisible) {
333 painter->setBrush(m_linePen.color());
333 painter->setBrush(m_linePen.color());
334 painter->drawPath(m_linePath);
334 painter->drawPath(m_linePath);
335 } else {
335 } else {
336 painter->setBrush(QBrush(Qt::NoBrush));
336 painter->setBrush(QBrush(Qt::NoBrush));
337 if (m_linePen.style() != Qt::SolidLine || alwaysUsePath) {
337 if (m_linePen.style() != Qt::SolidLine || alwaysUsePath) {
338 // If pen style is not solid line, always fall back to path painting
338 // If pen style is not solid line, always fall back to path painting
339 // to ensure proper continuity of the pattern
339 // to ensure proper continuity of the pattern
340 painter->drawPath(m_linePath);
340 painter->drawPath(m_linePath);
341 } else {
341 } else {
342 for (int i(1); i < m_points.size(); i++)
342 for (int i(1); i < m_points.size(); i++)
343 painter->drawLine(m_points.at(i - 1), m_points.at(i));
343 painter->drawLine(m_points.at(i - 1), m_points.at(i));
344 }
344 }
345 }
345 }
346
346
347 painter->restore();
347 painter->restore();
348 }
348 }
349
349
350 void LineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
350 void LineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
351 {
351 {
352 emit XYChart::clicked(domain()->calculateDomainPoint(event->pos()));
352 emit XYChart::clicked(domain()->calculateDomainPoint(event->pos()));
353 QGraphicsItem::mousePressEvent(event);
353 QGraphicsItem::mousePressEvent(event);
354 }
354 }
355
355
356 void LineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
356 void LineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
357 {
357 {
358 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
358 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
359 // event->accept();
359 // event->accept();
360 QGraphicsItem::hoverEnterEvent(event);
360 QGraphicsItem::hoverEnterEvent(event);
361 }
361 }
362
362
363 void LineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
363 void LineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
364 {
364 {
365 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
365 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
366 // event->accept();
366 // event->accept();
367 QGraphicsItem::hoverEnterEvent(event);
367 QGraphicsItem::hoverEnterEvent(event);
368 }
368 }
369
369
370 #include "moc_linechartitem_p.cpp"
370 #include "moc_linechartitem_p.cpp"
371
371
372 QTCOMMERCIALCHART_END_NAMESPACE
372 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,207 +1,207
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 Commercial Charts Add-on.
7 ** This file is part of the Qt Commercial Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Commercial licenses may use this file in
10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 ** accordance with the Qt Commercial License Agreement provided with the
11 ** accordance with the Qt Commercial 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 QVector<bool> offGridStatus = offGridStatusVector();
130 const int seriesLastIndex = m_series->count() - 1;
130 const int seriesLastIndex = m_series->count() - 1;
131
131
132 for (int i = 0; i < points.size(); i++) {
132 for (int i = 0; i < points.size(); i++) {
133 QGraphicsItem *item = items.at(i);
133 QGraphicsItem *item = items.at(i);
134 const QPointF &point = points.at(i);
134 const QPointF &point = points.at(i);
135 const QRectF &rect = item->boundingRect();
135 const QRectF &rect = item->boundingRect();
136 // During remove/append animation series may have different number of points,
136 // During remove/append animation series may have different number of points,
137 // so ensure we don't go over the index. Animation handling itself ensures that
137 // so ensure we don't go over the index. Animation handling itself ensures that
138 // if there is actually no points in the series, then it won't generate a fake point,
138 // if there is actually no points in the series, then it won't generate a fake point,
139 // so we can be assured there is always at least one point in m_series here.
139 // so we can be assured there is always at least one point in m_series here.
140 // Note that marker map values can be technically incorrect during the animation,
140 // Note that marker map values can be technically incorrect during the animation,
141 // if it was caused by an insert, but this shouldn't be a problem as the points are
141 // if it was caused by an insert, but this shouldn't be a problem as the points are
142 // fake anyway.
142 // fake anyway.
143 m_markerMap[item] = m_series->pointAt(qMin(seriesLastIndex, i));
143 m_markerMap[item] = m_series->at(qMin(seriesLastIndex, i));
144 item->setPos(point.x() - rect.width() / 2, point.y() - rect.height() / 2);
144 item->setPos(point.x() - rect.width() / 2, point.y() - rect.height() / 2);
145
145
146 if (!m_visible || offGridStatus.at(i))
146 if (!m_visible || offGridStatus.at(i))
147 item->setVisible(false);
147 item->setVisible(false);
148 else
148 else
149 item->setVisible(true);
149 item->setVisible(true);
150 }
150 }
151
151
152 prepareGeometryChange();
152 prepareGeometryChange();
153 m_rect = clipRect;
153 m_rect = clipRect;
154 }
154 }
155
155
156 void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
156 void ScatterChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
157 {
157 {
158 Q_UNUSED(painter)
158 Q_UNUSED(painter)
159 Q_UNUSED(option)
159 Q_UNUSED(option)
160 Q_UNUSED(widget)
160 Q_UNUSED(widget)
161 }
161 }
162
162
163 void ScatterChartItem::setPen(const QPen &pen)
163 void ScatterChartItem::setPen(const QPen &pen)
164 {
164 {
165 foreach (QGraphicsItem *item , m_items.childItems())
165 foreach (QGraphicsItem *item , m_items.childItems())
166 static_cast<QAbstractGraphicsShapeItem*>(item)->setPen(pen);
166 static_cast<QAbstractGraphicsShapeItem*>(item)->setPen(pen);
167 }
167 }
168
168
169 void ScatterChartItem::setBrush(const QBrush &brush)
169 void ScatterChartItem::setBrush(const QBrush &brush)
170 {
170 {
171 foreach (QGraphicsItem *item , m_items.childItems())
171 foreach (QGraphicsItem *item , m_items.childItems())
172 static_cast<QAbstractGraphicsShapeItem*>(item)->setBrush(brush);
172 static_cast<QAbstractGraphicsShapeItem*>(item)->setBrush(brush);
173 }
173 }
174
174
175 void ScatterChartItem::handleUpdated()
175 void ScatterChartItem::handleUpdated()
176 {
176 {
177 int count = m_items.childItems().count();
177 int count = m_items.childItems().count();
178
178
179 if (count == 0)
179 if (count == 0)
180 return;
180 return;
181
181
182 bool recreate = m_visible != m_series->isVisible()
182 bool recreate = m_visible != m_series->isVisible()
183 || m_size != m_series->markerSize()
183 || m_size != m_series->markerSize()
184 || m_shape != m_series->markerShape();
184 || m_shape != m_series->markerShape();
185
185
186 m_visible = m_series->isVisible();
186 m_visible = m_series->isVisible();
187 m_size = m_series->markerSize();
187 m_size = m_series->markerSize();
188 m_shape = m_series->markerShape();
188 m_shape = m_series->markerShape();
189 setOpacity(m_series->opacity());
189 setOpacity(m_series->opacity());
190
190
191 if (recreate) {
191 if (recreate) {
192 // TODO: optimize handleUpdate to recreate points only in case shape changed
192 // TODO: optimize handleUpdate to recreate points only in case shape changed
193 deletePoints(count);
193 deletePoints(count);
194 createPoints(count);
194 createPoints(count);
195
195
196 // Updating geometry is now safe, because it won't call handleUpdated unless it creates/deletes points
196 // Updating geometry is now safe, because it won't call handleUpdated unless it creates/deletes points
197 updateGeometry();
197 updateGeometry();
198 }
198 }
199
199
200 setPen(m_series->pen());
200 setPen(m_series->pen());
201 setBrush(m_series->brush());
201 setBrush(m_series->brush());
202 update();
202 update();
203 }
203 }
204
204
205 #include "moc_scatterchartitem_p.cpp"
205 #include "moc_scatterchartitem_p.cpp"
206
206
207 QTCOMMERCIALCHART_END_NAMESPACE
207 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,459 +1,459
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 Commercial Charts Add-on.
7 ** This file is part of the Qt Commercial Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Commercial licenses may use this file in
10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 ** accordance with the Qt Commercial License Agreement provided with the
11 ** accordance with the Qt Commercial 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->pointAt(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->pointAt(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->pointAt(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;
270 m_path = splinePath;
271
271
272 QPainterPathStroker stroker;
272 QPainterPathStroker stroker;
273 // The full path is comprised of three separate paths.
273 // 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
274 // 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.
275 // multiply line width with square root of two when defining shape and bounding rectangle.
276 stroker.setWidth(margin);
276 stroker.setWidth(margin);
277 stroker.setJoinStyle(Qt::MiterJoin);
277 stroker.setJoinStyle(Qt::MiterJoin);
278 stroker.setCapStyle(Qt::SquareCap);
278 stroker.setCapStyle(Qt::SquareCap);
279 stroker.setMiterLimit(m_linePen.miterLimit());
279 stroker.setMiterLimit(m_linePen.miterLimit());
280
280
281 prepareGeometryChange();
281 prepareGeometryChange();
282
282
283 m_fullPath = stroker.createStroke(fullPath);
283 m_fullPath = stroker.createStroke(fullPath);
284 m_rect = m_fullPath.boundingRect();
284 m_rect = m_fullPath.boundingRect();
285 }
285 }
286
286
287 /*!
287 /*!
288 Calculates control points which are needed by QPainterPath.cubicTo function to draw the cubic Bezier cureve between two points.
288 Calculates control points which are needed by QPainterPath.cubicTo function to draw the cubic Bezier cureve between two points.
289 */
289 */
290 QVector<QPointF> SplineChartItem::calculateControlPoints(const QVector<QPointF> &points)
290 QVector<QPointF> SplineChartItem::calculateControlPoints(const QVector<QPointF> &points)
291 {
291 {
292 QVector<QPointF> controlPoints;
292 QVector<QPointF> controlPoints;
293 controlPoints.resize(points.count() * 2 - 2);
293 controlPoints.resize(points.count() * 2 - 2);
294
294
295 int n = points.count() - 1;
295 int n = points.count() - 1;
296
296
297 if (n == 1) {
297 if (n == 1) {
298 //for n==1
298 //for n==1
299 controlPoints[0].setX((2 * points[0].x() + points[1].x()) / 3);
299 controlPoints[0].setX((2 * points[0].x() + points[1].x()) / 3);
300 controlPoints[0].setY((2 * points[0].y() + points[1].y()) / 3);
300 controlPoints[0].setY((2 * points[0].y() + points[1].y()) / 3);
301 controlPoints[1].setX(2 * controlPoints[0].x() - points[0].x());
301 controlPoints[1].setX(2 * controlPoints[0].x() - points[0].x());
302 controlPoints[1].setY(2 * controlPoints[0].y() - points[0].y());
302 controlPoints[1].setY(2 * controlPoints[0].y() - points[0].y());
303 return controlPoints;
303 return controlPoints;
304 }
304 }
305
305
306 // Calculate first Bezier control points
306 // Calculate first Bezier control points
307 // Set of equations for P0 to Pn points.
307 // Set of equations for P0 to Pn points.
308 //
308 //
309 // | 2 1 0 0 ... 0 0 0 ... 0 0 0 | | P1_1 | | P0 + 2 * P1 |
309 // | 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 |
310 // | 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 |
311 // | 0 1 4 1 ... 0 0 0 ... 0 0 0 | | P1_3 | | 4 * P2 + 2 * P3 |
312 // | . . . . . . . . . . . . | | ... | | ... |
312 // | . . . . . . . . . . . . | | ... | | ... |
313 // | 0 0 0 0 ... 1 4 1 ... 0 0 0 | * | P1_i | = | 4 * P(i-1) + 2 * Pi |
313 // | 0 0 0 0 ... 1 4 1 ... 0 0 0 | * | P1_i | = | 4 * P(i-1) + 2 * Pi |
314 // | . . . . . . . . . . . . | | ... | | ... |
314 // | . . . . . . . . . . . . | | ... | | ... |
315 // | 0 0 0 0 0 0 0 0 ... 1 4 1 | | P1_(n-1)| | 4 * P(n-2) + 2 * P(n-1) |
315 // | 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 |
316 // | 0 0 0 0 0 0 0 0 ... 0 2 7 | | P1_n | | 8 * P(n-1) + Pn |
317 //
317 //
318 QVector<qreal> vector;
318 QVector<qreal> vector;
319 vector.resize(n);
319 vector.resize(n);
320
320
321 vector[0] = points[0].x() + 2 * points[1].x();
321 vector[0] = points[0].x() + 2 * points[1].x();
322
322
323
323
324 for (int i = 1; i < n - 1; ++i)
324 for (int i = 1; i < n - 1; ++i)
325 vector[i] = 4 * points[i].x() + 2 * points[i + 1].x();
325 vector[i] = 4 * points[i].x() + 2 * points[i + 1].x();
326
326
327 vector[n - 1] = (8 * points[n - 1].x() + points[n].x()) / 2.0;
327 vector[n - 1] = (8 * points[n - 1].x() + points[n].x()) / 2.0;
328
328
329 QVector<qreal> xControl = firstControlPoints(vector);
329 QVector<qreal> xControl = firstControlPoints(vector);
330
330
331 vector[0] = points[0].y() + 2 * points[1].y();
331 vector[0] = points[0].y() + 2 * points[1].y();
332
332
333 for (int i = 1; i < n - 1; ++i)
333 for (int i = 1; i < n - 1; ++i)
334 vector[i] = 4 * points[i].y() + 2 * points[i + 1].y();
334 vector[i] = 4 * points[i].y() + 2 * points[i + 1].y();
335
335
336 vector[n - 1] = (8 * points[n - 1].y() + points[n].y()) / 2.0;
336 vector[n - 1] = (8 * points[n - 1].y() + points[n].y()) / 2.0;
337
337
338 QVector<qreal> yControl = firstControlPoints(vector);
338 QVector<qreal> yControl = firstControlPoints(vector);
339
339
340 for (int i = 0, j = 0; i < n; ++i, ++j) {
340 for (int i = 0, j = 0; i < n; ++i, ++j) {
341
341
342 controlPoints[j].setX(xControl[i]);
342 controlPoints[j].setX(xControl[i]);
343 controlPoints[j].setY(yControl[i]);
343 controlPoints[j].setY(yControl[i]);
344
344
345 j++;
345 j++;
346
346
347 if (i < n - 1) {
347 if (i < n - 1) {
348 controlPoints[j].setX(2 * points[i + 1].x() - xControl[i + 1]);
348 controlPoints[j].setX(2 * points[i + 1].x() - xControl[i + 1]);
349 controlPoints[j].setY(2 * points[i + 1].y() - yControl[i + 1]);
349 controlPoints[j].setY(2 * points[i + 1].y() - yControl[i + 1]);
350 } else {
350 } else {
351 controlPoints[j].setX((points[n].x() + xControl[n - 1]) / 2);
351 controlPoints[j].setX((points[n].x() + xControl[n - 1]) / 2);
352 controlPoints[j].setY((points[n].y() + yControl[n - 1]) / 2);
352 controlPoints[j].setY((points[n].y() + yControl[n - 1]) / 2);
353 }
353 }
354 }
354 }
355 return controlPoints;
355 return controlPoints;
356 }
356 }
357
357
358 QVector<qreal> SplineChartItem::firstControlPoints(const QVector<qreal>& vector)
358 QVector<qreal> SplineChartItem::firstControlPoints(const QVector<qreal>& vector)
359 {
359 {
360 QVector<qreal> result;
360 QVector<qreal> result;
361
361
362 int count = vector.count();
362 int count = vector.count();
363 result.resize(count);
363 result.resize(count);
364 result[0] = vector[0] / 2.0;
364 result[0] = vector[0] / 2.0;
365
365
366 QVector<qreal> temp;
366 QVector<qreal> temp;
367 temp.resize(count);
367 temp.resize(count);
368 temp[0] = 0;
368 temp[0] = 0;
369
369
370 qreal b = 2.0;
370 qreal b = 2.0;
371
371
372 for (int i = 1; i < count; i++) {
372 for (int i = 1; i < count; i++) {
373 temp[i] = 1 / b;
373 temp[i] = 1 / b;
374 b = (i < count - 1 ? 4.0 : 3.5) - temp[i];
374 b = (i < count - 1 ? 4.0 : 3.5) - temp[i];
375 result[i] = (vector[i] - result[i - 1]) / b;
375 result[i] = (vector[i] - result[i - 1]) / b;
376 }
376 }
377
377
378 for (int i = 1; i < count; i++)
378 for (int i = 1; i < count; i++)
379 result[count - i - 1] -= temp[count - i] * result[count - i];
379 result[count - i - 1] -= temp[count - i] * result[count - i];
380
380
381 return result;
381 return result;
382 }
382 }
383
383
384 //handlers
384 //handlers
385
385
386 void SplineChartItem::handleUpdated()
386 void SplineChartItem::handleUpdated()
387 {
387 {
388 setVisible(m_series->isVisible());
388 setVisible(m_series->isVisible());
389 setOpacity(m_series->opacity());
389 setOpacity(m_series->opacity());
390 m_pointsVisible = m_series->pointsVisible();
390 m_pointsVisible = m_series->pointsVisible();
391 m_linePen = m_series->pen();
391 m_linePen = m_series->pen();
392 m_pointPen = m_series->pen();
392 m_pointPen = m_series->pen();
393 m_pointPen.setWidthF(2 * m_pointPen.width());
393 m_pointPen.setWidthF(2 * m_pointPen.width());
394 update();
394 update();
395 }
395 }
396
396
397 //painter
397 //painter
398
398
399 void SplineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
399 void SplineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
400 {
400 {
401 Q_UNUSED(widget)
401 Q_UNUSED(widget)
402 Q_UNUSED(option)
402 Q_UNUSED(option)
403
403
404 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
404 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
405
405
406 painter->save();
406 painter->save();
407 painter->setPen(m_linePen);
407 painter->setPen(m_linePen);
408 painter->setBrush(Qt::NoBrush);
408 painter->setBrush(Qt::NoBrush);
409
409
410 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
410 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
411 qreal halfWidth = domain()->size().width() / 2.0;
411 qreal halfWidth = domain()->size().width() / 2.0;
412 QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
412 QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
413 QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
413 QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
414 QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
414 QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
415 QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
415 QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
416 QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
416 QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
417 painter->setClipRegion(clipRegionLeft);
417 painter->setClipRegion(clipRegionLeft);
418 painter->drawPath(m_pathPolarLeft);
418 painter->drawPath(m_pathPolarLeft);
419 painter->setClipRegion(clipRegionRight);
419 painter->setClipRegion(clipRegionRight);
420 painter->drawPath(m_pathPolarRight);
420 painter->drawPath(m_pathPolarRight);
421 painter->setClipRegion(fullPolarClipRegion);
421 painter->setClipRegion(fullPolarClipRegion);
422 } else {
422 } else {
423 painter->setClipRect(clipRect);
423 painter->setClipRect(clipRect);
424 }
424 }
425
425
426 painter->drawPath(m_path);
426 painter->drawPath(m_path);
427
427
428 if (m_pointsVisible) {
428 if (m_pointsVisible) {
429 painter->setPen(m_pointPen);
429 painter->setPen(m_pointPen);
430 if (m_series->chart()->chartType() == QChart::ChartTypePolar)
430 if (m_series->chart()->chartType() == QChart::ChartTypePolar)
431 painter->drawPoints(m_visiblePoints);
431 painter->drawPoints(m_visiblePoints);
432 else
432 else
433 painter->drawPoints(geometryPoints());
433 painter->drawPoints(geometryPoints());
434 }
434 }
435
435
436 painter->restore();
436 painter->restore();
437 }
437 }
438
438
439 void SplineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
439 void SplineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
440 {
440 {
441 emit XYChart::clicked(domain()->calculateDomainPoint(event->pos()));
441 emit XYChart::clicked(domain()->calculateDomainPoint(event->pos()));
442 QGraphicsItem::mousePressEvent(event);
442 QGraphicsItem::mousePressEvent(event);
443 }
443 }
444
444
445 void SplineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
445 void SplineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
446 {
446 {
447 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
447 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
448 QGraphicsItem::hoverEnterEvent(event);
448 QGraphicsItem::hoverEnterEvent(event);
449 }
449 }
450
450
451 void SplineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
451 void SplineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
452 {
452 {
453 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
453 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
454 QGraphicsItem::hoverLeaveEvent(event);
454 QGraphicsItem::hoverLeaveEvent(event);
455 }
455 }
456
456
457 #include "moc_splinechartitem_p.cpp"
457 #include "moc_splinechartitem_p.cpp"
458
458
459 QTCOMMERCIALCHART_END_NAMESPACE
459 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,560 +1,560
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 Commercial Charts Add-on.
7 ** This file is part of the Qt Commercial Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Commercial licenses may use this file in
10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 ** accordance with the Qt Commercial License Agreement provided with the
11 ** accordance with the Qt Commercial 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 "qxyseries.h"
21 #include "qxyseries.h"
22 #include "qxyseries_p.h"
22 #include "qxyseries_p.h"
23 #include "abstractdomain_p.h"
23 #include "abstractdomain_p.h"
24 #include "qvalueaxis.h"
24 #include "qvalueaxis.h"
25 #include "xychart_p.h"
25 #include "xychart_p.h"
26 #include "qxylegendmarker.h"
26 #include "qxylegendmarker.h"
27 #include "charthelpers_p.h"
27 #include "charthelpers_p.h"
28
28
29 QTCOMMERCIALCHART_BEGIN_NAMESPACE
29 QTCOMMERCIALCHART_BEGIN_NAMESPACE
30
30
31 /*!
31 /*!
32 \class QXYSeries
32 \class QXYSeries
33 \brief The QXYSeries class is a base class for line, spline and scatter series.
33 \brief The QXYSeries class is a base class for line, spline and scatter series.
34 */
34 */
35 /*!
35 /*!
36 \qmlclass XYSeries
36 \qmlclass XYSeries
37 \inherits AbstractSeries
37 \inherits AbstractSeries
38 The XYSeries class is a base class for line, spline and scatter series.
38 The XYSeries class is a base class for line, spline and scatter series.
39
39
40 The class cannot be instantiated directly.
40 The class cannot be instantiated directly.
41 */
41 */
42
42
43 /*!
43 /*!
44 \qmlproperty AbstractAxis XYSeries::axisX
44 \qmlproperty AbstractAxis XYSeries::axisX
45 The x axis used for the series. If you leave both axisX and axisXTop undefined, a ValueAxis is created for
45 The x axis used for the series. If you leave both axisX and axisXTop undefined, a ValueAxis is created for
46 the series.
46 the series.
47 \sa axisXTop
47 \sa axisXTop
48 */
48 */
49
49
50 /*!
50 /*!
51 \qmlproperty AbstractAxis XYSeries::axisY
51 \qmlproperty AbstractAxis XYSeries::axisY
52 The y axis used for the series. If you leave both axisY and axisYRight undefined, a ValueAxis is created for
52 The y axis used for the series. If you leave both axisY and axisYRight undefined, a ValueAxis is created for
53 the series.
53 the series.
54 \sa axisYRight
54 \sa axisYRight
55 */
55 */
56
56
57 /*!
57 /*!
58 \qmlproperty AbstractAxis XYSeries::axisXTop
58 \qmlproperty AbstractAxis XYSeries::axisXTop
59 The x axis used for the series, drawn on top of the chart view. Note that you can only provide either axisX or
59 The x axis used for the series, drawn on top of the chart view. Note that you can only provide either axisX or
60 axisXTop, but not both.
60 axisXTop, but not both.
61 \sa axisX
61 \sa axisX
62 */
62 */
63
63
64 /*!
64 /*!
65 \qmlproperty AbstractAxis XYSeries::axisYRight
65 \qmlproperty AbstractAxis XYSeries::axisYRight
66 The y axis used for the series, drawn to the right on the chart view. Note that you can only provide either axisY
66 The y axis used for the series, drawn to the right on the chart view. Note that you can only provide either axisY
67 or axisYRight, but not both.
67 or axisYRight, but not both.
68 \sa axisY
68 \sa axisY
69 */
69 */
70
70
71 /*!
71 /*!
72 \qmlproperty AbstractAxis XYSeries::axisAngular
72 \qmlproperty AbstractAxis XYSeries::axisAngular
73 The angular axis used for the series, drawn around the polar chart view.
73 The angular axis used for the series, drawn around the polar chart view.
74 \sa axisX
74 \sa axisX
75 */
75 */
76
76
77 /*!
77 /*!
78 \qmlproperty AbstractAxis XYSeries::axisRadial
78 \qmlproperty AbstractAxis XYSeries::axisRadial
79 The radial axis used for the series, drawn inside the polar chart view.
79 The radial axis used for the series, drawn inside the polar chart view.
80 \sa axisY
80 \sa axisY
81 */
81 */
82
82
83 /*!
83 /*!
84 \property QXYSeries::pointsVisible
84 \property QXYSeries::pointsVisible
85 Controls if the data points are visible and should be drawn.
85 Controls if the data points are visible and should be drawn.
86 */
86 */
87 /*!
87 /*!
88 \qmlproperty bool XYSeries::pointsVisible
88 \qmlproperty bool XYSeries::pointsVisible
89 Controls if the data points are visible and should be drawn.
89 Controls if the data points are visible and should be drawn.
90 */
90 */
91
91
92 /*!
92 /*!
93 \fn QPen QXYSeries::pen() const
93 \fn QPen QXYSeries::pen() const
94 \brief Returns pen used to draw points for series.
94 \brief Returns pen used to draw points for series.
95 \sa setPen()
95 \sa setPen()
96 */
96 */
97
97
98 /*!
98 /*!
99 \fn QBrush QXYSeries::brush() const
99 \fn QBrush QXYSeries::brush() const
100 \brief Returns brush used to draw points for series.
100 \brief Returns brush used to draw points for series.
101 \sa setBrush()
101 \sa setBrush()
102 */
102 */
103
103
104 /*!
104 /*!
105 \property QXYSeries::color
105 \property QXYSeries::color
106 The color of the series. This is line (pen) color in case of QLineSeries or QSplineSeries and
106 The color of the series. This is line (pen) color in case of QLineSeries or QSplineSeries and
107 fill (brush) color in case of QScatterSeries or QAreaSeries.
107 fill (brush) color in case of QScatterSeries or QAreaSeries.
108 \sa QXYSeries::pen(), QXYSeries::brush()
108 \sa QXYSeries::pen(), QXYSeries::brush()
109 */
109 */
110 /*!
110 /*!
111 \qmlproperty color XYSeries::color
111 \qmlproperty color XYSeries::color
112 The color of the series. This is line (pen) color in case of LineSeries or SplineSeries and
112 The color of the series. This is line (pen) color in case of LineSeries or SplineSeries and
113 fill (brush) color in case of ScatterSeries or AreaSeries.
113 fill (brush) color in case of ScatterSeries or AreaSeries.
114 */
114 */
115
115
116 /*!
116 /*!
117 \fn void QXYSeries::clicked(const QPointF& point)
117 \fn void QXYSeries::clicked(const QPointF& point)
118 \brief Signal is emitted when user clicks the \a point on chart.
118 \brief Signal is emitted when user clicks the \a point on chart.
119 */
119 */
120 /*!
120 /*!
121 \qmlsignal XYSeries::onClicked(QPointF point)
121 \qmlsignal XYSeries::onClicked(QPointF point)
122 Signal is emitted when user clicks the \a point on chart. For example:
122 Signal is emitted when user clicks the \a point on chart. For example:
123 \code
123 \code
124 LineSeries {
124 LineSeries {
125 XYPoint { x: 0; y: 0 }
125 XYPoint { x: 0; y: 0 }
126 XYPoint { x: 1.1; y: 2.1 }
126 XYPoint { x: 1.1; y: 2.1 }
127 onClicked: console.log("onClicked: " + point.x + ", " + point.y);
127 onClicked: console.log("onClicked: " + point.x + ", " + point.y);
128 }
128 }
129 \endcode
129 \endcode
130 */
130 */
131
131
132 /*!
132 /*!
133 \fn void QXYSeries::hovered(const QPointF &point, bool state)
133 \fn void QXYSeries::hovered(const QPointF &point, bool state)
134 This signal is emitted when user has hovered over or away from the series. \a point shows the origin (coordinate)
134 This signal is emitted when user has hovered over or away from the series. \a point shows the origin (coordinate)
135 of the hover event. \a state is true when user has hovered over the series and false when hover has moved away from
135 of the hover event. \a state is true when user has hovered over the series and false when hover has moved away from
136 the series.
136 the series.
137 */
137 */
138 /*!
138 /*!
139 \qmlsignal XYSeries::onHovered(point point, bool state)
139 \qmlsignal XYSeries::onHovered(point point, bool state)
140 This signal is emitted when user has hovered over or away from the series. \a point shows the origin (coordinate)
140 This signal is emitted when user has hovered over or away from the series. \a point shows the origin (coordinate)
141 of the hover event. \a state is true when user has hovered over the series and false when hover has moved away from
141 of the hover event. \a state is true when user has hovered over the series and false when hover has moved away from
142 the series.
142 the series.
143 */
143 */
144
144
145 /*!
145 /*!
146 \fn void QXYSeries::pointReplaced(int index)
146 \fn void QXYSeries::pointReplaced(int index)
147 Signal is emitted when a point has been replaced at \a index.
147 Signal is emitted when a point has been replaced at \a index.
148 \sa replace()
148 \sa replace()
149 */
149 */
150 /*!
150 /*!
151 \qmlsignal XYSeries::onPointReplaced(int index)
151 \qmlsignal XYSeries::onPointReplaced(int index)
152 Signal is emitted when a point has been replaced at \a index.
152 Signal is emitted when a point has been replaced at \a index.
153 */
153 */
154
154
155 /*!
155 /*!
156 \fn void QXYSeries::pointsReplaced()
156 \fn void QXYSeries::pointsReplaced()
157 Signal is emitted when all points have been replaced with another points.
157 Signal is emitted when all points have been replaced with another points.
158 \sa replace()
158 \sa replace()
159 */
159 */
160 /*!
160 /*!
161 \qmlsignal XYSeries::onPointsReplaced()
161 \qmlsignal XYSeries::onPointsReplaced()
162 */
162 */
163
163
164 /*!
164 /*!
165 \fn void QXYSeries::pointAdded(int index)
165 \fn void QXYSeries::pointAdded(int index)
166 Signal is emitted when a point has been added at \a index.
166 Signal is emitted when a point has been added at \a index.
167 \sa append(), insert()
167 \sa append(), insert()
168 */
168 */
169 /*!
169 /*!
170 \qmlsignal XYSeries::onPointAdded(int index)
170 \qmlsignal XYSeries::onPointAdded(int index)
171 Signal is emitted when a point has been added at \a index.
171 Signal is emitted when a point has been added at \a index.
172 */
172 */
173
173
174 /*!
174 /*!
175 \fn void QXYSeries::pointRemoved(int index)
175 \fn void QXYSeries::pointRemoved(int index)
176 Signal is emitted when a point has been removed from \a index.
176 Signal is emitted when a point has been removed from \a index.
177 \sa remove()
177 \sa remove()
178 */
178 */
179 /*!
179 /*!
180 \qmlsignal XYSeries::onPointRemoved(int index)
180 \qmlsignal XYSeries::onPointRemoved(int index)
181 Signal is emitted when a point has been removed from \a index.
181 Signal is emitted when a point has been removed from \a index.
182 */
182 */
183
183
184 /*!
184 /*!
185 \fn void QXYSeries::colorChanged(QColor color)
185 \fn void QXYSeries::colorChanged(QColor color)
186 \brief Signal is emitted when the line (pen) color has changed to \a color.
186 \brief Signal is emitted when the line (pen) color has changed to \a color.
187 */
187 */
188 /*!
188 /*!
189 \qmlsignal XYSeries::onColorChanged(color color)
189 \qmlsignal XYSeries::onColorChanged(color color)
190 Signal is emitted when the line (pen) color has changed to \a color.
190 Signal is emitted when the line (pen) color has changed to \a color.
191 */
191 */
192
192
193 /*!
193 /*!
194 \fn void QXYSeriesPrivate::updated()
194 \fn void QXYSeriesPrivate::updated()
195 \brief \internal
195 \brief \internal
196 */
196 */
197
197
198 /*!
198 /*!
199 \qmlmethod XYSeries::append(real x, real y)
199 \qmlmethod XYSeries::append(real x, real y)
200 Append point (\a x, \a y) to the series
200 Append point (\a x, \a y) to the series
201 */
201 */
202
202
203 /*!
203 /*!
204 \qmlmethod XYSeries::replace(real oldX, real oldY, real newX, real newY)
204 \qmlmethod XYSeries::replace(real oldX, real oldY, real newX, real newY)
205 Replaces point (\a oldX, \a oldY) with point (\a newX, \a newY). Does nothing, if point (oldX, oldY) does not
205 Replaces point (\a oldX, \a oldY) with point (\a newX, \a newY). Does nothing, if point (oldX, oldY) does not
206 exist.
206 exist.
207 */
207 */
208
208
209 /*!
209 /*!
210 \qmlmethod XYSeries::remove(real x, real y)
210 \qmlmethod XYSeries::remove(real x, real y)
211 Removes point (\a x, \a y) from the series. Does nothing, if point (x, y) does not exist.
211 Removes point (\a x, \a y) from the series. Does nothing, if point (x, y) does not exist.
212 */
212 */
213
213
214 /*!
214 /*!
215 \qmlmethod XYSeries::insert(int index, real x, real y)
215 \qmlmethod XYSeries::insert(int index, real x, real y)
216 Inserts point (\a x, \a y) to the \a index. If index is 0 or smaller than 0 the point is prepended to the list of
216 Inserts point (\a x, \a y) to the \a index. If index is 0 or smaller than 0 the point is prepended to the list of
217 points. If index is the same as or bigger than count, the point is appended to the list of points.
217 points. If index is the same as or bigger than count, the point is appended to the list of points.
218 */
218 */
219
219
220 /*!
220 /*!
221 \qmlmethod QPointF XYSeries::at(int index)
221 \qmlmethod QPointF XYSeries::at(int index)
222 Returns point at \a index. Returns (0, 0) if the index is not valid.
222 Returns point at \a index. Returns (0, 0) if the index is not valid.
223 */
223 */
224
224
225 /*!
225 /*!
226 \internal
226 \internal
227
227
228 Constructs empty series object which is a child of \a parent.
228 Constructs empty series object which is a child of \a parent.
229 When series object is added to QChartView or QChart instance ownerships is transferred.
229 When series object is added to QChartView or QChart instance ownerships is transferred.
230 */
230 */
231 QXYSeries::QXYSeries(QXYSeriesPrivate &d, QObject *parent)
231 QXYSeries::QXYSeries(QXYSeriesPrivate &d, QObject *parent)
232 : QAbstractSeries(d, parent)
232 : QAbstractSeries(d, parent)
233 {
233 {
234 }
234 }
235
235
236 /*!
236 /*!
237 Destroys the object. Series added to QChartView or QChart instances are owned by those,
237 Destroys the object. Series added to QChartView or QChart instances are owned by those,
238 and are deleted when mentioned object are destroyed.
238 and are deleted when mentioned object are destroyed.
239 */
239 */
240 QXYSeries::~QXYSeries()
240 QXYSeries::~QXYSeries()
241 {
241 {
242 }
242 }
243
243
244 /*!
244 /*!
245 Adds data point \a x \a y to the series. Points are connected with lines on the chart.
245 Adds data point \a x \a y to the series. Points are connected with lines on the chart.
246 */
246 */
247 void QXYSeries::append(qreal x, qreal y)
247 void QXYSeries::append(qreal x, qreal y)
248 {
248 {
249 append(QPointF(x, y));
249 append(QPointF(x, y));
250 }
250 }
251
251
252 /*!
252 /*!
253 This is an overloaded function.
253 This is an overloaded function.
254 Adds data \a point to the series. Points are connected with lines on the chart.
254 Adds data \a point to the series. Points are connected with lines on the chart.
255 */
255 */
256 void QXYSeries::append(const QPointF &point)
256 void QXYSeries::append(const QPointF &point)
257 {
257 {
258 Q_D(QXYSeries);
258 Q_D(QXYSeries);
259
259
260 if (isValidValue(point)) {
260 if (isValidValue(point)) {
261 d->m_points << point;
261 d->m_points << point;
262 emit pointAdded(d->m_points.count() - 1);
262 emit pointAdded(d->m_points.count() - 1);
263 }
263 }
264 }
264 }
265
265
266 /*!
266 /*!
267 This is an overloaded function.
267 This is an overloaded function.
268 Adds list of data \a points to the series. Points are connected with lines on the chart.
268 Adds list of data \a points to the series. Points are connected with lines on the chart.
269 */
269 */
270 void QXYSeries::append(const QList<QPointF> &points)
270 void QXYSeries::append(const QList<QPointF> &points)
271 {
271 {
272 foreach (const QPointF &point , points)
272 foreach (const QPointF &point , points)
273 append(point);
273 append(point);
274 }
274 }
275
275
276 /*!
276 /*!
277 Replaces data point \a oldX \a oldY with data point \a newX \a newY.
277 Replaces data point \a oldX \a oldY with data point \a newX \a newY.
278 \sa QXYSeries::pointReplaced()
278 \sa QXYSeries::pointReplaced()
279 */
279 */
280 void QXYSeries::replace(qreal oldX, qreal oldY, qreal newX, qreal newY)
280 void QXYSeries::replace(qreal oldX, qreal oldY, qreal newX, qreal newY)
281 {
281 {
282 replace(QPointF(oldX, oldY), QPointF(newX, newY));
282 replace(QPointF(oldX, oldY), QPointF(newX, newY));
283 }
283 }
284
284
285 /*!
285 /*!
286 Replaces \a oldPoint with \a newPoint.
286 Replaces \a oldPoint with \a newPoint.
287 \sa QXYSeries::pointReplaced()
287 \sa QXYSeries::pointReplaced()
288 */
288 */
289 void QXYSeries::replace(const QPointF &oldPoint, const QPointF &newPoint)
289 void QXYSeries::replace(const QPointF &oldPoint, const QPointF &newPoint)
290 {
290 {
291 Q_D(QXYSeries);
291 Q_D(QXYSeries);
292 int index = d->m_points.indexOf(oldPoint);
292 int index = d->m_points.indexOf(oldPoint);
293 if (index == -1)
293 if (index == -1)
294 return;
294 return;
295 if (isValidValue(newPoint)) {
295 if (isValidValue(newPoint)) {
296 d->m_points[index] = newPoint;
296 d->m_points[index] = newPoint;
297 emit pointReplaced(index);
297 emit pointReplaced(index);
298 }
298 }
299 }
299 }
300
300
301 /*!
301 /*!
302 Replaces the current points with \a points. This is faster than replacing data points one by one,
302 Replaces the current points with \a points. This is faster than replacing data points one by one,
303 or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced()
303 or first clearing all data, and then appending the new data. Emits QXYSeries::pointsReplaced()
304 when the points have been replaced.
304 when the points have been replaced.
305 \sa QXYSeries::pointsReplaced()
305 \sa QXYSeries::pointsReplaced()
306 */
306 */
307 void QXYSeries::replace(QList<QPointF> points)
307 void QXYSeries::replace(QList<QPointF> points)
308 {
308 {
309 Q_D(QXYSeries);
309 Q_D(QXYSeries);
310 d->m_points = points.toVector();
310 d->m_points = points.toVector();
311 emit pointsReplaced();
311 emit pointsReplaced();
312 }
312 }
313
313
314 /*!
314 /*!
315 Removes current \a x and \a y value.
315 Removes current \a x and \a y value.
316 */
316 */
317 void QXYSeries::remove(qreal x, qreal y)
317 void QXYSeries::remove(qreal x, qreal y)
318 {
318 {
319 remove(QPointF(x, y));
319 remove(QPointF(x, y));
320 }
320 }
321
321
322 /*!
322 /*!
323 Removes current \a point x value.
323 Removes current \a point x value.
324
324
325 Note: point y value is ignored.
325 Note: point y value is ignored.
326 */
326 */
327 void QXYSeries::remove(const QPointF &point)
327 void QXYSeries::remove(const QPointF &point)
328 {
328 {
329 Q_D(QXYSeries);
329 Q_D(QXYSeries);
330 int index = d->m_points.indexOf(point);
330 int index = d->m_points.indexOf(point);
331 if (index == -1)
331 if (index == -1)
332 return;
332 return;
333 d->m_points.remove(index);
333 d->m_points.remove(index);
334 emit pointRemoved(index);
334 emit pointRemoved(index);
335 }
335 }
336
336
337 /*!
337 /*!
338 Inserts a \a point in the series at \a index position.
338 Inserts a \a point in the series at \a index position.
339 */
339 */
340 void QXYSeries::insert(int index, const QPointF &point)
340 void QXYSeries::insert(int index, const QPointF &point)
341 {
341 {
342 Q_D(QXYSeries);
342 Q_D(QXYSeries);
343 if (isValidValue(point)) {
343 if (isValidValue(point)) {
344 d->m_points.insert(index, point);
344 d->m_points.insert(index, point);
345 emit pointAdded(index);
345 emit pointAdded(index);
346 }
346 }
347 }
347 }
348
348
349 /*!
349 /*!
350 Removes all points from the series.
350 Removes all points from the series.
351 */
351 */
352 void QXYSeries::clear()
352 void QXYSeries::clear()
353 {
353 {
354 Q_D(QXYSeries);
354 Q_D(QXYSeries);
355 for (int i = d->m_points.size() - 1; i >= 0; i--)
355 for (int i = d->m_points.size() - 1; i >= 0; i--)
356 remove(d->m_points.at(i));
356 remove(d->m_points.at(i));
357 }
357 }
358
358
359 /*!
359 /*!
360 Returns list of points in the series.
360 Returns list of points in the series.
361 */
361 */
362 QList<QPointF> QXYSeries::points() const
362 QList<QPointF> QXYSeries::points() const
363 {
363 {
364 Q_D(const QXYSeries);
364 Q_D(const QXYSeries);
365 return d->m_points.toList();
365 return d->m_points.toList();
366 }
366 }
367
367
368 /*!
368 /*!
369 Returns point at \a index in internal points vector.
369 Returns point at \a index in internal points vector.
370 */
370 */
371 const QPointF &QXYSeries::pointAt(int index) const
371 const QPointF &QXYSeries::at(int index) const
372 {
372 {
373 Q_D(const QXYSeries);
373 Q_D(const QXYSeries);
374 return d->m_points.at(index);
374 return d->m_points.at(index);
375 }
375 }
376
376
377 /*!
377 /*!
378 Returns number of data points within series.
378 Returns number of data points within series.
379 */
379 */
380 int QXYSeries::count() const
380 int QXYSeries::count() const
381 {
381 {
382 Q_D(const QXYSeries);
382 Q_D(const QXYSeries);
383 return d->m_points.count();
383 return d->m_points.count();
384 }
384 }
385
385
386
386
387 /*!
387 /*!
388 Sets \a pen used for drawing points on the chart. If the pen is not defined, the
388 Sets \a pen used for drawing points on the chart. If the pen is not defined, the
389 pen from chart theme is used.
389 pen from chart theme is used.
390 \sa QChart::setTheme()
390 \sa QChart::setTheme()
391 */
391 */
392 void QXYSeries::setPen(const QPen &pen)
392 void QXYSeries::setPen(const QPen &pen)
393 {
393 {
394 Q_D(QXYSeries);
394 Q_D(QXYSeries);
395 if (d->m_pen != pen) {
395 if (d->m_pen != pen) {
396 bool emitColorChanged = d->m_pen.color() != pen.color();
396 bool emitColorChanged = d->m_pen.color() != pen.color();
397 d->m_pen = pen;
397 d->m_pen = pen;
398 emit d->updated();
398 emit d->updated();
399 if (emitColorChanged)
399 if (emitColorChanged)
400 emit colorChanged(pen.color());
400 emit colorChanged(pen.color());
401 }
401 }
402 }
402 }
403
403
404 QPen QXYSeries::pen() const
404 QPen QXYSeries::pen() const
405 {
405 {
406 Q_D(const QXYSeries);
406 Q_D(const QXYSeries);
407 return d->m_pen;
407 return d->m_pen;
408 }
408 }
409
409
410 /*!
410 /*!
411 Sets \a brush used for drawing points on the chart. If the brush is not defined, brush
411 Sets \a brush used for drawing points on the chart. If the brush is not defined, brush
412 from chart theme setting is used.
412 from chart theme setting is used.
413 \sa QChart::setTheme()
413 \sa QChart::setTheme()
414 */
414 */
415 void QXYSeries::setBrush(const QBrush &brush)
415 void QXYSeries::setBrush(const QBrush &brush)
416 {
416 {
417 Q_D(QXYSeries);
417 Q_D(QXYSeries);
418 if (d->m_brush != brush) {
418 if (d->m_brush != brush) {
419 d->m_brush = brush;
419 d->m_brush = brush;
420 emit d->updated();
420 emit d->updated();
421 }
421 }
422 }
422 }
423
423
424 QBrush QXYSeries::brush() const
424 QBrush QXYSeries::brush() const
425 {
425 {
426 Q_D(const QXYSeries);
426 Q_D(const QXYSeries);
427 return d->m_brush;
427 return d->m_brush;
428 }
428 }
429
429
430 void QXYSeries::setColor(const QColor &color)
430 void QXYSeries::setColor(const QColor &color)
431 {
431 {
432 QPen p = pen();
432 QPen p = pen();
433 if (p.color() != color) {
433 if (p.color() != color) {
434 p.setColor(color);
434 p.setColor(color);
435 setPen(p);
435 setPen(p);
436 }
436 }
437 }
437 }
438
438
439 QColor QXYSeries::color() const
439 QColor QXYSeries::color() const
440 {
440 {
441 return pen().color();
441 return pen().color();
442 }
442 }
443
443
444 void QXYSeries::setPointsVisible(bool visible)
444 void QXYSeries::setPointsVisible(bool visible)
445 {
445 {
446 Q_D(QXYSeries);
446 Q_D(QXYSeries);
447 if (d->m_pointsVisible != visible) {
447 if (d->m_pointsVisible != visible) {
448 d->m_pointsVisible = visible;
448 d->m_pointsVisible = visible;
449 emit d->updated();
449 emit d->updated();
450 }
450 }
451 }
451 }
452
452
453 bool QXYSeries::pointsVisible() const
453 bool QXYSeries::pointsVisible() const
454 {
454 {
455 Q_D(const QXYSeries);
455 Q_D(const QXYSeries);
456 return d->m_pointsVisible;
456 return d->m_pointsVisible;
457 }
457 }
458
458
459
459
460 /*!
460 /*!
461 Stream operator for adding a data \a point to the series.
461 Stream operator for adding a data \a point to the series.
462 \sa append()
462 \sa append()
463 */
463 */
464 QXYSeries &QXYSeries::operator<< (const QPointF &point)
464 QXYSeries &QXYSeries::operator<< (const QPointF &point)
465 {
465 {
466 append(point);
466 append(point);
467 return *this;
467 return *this;
468 }
468 }
469
469
470
470
471 /*!
471 /*!
472 Stream operator for adding a list of \a points to the series.
472 Stream operator for adding a list of \a points to the series.
473 \sa append()
473 \sa append()
474 */
474 */
475
475
476 QXYSeries &QXYSeries::operator<< (const QList<QPointF>& points)
476 QXYSeries &QXYSeries::operator<< (const QList<QPointF>& points)
477 {
477 {
478 append(points);
478 append(points);
479 return *this;
479 return *this;
480 }
480 }
481
481
482 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
482 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
483
483
484
484
485 QXYSeriesPrivate::QXYSeriesPrivate(QXYSeries *q)
485 QXYSeriesPrivate::QXYSeriesPrivate(QXYSeries *q)
486 : QAbstractSeriesPrivate(q),
486 : QAbstractSeriesPrivate(q),
487 m_pointsVisible(false)
487 m_pointsVisible(false)
488 {
488 {
489 }
489 }
490
490
491 void QXYSeriesPrivate::initializeDomain()
491 void QXYSeriesPrivate::initializeDomain()
492 {
492 {
493 qreal minX(0);
493 qreal minX(0);
494 qreal minY(0);
494 qreal minY(0);
495 qreal maxX(1);
495 qreal maxX(1);
496 qreal maxY(1);
496 qreal maxY(1);
497
497
498 Q_Q(QXYSeries);
498 Q_Q(QXYSeries);
499
499
500 const QList<QPointF>& points = q->points();
500 const QList<QPointF>& points = q->points();
501
501
502 if (!points.isEmpty()) {
502 if (!points.isEmpty()) {
503 minX = points[0].x();
503 minX = points[0].x();
504 minY = points[0].y();
504 minY = points[0].y();
505 maxX = minX;
505 maxX = minX;
506 maxY = minY;
506 maxY = minY;
507
507
508 for (int i = 0; i < points.count(); i++) {
508 for (int i = 0; i < points.count(); i++) {
509 qreal x = points[i].x();
509 qreal x = points[i].x();
510 qreal y = points[i].y();
510 qreal y = points[i].y();
511 minX = qMin(minX, x);
511 minX = qMin(minX, x);
512 minY = qMin(minY, y);
512 minY = qMin(minY, y);
513 maxX = qMax(maxX, x);
513 maxX = qMax(maxX, x);
514 maxY = qMax(maxY, y);
514 maxY = qMax(maxY, y);
515 }
515 }
516 }
516 }
517
517
518 domain()->setRange(minX, maxX, minY, maxY);
518 domain()->setRange(minX, maxX, minY, maxY);
519 }
519 }
520
520
521 QList<QLegendMarker*> QXYSeriesPrivate::createLegendMarkers(QLegend* legend)
521 QList<QLegendMarker*> QXYSeriesPrivate::createLegendMarkers(QLegend* legend)
522 {
522 {
523 Q_Q(QXYSeries);
523 Q_Q(QXYSeries);
524 QList<QLegendMarker*> list;
524 QList<QLegendMarker*> list;
525 return list << new QXYLegendMarker(q,legend);
525 return list << new QXYLegendMarker(q,legend);
526 }
526 }
527
527
528 void QXYSeriesPrivate::initializeAxes()
528 void QXYSeriesPrivate::initializeAxes()
529 {
529 {
530
530
531 }
531 }
532
532
533 QAbstractAxis::AxisType QXYSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
533 QAbstractAxis::AxisType QXYSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
534 {
534 {
535 Q_UNUSED(orientation);
535 Q_UNUSED(orientation);
536 return QAbstractAxis::AxisTypeValue;
536 return QAbstractAxis::AxisTypeValue;
537 }
537 }
538
538
539 QAbstractAxis* QXYSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const
539 QAbstractAxis* QXYSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const
540 {
540 {
541 Q_UNUSED(orientation);
541 Q_UNUSED(orientation);
542 return 0;
542 return 0;
543 }
543 }
544
544
545 void QXYSeriesPrivate::initializeAnimations(QtCommercialChart::QChart::AnimationOptions options)
545 void QXYSeriesPrivate::initializeAnimations(QtCommercialChart::QChart::AnimationOptions options)
546 {
546 {
547 XYChart *item = static_cast<XYChart *>(m_item.data());
547 XYChart *item = static_cast<XYChart *>(m_item.data());
548 Q_ASSERT(item);
548 Q_ASSERT(item);
549 if (options.testFlag(QChart::SeriesAnimations)) {
549 if (options.testFlag(QChart::SeriesAnimations)) {
550 item->setAnimation(new XYAnimation(item));
550 item->setAnimation(new XYAnimation(item));
551 }else{
551 }else{
552 item->setAnimation(0);
552 item->setAnimation(0);
553 }
553 }
554 QAbstractSeriesPrivate::initializeAnimations(options);
554 QAbstractSeriesPrivate::initializeAnimations(options);
555 }
555 }
556
556
557 #include "moc_qxyseries.cpp"
557 #include "moc_qxyseries.cpp"
558 #include "moc_qxyseries_p.cpp"
558 #include "moc_qxyseries_p.cpp"
559
559
560 QTCOMMERCIALCHART_END_NAMESPACE
560 QTCOMMERCIALCHART_END_NAMESPACE
@@ -1,97 +1,97
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 Commercial Charts Add-on.
7 ** This file is part of the Qt Commercial Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Commercial licenses may use this file in
10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 ** accordance with the Qt Commercial License Agreement provided with the
11 ** accordance with the Qt Commercial 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 #ifndef QXYSERIES_H
21 #ifndef QXYSERIES_H
22 #define QXYSERIES_H
22 #define QXYSERIES_H
23
23
24 #include <qchartglobal.h>
24 #include <qchartglobal.h>
25 #include <qabstractseries.h>
25 #include <qabstractseries.h>
26 #include <QPen>
26 #include <QPen>
27 #include <QBrush>
27 #include <QBrush>
28
28
29 class QModelIndex;
29 class QModelIndex;
30
30
31 QTCOMMERCIALCHART_BEGIN_NAMESPACE
31 QTCOMMERCIALCHART_BEGIN_NAMESPACE
32
32
33 class QXYSeriesPrivate;
33 class QXYSeriesPrivate;
34 class QXYModelMapper;
34 class QXYModelMapper;
35
35
36 class QTCOMMERCIALCHART_EXPORT QXYSeries : public QAbstractSeries
36 class QTCOMMERCIALCHART_EXPORT QXYSeries : public QAbstractSeries
37 {
37 {
38 Q_OBJECT
38 Q_OBJECT
39 Q_PROPERTY(bool pointsVisible READ pointsVisible WRITE setPointsVisible)
39 Q_PROPERTY(bool pointsVisible READ pointsVisible WRITE setPointsVisible)
40 Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
40 Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
41
41
42 protected:
42 protected:
43 explicit QXYSeries(QXYSeriesPrivate &d, QObject *parent = 0);
43 explicit QXYSeries(QXYSeriesPrivate &d, QObject *parent = 0);
44
44
45 public:
45 public:
46 ~QXYSeries();
46 ~QXYSeries();
47 void append(qreal x, qreal y);
47 void append(qreal x, qreal y);
48 void append(const QPointF &point);
48 void append(const QPointF &point);
49 void append(const QList<QPointF> &points);
49 void append(const QList<QPointF> &points);
50 void replace(qreal oldX, qreal oldY, qreal newX, qreal newY);
50 void replace(qreal oldX, qreal oldY, qreal newX, qreal newY);
51 void replace(const QPointF &oldPoint, const QPointF &newPoint);
51 void replace(const QPointF &oldPoint, const QPointF &newPoint);
52 void remove(qreal x, qreal y);
52 void remove(qreal x, qreal y);
53 void remove(const QPointF &point);
53 void remove(const QPointF &point);
54 void insert(int index, const QPointF &point);
54 void insert(int index, const QPointF &point);
55 void clear();
55 void clear();
56
56
57 int count() const;
57 int count() const;
58 QList<QPointF> points() const;
58 QList<QPointF> points() const;
59 const QPointF &pointAt(int index) const;
59 const QPointF &at(int index) const;
60
60
61 QXYSeries &operator << (const QPointF &point);
61 QXYSeries &operator << (const QPointF &point);
62 QXYSeries &operator << (const QList<QPointF> &points);
62 QXYSeries &operator << (const QList<QPointF> &points);
63
63
64 virtual void setPen(const QPen &pen);
64 virtual void setPen(const QPen &pen);
65 QPen pen() const;
65 QPen pen() const;
66
66
67 virtual void setBrush(const QBrush &brush);
67 virtual void setBrush(const QBrush &brush);
68 QBrush brush() const;
68 QBrush brush() const;
69
69
70 virtual void setColor(const QColor &color);
70 virtual void setColor(const QColor &color);
71 virtual QColor color() const;
71 virtual QColor color() const;
72
72
73 void setPointsVisible(bool visible = true);
73 void setPointsVisible(bool visible = true);
74 bool pointsVisible() const;
74 bool pointsVisible() const;
75
75
76 void replace(QList<QPointF> points);
76 void replace(QList<QPointF> points);
77
77
78 Q_SIGNALS:
78 Q_SIGNALS:
79 void clicked(const QPointF &point);
79 void clicked(const QPointF &point);
80 void hovered(const QPointF &point, bool state);
80 void hovered(const QPointF &point, bool state);
81 void pointReplaced(int index);
81 void pointReplaced(int index);
82 void pointRemoved(int index);
82 void pointRemoved(int index);
83 void pointAdded(int index);
83 void pointAdded(int index);
84 void colorChanged(QColor color);
84 void colorChanged(QColor color);
85 void pointsReplaced();
85 void pointsReplaced();
86
86
87 private:
87 private:
88 Q_DECLARE_PRIVATE(QXYSeries)
88 Q_DECLARE_PRIVATE(QXYSeries)
89 Q_DISABLE_COPY(QXYSeries)
89 Q_DISABLE_COPY(QXYSeries)
90 friend class QXYLegendMarkerPrivate;
90 friend class QXYLegendMarkerPrivate;
91 friend class XYLegendMarker;
91 friend class XYLegendMarker;
92 friend class XYChart;
92 friend class XYChart;
93 };
93 };
94
94
95 QTCOMMERCIALCHART_END_NAMESPACE
95 QTCOMMERCIALCHART_END_NAMESPACE
96
96
97 #endif // QXYSERIES_H
97 #endif // QXYSERIES_H
@@ -1,191 +1,191
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 Commercial Charts Add-on.
7 ** This file is part of the Qt Commercial Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Commercial licenses may use this file in
10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 ** accordance with the Qt Commercial License Agreement provided with the
11 ** accordance with the Qt Commercial 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 "xychart_p.h"
21 #include "xychart_p.h"
22 #include "qxyseries.h"
22 #include "qxyseries.h"
23 #include "qxyseries_p.h"
23 #include "qxyseries_p.h"
24 #include "chartpresenter_p.h"
24 #include "chartpresenter_p.h"
25 #include "abstractdomain_p.h"
25 #include "abstractdomain_p.h"
26 #include "qxymodelmapper.h"
26 #include "qxymodelmapper.h"
27 #include "qabstractaxis_p.h"
27 #include "qabstractaxis_p.h"
28 #include <QPainter>
28 #include <QPainter>
29 #include <QAbstractItemModel>
29 #include <QAbstractItemModel>
30
30
31
31
32 QTCOMMERCIALCHART_BEGIN_NAMESPACE
32 QTCOMMERCIALCHART_BEGIN_NAMESPACE
33
33
34 //TODO: optimize : remove points which are not visible
34 //TODO: optimize : remove points which are not visible
35
35
36 XYChart::XYChart(QXYSeries *series, QGraphicsItem *item):
36 XYChart::XYChart(QXYSeries *series, QGraphicsItem *item):
37 ChartItem(series->d_func(),item),
37 ChartItem(series->d_func(),item),
38 m_series(series),
38 m_series(series),
39 m_animation(0),
39 m_animation(0),
40 m_dirty(true)
40 m_dirty(true)
41 {
41 {
42 QObject::connect(series, SIGNAL(pointReplaced(int)), this, SLOT(handlePointReplaced(int)));
42 QObject::connect(series, SIGNAL(pointReplaced(int)), this, SLOT(handlePointReplaced(int)));
43 QObject::connect(series, SIGNAL(pointsReplaced()), this, SLOT(handlePointsReplaced()));
43 QObject::connect(series, SIGNAL(pointsReplaced()), this, SLOT(handlePointsReplaced()));
44 QObject::connect(series, SIGNAL(pointAdded(int)), this, SLOT(handlePointAdded(int)));
44 QObject::connect(series, SIGNAL(pointAdded(int)), this, SLOT(handlePointAdded(int)));
45 QObject::connect(series, SIGNAL(pointRemoved(int)), this, SLOT(handlePointRemoved(int)));
45 QObject::connect(series, SIGNAL(pointRemoved(int)), this, SLOT(handlePointRemoved(int)));
46 QObject::connect(this, SIGNAL(clicked(QPointF)), series, SIGNAL(clicked(QPointF)));
46 QObject::connect(this, SIGNAL(clicked(QPointF)), series, SIGNAL(clicked(QPointF)));
47 QObject::connect(this, SIGNAL(hovered(QPointF,bool)), series, SIGNAL(hovered(QPointF,bool)));
47 QObject::connect(this, SIGNAL(hovered(QPointF,bool)), series, SIGNAL(hovered(QPointF,bool)));
48 }
48 }
49
49
50 void XYChart::setGeometryPoints(const QVector<QPointF> &points)
50 void XYChart::setGeometryPoints(const QVector<QPointF> &points)
51 {
51 {
52 m_points = points;
52 m_points = points;
53 }
53 }
54
54
55 void XYChart::setAnimation(XYAnimation *animation)
55 void XYChart::setAnimation(XYAnimation *animation)
56 {
56 {
57 m_animation = animation;
57 m_animation = animation;
58 }
58 }
59
59
60 void XYChart::setDirty(bool dirty)
60 void XYChart::setDirty(bool dirty)
61 {
61 {
62 m_dirty = dirty;
62 m_dirty = dirty;
63 }
63 }
64
64
65 // Returns a vector with same size as geometryPoints vector, indicating
65 // Returns a vector with same size as geometryPoints vector, indicating
66 // the off grid status of points.
66 // the off grid status of points.
67 QVector<bool> XYChart::offGridStatusVector()
67 QVector<bool> XYChart::offGridStatusVector()
68 {
68 {
69 qreal minX = domain()->minX();
69 qreal minX = domain()->minX();
70 qreal maxX = domain()->maxX();
70 qreal maxX = domain()->maxX();
71 qreal minY = domain()->minY();
71 qreal minY = domain()->minY();
72 qreal maxY = domain()->maxY();
72 qreal maxY = domain()->maxY();
73
73
74 QVector<bool> returnVector;
74 QVector<bool> returnVector;
75 returnVector.resize(m_points.size());
75 returnVector.resize(m_points.size());
76 // During remove/append animation series may have different number of points,
76 // During remove/append animation series may have different number of points,
77 // so ensure we don't go over the index. No need to check for zero points, this
77 // so ensure we don't go over the index. No need to check for zero points, this
78 // will not be called in such a situation.
78 // will not be called in such a situation.
79 const int seriesLastIndex = m_series->count() - 1;
79 const int seriesLastIndex = m_series->count() - 1;
80
80
81 for (int i = 0; i < m_points.size(); i++) {
81 for (int i = 0; i < m_points.size(); i++) {
82 const QPointF &seriesPoint = m_series->pointAt(qMin(seriesLastIndex, i));
82 const QPointF &seriesPoint = m_series->at(qMin(seriesLastIndex, i));
83 if (seriesPoint.x() < minX
83 if (seriesPoint.x() < minX
84 || seriesPoint.x() > maxX
84 || seriesPoint.x() > maxX
85 || seriesPoint.y() < minY
85 || seriesPoint.y() < minY
86 || seriesPoint.y() > maxY) {
86 || seriesPoint.y() > maxY) {
87 returnVector[i] = true;
87 returnVector[i] = true;
88 } else {
88 } else {
89 returnVector[i] = false;
89 returnVector[i] = false;
90 }
90 }
91 }
91 }
92 return returnVector;
92 return returnVector;
93 }
93 }
94
94
95 void XYChart::updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index)
95 void XYChart::updateChart(QVector<QPointF> &oldPoints, QVector<QPointF> &newPoints, int index)
96 {
96 {
97
97
98 if (m_animation) {
98 if (m_animation) {
99 m_animation->setup(oldPoints, newPoints, index);
99 m_animation->setup(oldPoints, newPoints, index);
100 m_points = newPoints;
100 m_points = newPoints;
101 setDirty(false);
101 setDirty(false);
102 presenter()->startAnimation(m_animation);
102 presenter()->startAnimation(m_animation);
103 } else {
103 } else {
104 m_points = newPoints;
104 m_points = newPoints;
105 updateGeometry();
105 updateGeometry();
106 }
106 }
107 }
107 }
108
108
109 //handlers
109 //handlers
110
110
111 void XYChart::handlePointAdded(int index)
111 void XYChart::handlePointAdded(int index)
112 {
112 {
113 Q_ASSERT(index < m_series->count());
113 Q_ASSERT(index < m_series->count());
114 Q_ASSERT(index >= 0);
114 Q_ASSERT(index >= 0);
115
115
116 QVector<QPointF> points;
116 QVector<QPointF> points;
117
117
118 if (m_dirty || m_points.isEmpty()) {
118 if (m_dirty || m_points.isEmpty()) {
119 points = domain()->calculateGeometryPoints(m_series->points());
119 points = domain()->calculateGeometryPoints(m_series->points());
120 } else {
120 } else {
121 points = m_points;
121 points = m_points;
122 QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData);
122 QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData);
123 if (!m_validData)
123 if (!m_validData)
124 m_points.clear();
124 m_points.clear();
125 else
125 else
126 points.insert(index, point);
126 points.insert(index, point);
127 }
127 }
128
128
129 updateChart(m_points, points, index);
129 updateChart(m_points, points, index);
130 }
130 }
131
131
132 void XYChart::handlePointRemoved(int index)
132 void XYChart::handlePointRemoved(int index)
133 {
133 {
134 Q_ASSERT(index <= m_series->count());
134 Q_ASSERT(index <= m_series->count());
135 Q_ASSERT(index >= 0);
135 Q_ASSERT(index >= 0);
136
136
137 QVector<QPointF> points;
137 QVector<QPointF> points;
138
138
139 if (m_dirty || m_points.isEmpty()) {
139 if (m_dirty || m_points.isEmpty()) {
140 points = domain()->calculateGeometryPoints(m_series->points());
140 points = domain()->calculateGeometryPoints(m_series->points());
141 } else {
141 } else {
142 points = m_points;
142 points = m_points;
143 points.remove(index);
143 points.remove(index);
144 }
144 }
145
145
146 updateChart(m_points, points, index);
146 updateChart(m_points, points, index);
147 }
147 }
148
148
149 void XYChart::handlePointReplaced(int index)
149 void XYChart::handlePointReplaced(int index)
150 {
150 {
151 Q_ASSERT(index < m_series->count());
151 Q_ASSERT(index < m_series->count());
152 Q_ASSERT(index >= 0);
152 Q_ASSERT(index >= 0);
153
153
154 QVector<QPointF> points;
154 QVector<QPointF> points;
155
155
156 if (m_dirty || m_points.isEmpty()) {
156 if (m_dirty || m_points.isEmpty()) {
157 points = domain()->calculateGeometryPoints(m_series->points());
157 points = domain()->calculateGeometryPoints(m_series->points());
158 } else {
158 } else {
159 QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData);
159 QPointF point = domain()->calculateGeometryPoint(m_series->points()[index], m_validData);
160 if (!m_validData)
160 if (!m_validData)
161 m_points.clear();
161 m_points.clear();
162 points = m_points;
162 points = m_points;
163 if (m_validData)
163 if (m_validData)
164 points.replace(index, point);
164 points.replace(index, point);
165 }
165 }
166
166
167 updateChart(m_points, points, index);
167 updateChart(m_points, points, index);
168 }
168 }
169
169
170 void XYChart::handlePointsReplaced()
170 void XYChart::handlePointsReplaced()
171 {
171 {
172 // All the points were replaced -> recalculate
172 // All the points were replaced -> recalculate
173 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points());
173 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points());
174 updateChart(m_points, points, -1);
174 updateChart(m_points, points, -1);
175 }
175 }
176
176
177 void XYChart::handleDomainUpdated()
177 void XYChart::handleDomainUpdated()
178 {
178 {
179 if (isEmpty()) return;
179 if (isEmpty()) return;
180 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points());
180 QVector<QPointF> points = domain()->calculateGeometryPoints(m_series->points());
181 updateChart(m_points, points);
181 updateChart(m_points, points);
182 }
182 }
183
183
184 bool XYChart::isEmpty()
184 bool XYChart::isEmpty()
185 {
185 {
186 return domain()->isEmpty() || m_series->points().isEmpty();
186 return domain()->isEmpty() || m_series->points().isEmpty();
187 }
187 }
188
188
189 #include "moc_xychart_p.cpp"
189 #include "moc_xychart_p.cpp"
190
190
191 QTCOMMERCIALCHART_END_NAMESPACE
191 QTCOMMERCIALCHART_END_NAMESPACE
General Comments 0
You need to be logged in to leave comments. Login now