@@ -1,13 +1,100 | |||||
1 | #include "qsplineseries.h" |
|
1 | #include "qsplineseries.h" | |
2 |
|
2 | |||
3 | QSplineSeries::QSplineSeries(QObject *parent) : |
|
3 | QSplineSeries::QSplineSeries(QObject *parent) : | |
4 | QObject(parent) |
|
4 | QObject(parent) | |
5 | { |
|
5 | { | |
6 | } |
|
6 | } | |
7 |
|
7 | |||
8 | QSplineSeries& QSplineSeries::operator << (const QPointF &value) |
|
8 | QSplineSeries& QSplineSeries::operator << (const QPointF &value) | |
9 | { |
|
9 | { | |
10 | d->m_data.append(value); |
|
10 | d->m_data.append(value); | |
11 | emit changed(); |
|
11 | emit changed(); | |
12 | return *this; |
|
12 | return *this; | |
13 | } |
|
13 | } | |
|
14 | ||||
|
15 | void QSplineSeries::GetCurveControlPoints() | |||
|
16 | { | |||
|
17 | int n = m_data.size() - 1; | |||
|
18 | if (n < 1) | |||
|
19 | throw new ArgumentException | |||
|
20 | ("At least two knot points required", "knots"); | |||
|
21 | if (n == 1) | |||
|
22 | { // Special case: Bezier curve should be a straight line. | |||
|
23 | firstControlPoints = new Point[1]; | |||
|
24 | // 3P1 = 2P0 + P3 | |||
|
25 | firstControlPoints[0].X = (2 * knots[0].X + knots[1].X) / 3; | |||
|
26 | firstControlPoints[0].Y = (2 * knots[0].Y + knots[1].Y) / 3; | |||
|
27 | ||||
|
28 | secondControlPoints = new Point[1]; | |||
|
29 | // P2 = 2P1 P0 | |||
|
30 | secondControlPoints[0].X = 2 * | |||
|
31 | firstControlPoints[0].X - knots[0].X; | |||
|
32 | secondControlPoints[0].Y = 2 * | |||
|
33 | firstControlPoints[0].Y - knots[0].Y; | |||
|
34 | return; | |||
|
35 | } | |||
|
36 | ||||
|
37 | // Calculate first Bezier control points | |||
|
38 | // Right hand side vector | |||
|
39 | double[] rhs = new double[n]; | |||
|
40 | ||||
|
41 | // Set right hand side X values | |||
|
42 | for (int i = 1; i < n - 1; ++i) | |||
|
43 | rhs[i] = 4 * knots[i].X + 2 * knots[i + 1].X; | |||
|
44 | rhs[0] = knots[0].X + 2 * knots[1].X; | |||
|
45 | rhs[n - 1] = (8 * knots[n - 1].X + knots[n].X) / 2.0; | |||
|
46 | // Get first control points X-values | |||
|
47 | double[] x = GetFirstControlPoints(rhs); | |||
|
48 | ||||
|
49 | // Set right hand side Y values | |||
|
50 | for (int i = 1; i < n - 1; ++i) | |||
|
51 | rhs[i] = 4 * knots[i].Y + 2 * knots[i + 1].Y; | |||
|
52 | rhs[0] = knots[0].Y + 2 * knots[1].Y; | |||
|
53 | rhs[n - 1] = (8 * knots[n - 1].Y + knots[n].Y) / 2.0; | |||
|
54 | // Get first control points Y-values | |||
|
55 | double[] y = GetFirstControlPoints(rhs); | |||
|
56 | ||||
|
57 | // Fill output arrays. | |||
|
58 | firstControlPoints = new Point[n]; | |||
|
59 | secondControlPoints = new Point[n]; | |||
|
60 | for (int i = 0; i < n; ++i) | |||
|
61 | { | |||
|
62 | // First control point | |||
|
63 | firstControlPoints[i] = new Point(x[i], y[i]); | |||
|
64 | // Second control point | |||
|
65 | if (i < n - 1) | |||
|
66 | secondControlPoints[i] = new Point(2 * knots | |||
|
67 | [i + 1].X - x[i + 1], 2 * | |||
|
68 | knots[i + 1].Y - y[i + 1]); | |||
|
69 | else | |||
|
70 | secondControlPoints[i] = new Point((knots | |||
|
71 | [n].X + x[n - 1]) / 2, | |||
|
72 | (knots[n].Y + y[n - 1]) / 2); | |||
|
73 | } | |||
|
74 | } | |||
|
75 | ||||
|
76 | /// <summary> | |||
|
77 | /// Solves a tridiagonal system for one of coordinates (x or y) | |||
|
78 | /// of first Bezier control points. | |||
|
79 | /// </summary> | |||
|
80 | /// <param name="rhs">Right hand side vector.</param> | |||
|
81 | /// <returns>Solution vector.</returns> | |||
|
82 | void GetFirstControlPoints(qreal[] rhs) | |||
|
83 | { | |||
|
84 | int n = rhs.Length; | |||
|
85 | double[] x = new double[n]; // Solution vector. | |||
|
86 | double[] tmp = new double[n]; // Temp workspace. | |||
|
87 | ||||
|
88 | double b = 2.0; | |||
|
89 | x[0] = rhs[0] / b; | |||
|
90 | for (int i = 1; i < n; i++) // Decomposition and forward substitution. | |||
|
91 | { | |||
|
92 | tmp[i] = 1 / b; | |||
|
93 | b = (i < n - 1 ? 4.0 : 3.5) - tmp[i]; | |||
|
94 | x[i] = (rhs[i] - x[i - 1]) / b; | |||
|
95 | } | |||
|
96 | for (int i = 1; i < n; i++) | |||
|
97 | x[n - i - 1] -= tmp[n - i] * x[n - i]; // Backsubstitution. | |||
|
98 | ||||
|
99 | return x; | |||
|
100 | } |
@@ -1,29 +1,31 | |||||
1 | #ifndef QSPLINESERIES_H |
|
1 | #ifndef QSPLINESERIES_H | |
2 | #define QSPLINESERIES_H |
|
2 | #define QSPLINESERIES_H | |
3 |
|
3 | |||
4 | #include "qchartseries.h" |
|
4 | #include "qchartseries.h" | |
5 |
|
5 | |||
6 | QTCOMMERCIALCHART_BEGIN_NAMESPACE |
|
6 | QTCOMMERCIALCHART_BEGIN_NAMESPACE | |
7 |
|
7 | |||
8 | class QSplineSeries : public QChartSeries |
|
8 | class QSplineSeries : public QChartSeries | |
9 | { |
|
9 | { | |
10 | Q_OBJECT |
|
10 | Q_OBJECT | |
11 | public: |
|
11 | public: | |
12 |
|
12 | |||
13 | QSplineSeries(QObject *parent = 0); |
|
13 | QSplineSeries(QObject *parent = 0); | |
14 | QChartSeriesType type() const { return QChartSeries::SeriesTypeSpline; } |
|
14 | QChartSeriesType type() const { return QChartSeries::SeriesTypeSpline; } | |
15 | void addData(QPointF value); |
|
15 | void addData(QPointF value); | |
16 | QSplineSeries& operator << (const QPointF &value); |
|
16 | QSplineSeries& operator << (const QPointF &value); | |
|
17 | void calculateControlPoints(); | |||
17 |
|
18 | |||
18 | signals: |
|
19 | signals: | |
19 |
|
20 | |||
20 | public slots: |
|
21 | public slots: | |
21 |
|
22 | |||
22 | private: |
|
23 | private: | |
23 | QList<QPointF> m_data; |
|
24 | QList<QPointF> m_data; | |
|
25 | QList<QPointF> m_controlPoints; | |||
24 |
|
26 | |||
25 | }; |
|
27 | }; | |
26 |
|
28 | |||
27 | QTCOMMERCIALCHART_END_NAMESPACE |
|
29 | QTCOMMERCIALCHART_END_NAMESPACE | |
28 |
|
30 | |||
29 | #endif // QSPLINESERIES_H |
|
31 | #endif // QSPLINESERIES_H |
General Comments 0
You need to be logged in to leave comments.
Login now