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