Showing
7 changed files
with
264 additions
and
160 deletions
@@ -30,6 +30,9 @@ export 'package:barcode/barcode.dart'; | @@ -30,6 +30,9 @@ export 'package:barcode/barcode.dart'; | ||
30 | part 'widgets/annotations.dart'; | 30 | part 'widgets/annotations.dart'; |
31 | part 'widgets/barcode.dart'; | 31 | part 'widgets/barcode.dart'; |
32 | part 'widgets/basic.dart'; | 32 | part 'widgets/basic.dart'; |
33 | +part 'widgets/chart/chart.dart'; | ||
34 | +part 'widgets/chart/linear_grid.dart'; | ||
35 | +part 'widgets/chart/line_chart.dart'; | ||
33 | part 'widgets/clip.dart'; | 36 | part 'widgets/clip.dart'; |
34 | part 'widgets/container.dart'; | 37 | part 'widgets/container.dart'; |
35 | part 'widgets/content.dart'; | 38 | part 'widgets/content.dart'; |
@@ -54,4 +57,3 @@ part 'widgets/text_style.dart'; | @@ -54,4 +57,3 @@ part 'widgets/text_style.dart'; | ||
54 | part 'widgets/theme.dart'; | 57 | part 'widgets/theme.dart'; |
55 | part 'widgets/widget.dart'; | 58 | part 'widgets/widget.dart'; |
56 | part 'widgets/wrap.dart'; | 59 | part 'widgets/wrap.dart'; |
57 | -part 'widgets/chart.dart'; |
pdf/lib/widgets/chart/chart.dart
0 → 100644
1 | +/* | ||
2 | + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com> | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | + | ||
17 | +// ignore_for_file: omit_local_variable_types | ||
18 | + | ||
19 | +part of widget; | ||
20 | + | ||
21 | +class Chart extends Widget { | ||
22 | + Chart({ | ||
23 | + @required this.grid, | ||
24 | + @required this.data, | ||
25 | + }); | ||
26 | + | ||
27 | + final ChartGrid grid; | ||
28 | + | ||
29 | + final List<DataSet> data; | ||
30 | + | ||
31 | + PdfPoint _computeSize(BoxConstraints constraints) { | ||
32 | + if (constraints.isTight) { | ||
33 | + return constraints.smallest; | ||
34 | + } | ||
35 | + | ||
36 | + double width = constraints.maxWidth; | ||
37 | + double height = constraints.maxHeight; | ||
38 | + | ||
39 | + const double aspectRatio = 1; | ||
40 | + | ||
41 | + if (!width.isFinite) { | ||
42 | + width = height * aspectRatio; | ||
43 | + } | ||
44 | + | ||
45 | + if (!height.isFinite) { | ||
46 | + height = width * aspectRatio; | ||
47 | + } | ||
48 | + | ||
49 | + return constraints.constrain(PdfPoint(width, height)); | ||
50 | + } | ||
51 | + | ||
52 | + @override | ||
53 | + void layout(Context context, BoxConstraints constraints, | ||
54 | + {bool parentUsesSize = false}) { | ||
55 | + box = PdfRect.fromPoints(PdfPoint.zero, _computeSize(constraints)); | ||
56 | + | ||
57 | + grid.layout(context, box.size); | ||
58 | + } | ||
59 | + | ||
60 | + @override | ||
61 | + void paint(Context context) { | ||
62 | + super.paint(context); | ||
63 | + | ||
64 | + final Matrix4 mat = Matrix4.identity(); | ||
65 | + mat.translate(box.x, box.y); | ||
66 | + context.canvas | ||
67 | + ..saveContext() | ||
68 | + ..setTransform(mat); | ||
69 | + | ||
70 | + grid.paintBackground(context, box.size); | ||
71 | + for (DataSet dataSet in data) { | ||
72 | + dataSet.paintBackground(context, grid); | ||
73 | + } | ||
74 | + for (DataSet dataSet in data) { | ||
75 | + dataSet.paintForeground(context, grid); | ||
76 | + } | ||
77 | + grid.paintForeground(context, box.size); | ||
78 | + context.canvas.restoreContext(); | ||
79 | + } | ||
80 | +} | ||
81 | + | ||
82 | +abstract class ChartGrid { | ||
83 | + void layout(Context context, PdfPoint size); | ||
84 | + void paintBackground(Context context, PdfPoint size); | ||
85 | + void paintForeground(Context context, PdfPoint size); | ||
86 | + | ||
87 | + PdfPoint tochart(PdfPoint p); | ||
88 | +} | ||
89 | + | ||
90 | +@immutable | ||
91 | +abstract class ChartValue { | ||
92 | + const ChartValue(); | ||
93 | +} | ||
94 | + | ||
95 | +abstract class DataSet { | ||
96 | + void paintBackground(Context context, ChartGrid grid); | ||
97 | + void paintForeground(Context context, ChartGrid grid); | ||
98 | +} |
pdf/lib/widgets/chart/line_chart.dart
0 → 100644
1 | +/* | ||
2 | + * Copyright (C) 2017, David PHAM-VAN <dev.nfet.net@gmail.com> | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | + | ||
17 | +// ignore_for_file: omit_local_variable_types | ||
18 | + | ||
19 | +part of widget; | ||
20 | + | ||
21 | +class LineChartValue extends ChartValue { | ||
22 | + const LineChartValue(this.x, this.y); | ||
23 | + final double x; | ||
24 | + final double y; | ||
25 | + | ||
26 | + PdfPoint get point => PdfPoint(x, y); | ||
27 | +} | ||
28 | + | ||
29 | +class LineDataSet extends DataSet { | ||
30 | + LineDataSet({ | ||
31 | + @required this.data, | ||
32 | + this.pointColor, | ||
33 | + this.pointSize = 3, | ||
34 | + this.color = PdfColors.blue, | ||
35 | + this.lineWidth = 2, | ||
36 | + this.drawLine = true, | ||
37 | + this.drawPoints = true, | ||
38 | + this.isCurved = false, | ||
39 | + this.smoothness = 0.35, | ||
40 | + }) : assert(drawLine || drawPoints); | ||
41 | + | ||
42 | + final List<LineChartValue> data; | ||
43 | + final PdfColor pointColor; | ||
44 | + final double pointSize; | ||
45 | + final PdfColor color; | ||
46 | + final double lineWidth; | ||
47 | + final bool drawLine; | ||
48 | + final bool drawPoints; | ||
49 | + final bool isCurved; | ||
50 | + final double smoothness; | ||
51 | + | ||
52 | + void _drawLine(Context context, ChartGrid grid) { | ||
53 | + if (data.length < 2) { | ||
54 | + return; | ||
55 | + } | ||
56 | + | ||
57 | + PdfPoint t = const PdfPoint(0, 0); | ||
58 | + | ||
59 | + final PdfPoint p = grid.tochart(data.first.point); | ||
60 | + context.canvas.moveTo(p.x, p.y); | ||
61 | + | ||
62 | + for (int i = 1; i < data.length; i++) { | ||
63 | + final PdfPoint p = grid.tochart(data[i].point); | ||
64 | + | ||
65 | + if (!isCurved) { | ||
66 | + context.canvas.lineTo(p.x, p.y); | ||
67 | + continue; | ||
68 | + } | ||
69 | + | ||
70 | + final PdfPoint pp = grid.tochart(data[i - 1].point); | ||
71 | + final PdfPoint pn = | ||
72 | + grid.tochart(data[i + 1 < data.length ? i + 1 : i].point); | ||
73 | + | ||
74 | + final PdfPoint c1 = PdfPoint(pp.x + t.x, pp.y + t.y); | ||
75 | + | ||
76 | + t = PdfPoint( | ||
77 | + (pn.x - pp.x) / 2 * smoothness, (pn.y - pp.y) / 2 * smoothness); | ||
78 | + | ||
79 | + final PdfPoint c2 = PdfPoint(p.x - t.x, p.y - t.y); | ||
80 | + | ||
81 | + context.canvas.curveTo(c1.x, c1.y, c2.x, c2.y, p.x, p.y); | ||
82 | + } | ||
83 | + } | ||
84 | + | ||
85 | + void _drawPoints(Context context, ChartGrid grid) { | ||
86 | + for (final LineChartValue value in data) { | ||
87 | + final PdfPoint p = grid.tochart(value.point); | ||
88 | + context.canvas.drawEllipse(p.x, p.y, pointSize, pointSize); | ||
89 | + } | ||
90 | + } | ||
91 | + | ||
92 | + @override | ||
93 | + void paintBackground(Context context, ChartGrid grid) {} | ||
94 | + | ||
95 | + @override | ||
96 | + void paintForeground(Context context, ChartGrid grid) { | ||
97 | + if (data.isEmpty) { | ||
98 | + return; | ||
99 | + } | ||
100 | + | ||
101 | + if (drawLine) { | ||
102 | + _drawLine(context, grid); | ||
103 | + | ||
104 | + context.canvas | ||
105 | + ..setStrokeColor(color) | ||
106 | + ..setLineWidth(lineWidth) | ||
107 | + ..setLineCap(PdfLineCap.joinRound) | ||
108 | + ..setLineJoin(PdfLineCap.joinRound) | ||
109 | + ..strokePath(); | ||
110 | + } | ||
111 | + | ||
112 | + if (drawPoints) { | ||
113 | + _drawPoints(context, grid); | ||
114 | + | ||
115 | + context.canvas | ||
116 | + ..setColor(pointColor ?? color) | ||
117 | + ..fillPath(); | ||
118 | + } | ||
119 | + } | ||
120 | +} |
@@ -18,86 +18,6 @@ | @@ -18,86 +18,6 @@ | ||
18 | 18 | ||
19 | part of widget; | 19 | part of widget; |
20 | 20 | ||
21 | -bool _isSortedAscending(List<double> list) { | ||
22 | - double prev = list.first; | ||
23 | - for (double elem in list) { | ||
24 | - if (prev > elem) { | ||
25 | - return false; | ||
26 | - } | ||
27 | - prev = elem; | ||
28 | - } | ||
29 | - return true; | ||
30 | -} | ||
31 | - | ||
32 | -class Chart extends Widget { | ||
33 | - Chart({ | ||
34 | - @required this.grid, | ||
35 | - @required this.data, | ||
36 | - }); | ||
37 | - | ||
38 | - final ChartGrid grid; | ||
39 | - | ||
40 | - final List<DataSet> data; | ||
41 | - | ||
42 | - PdfPoint _computeSize(BoxConstraints constraints) { | ||
43 | - if (constraints.isTight) { | ||
44 | - return constraints.smallest; | ||
45 | - } | ||
46 | - | ||
47 | - double width = constraints.maxWidth; | ||
48 | - double height = constraints.maxHeight; | ||
49 | - | ||
50 | - const double aspectRatio = 1; | ||
51 | - | ||
52 | - if (!width.isFinite) { | ||
53 | - width = height * aspectRatio; | ||
54 | - } | ||
55 | - | ||
56 | - if (!height.isFinite) { | ||
57 | - height = width * aspectRatio; | ||
58 | - } | ||
59 | - | ||
60 | - return constraints.constrain(PdfPoint(width, height)); | ||
61 | - } | ||
62 | - | ||
63 | - @override | ||
64 | - void layout(Context context, BoxConstraints constraints, | ||
65 | - {bool parentUsesSize = false}) { | ||
66 | - box = PdfRect.fromPoints(PdfPoint.zero, _computeSize(constraints)); | ||
67 | - | ||
68 | - grid.layout(context, box.size); | ||
69 | - } | ||
70 | - | ||
71 | - @override | ||
72 | - void paint(Context context) { | ||
73 | - super.paint(context); | ||
74 | - | ||
75 | - final Matrix4 mat = Matrix4.identity(); | ||
76 | - mat.translate(box.x, box.y); | ||
77 | - context.canvas | ||
78 | - ..saveContext() | ||
79 | - ..setTransform(mat); | ||
80 | - | ||
81 | - grid.paintBackground(context, box.size); | ||
82 | - for (DataSet dataSet in data) { | ||
83 | - dataSet.paintBackground(context, grid); | ||
84 | - } | ||
85 | - for (DataSet dataSet in data) { | ||
86 | - dataSet.paintForeground(context, grid); | ||
87 | - } | ||
88 | - grid.paintForeground(context, box.size); | ||
89 | - context.canvas.restoreContext(); | ||
90 | - } | ||
91 | -} | ||
92 | - | ||
93 | -abstract class ChartGrid { | ||
94 | - void layout(Context context, PdfPoint size); | ||
95 | - void paintBackground(Context context, PdfPoint size); | ||
96 | - void paintForeground(Context context, PdfPoint size); | ||
97 | - | ||
98 | - PdfPoint tochart(PdfPoint p); | ||
99 | -} | ||
100 | - | ||
101 | class LinearGrid extends ChartGrid { | 21 | class LinearGrid extends ChartGrid { |
102 | LinearGrid({ | 22 | LinearGrid({ |
103 | @required this.xAxis, | 23 | @required this.xAxis, |
@@ -107,7 +27,7 @@ class LinearGrid extends ChartGrid { | @@ -107,7 +27,7 @@ class LinearGrid extends ChartGrid { | ||
107 | this.textStyle, | 27 | this.textStyle, |
108 | this.lineWidth = 1, | 28 | this.lineWidth = 1, |
109 | this.color = PdfColors.black, | 29 | this.color = PdfColors.black, |
110 | - this.separatorLineWidth = 1, | 30 | + this.separatorLineWidth = .5, |
111 | this.separatorColor = PdfColors.grey, | 31 | this.separatorColor = PdfColors.grey, |
112 | }) : assert(_isSortedAscending(xAxis)), | 32 | }) : assert(_isSortedAscending(xAxis)), |
113 | assert(_isSortedAscending(yAxis)); | 33 | assert(_isSortedAscending(yAxis)); |
@@ -132,6 +52,17 @@ class LinearGrid extends ChartGrid { | @@ -132,6 +52,17 @@ class LinearGrid extends ChartGrid { | ||
132 | double yOffset; | 52 | double yOffset; |
133 | double yTotal; | 53 | double yTotal; |
134 | 54 | ||
55 | + static bool _isSortedAscending(List<double> list) { | ||
56 | + double prev = list.first; | ||
57 | + for (double elem in list) { | ||
58 | + if (prev > elem) { | ||
59 | + return false; | ||
60 | + } | ||
61 | + prev = elem; | ||
62 | + } | ||
63 | + return true; | ||
64 | + } | ||
65 | + | ||
135 | @override | 66 | @override |
136 | PdfPoint tochart(PdfPoint p) { | 67 | PdfPoint tochart(PdfPoint p) { |
137 | return PdfPoint( | 68 | return PdfPoint( |
@@ -216,78 +147,3 @@ class LinearGrid extends ChartGrid { | @@ -216,78 +147,3 @@ class LinearGrid extends ChartGrid { | ||
216 | @override | 147 | @override |
217 | void paintForeground(Context context, PdfPoint size) {} | 148 | void paintForeground(Context context, PdfPoint size) {} |
218 | } | 149 | } |
219 | - | ||
220 | -@immutable | ||
221 | -abstract class ChartValue { | ||
222 | - const ChartValue(); | ||
223 | -} | ||
224 | - | ||
225 | -class LineChartValue extends ChartValue { | ||
226 | - const LineChartValue(this.x, this.y); | ||
227 | - final double x; | ||
228 | - final double y; | ||
229 | - | ||
230 | - PdfPoint get point => PdfPoint(x, y); | ||
231 | -} | ||
232 | - | ||
233 | -abstract class DataSet { | ||
234 | - void paintBackground(Context context, ChartGrid grid); | ||
235 | - void paintForeground(Context context, ChartGrid grid); | ||
236 | -} | ||
237 | - | ||
238 | -class LineDataSet extends DataSet { | ||
239 | - LineDataSet({ | ||
240 | - @required this.data, | ||
241 | - this.pointColor = PdfColors.blue, | ||
242 | - this.pointSize = 3, | ||
243 | - this.lineColor = PdfColors.blue, | ||
244 | - this.lineWidth = 2, | ||
245 | - this.drawLine = true, | ||
246 | - this.drawPoints = true, | ||
247 | - }) : assert(drawLine || drawPoints); | ||
248 | - | ||
249 | - final List<LineChartValue> data; | ||
250 | - final PdfColor pointColor; | ||
251 | - final double pointSize; | ||
252 | - final PdfColor lineColor; | ||
253 | - final double lineWidth; | ||
254 | - final bool drawLine; | ||
255 | - final bool drawPoints; | ||
256 | - | ||
257 | - double maxValue; | ||
258 | - | ||
259 | - @override | ||
260 | - void paintBackground(Context context, ChartGrid grid) { | ||
261 | - if (drawLine) { | ||
262 | - LineChartValue lastValue; | ||
263 | - for (LineChartValue value in data) { | ||
264 | - if (lastValue != null) { | ||
265 | - final PdfPoint p1 = grid.tochart(lastValue.point); | ||
266 | - final PdfPoint p2 = grid.tochart(value.point); | ||
267 | - context.canvas.drawLine(p1.x, p1.y, p2.x, p2.y); | ||
268 | - } | ||
269 | - lastValue = value; | ||
270 | - } | ||
271 | - | ||
272 | - context.canvas | ||
273 | - ..setStrokeColor(lineColor) | ||
274 | - ..setLineWidth(lineWidth) | ||
275 | - ..setLineCap(PdfLineCap.joinRound) | ||
276 | - ..setLineJoin(PdfLineCap.joinRound) | ||
277 | - ..strokePath(); | ||
278 | - } | ||
279 | - | ||
280 | - if (drawPoints) { | ||
281 | - for (LineChartValue value in data) { | ||
282 | - final PdfPoint p = grid.tochart(value.point); | ||
283 | - context.canvas | ||
284 | - ..setColor(pointColor) | ||
285 | - ..drawEllipse(p.x, p.y, pointSize, pointSize) | ||
286 | - ..fillPath(); | ||
287 | - } | ||
288 | - } | ||
289 | - } | ||
290 | - | ||
291 | - @override | ||
292 | - void paintForeground(Context context, ChartGrid grid) {} | ||
293 | -} |
@@ -33,6 +33,7 @@ void main() { | @@ -33,6 +33,7 @@ void main() { | ||
33 | group('LineChart test', () { | 33 | group('LineChart test', () { |
34 | test('Default LineChart', () { | 34 | test('Default LineChart', () { |
35 | pdf.addPage(Page( | 35 | pdf.addPage(Page( |
36 | + pageFormat: PdfPageFormat.standard.landscape, | ||
36 | build: (Context context) => Chart( | 37 | build: (Context context) => Chart( |
37 | grid: LinearGrid( | 38 | grid: LinearGrid( |
38 | xAxis: <double>[0, 1, 2, 3, 4, 5, 6], | 39 | xAxis: <double>[0, 1, 2, 3, 4, 5, 6], |
@@ -53,6 +54,7 @@ void main() { | @@ -53,6 +54,7 @@ void main() { | ||
53 | 54 | ||
54 | test('Default LineChart without lines connecting points', () { | 55 | test('Default LineChart without lines connecting points', () { |
55 | pdf.addPage(Page( | 56 | pdf.addPage(Page( |
57 | + pageFormat: PdfPageFormat.standard.landscape, | ||
56 | build: (Context context) => Chart( | 58 | build: (Context context) => Chart( |
57 | grid: LinearGrid( | 59 | grid: LinearGrid( |
58 | xAxis: <double>[0, 1, 2, 3, 4, 5, 6], | 60 | xAxis: <double>[0, 1, 2, 3, 4, 5, 6], |
@@ -95,6 +97,7 @@ void main() { | @@ -95,6 +97,7 @@ void main() { | ||
95 | 97 | ||
96 | test('ScatterChart with custom points and lines', () { | 98 | test('ScatterChart with custom points and lines', () { |
97 | pdf.addPage(Page( | 99 | pdf.addPage(Page( |
100 | + pageFormat: PdfPageFormat.standard.landscape, | ||
98 | build: (Context context) => Chart( | 101 | build: (Context context) => Chart( |
99 | grid: LinearGrid( | 102 | grid: LinearGrid( |
100 | xAxis: <double>[0, 1, 2, 3, 4, 5, 6], | 103 | xAxis: <double>[0, 1, 2, 3, 4, 5, 6], |
@@ -110,7 +113,7 @@ void main() { | @@ -110,7 +113,7 @@ void main() { | ||
110 | drawLine: false, | 113 | drawLine: false, |
111 | pointColor: PdfColors.red, | 114 | pointColor: PdfColors.red, |
112 | pointSize: 4, | 115 | pointSize: 4, |
113 | - lineColor: PdfColors.purple, | 116 | + color: PdfColors.purple, |
114 | lineWidth: 4, | 117 | lineWidth: 4, |
115 | ), | 118 | ), |
116 | ], | 119 | ], |
@@ -120,6 +123,7 @@ void main() { | @@ -120,6 +123,7 @@ void main() { | ||
120 | 123 | ||
121 | test('ScatterChart with custom size', () { | 124 | test('ScatterChart with custom size', () { |
122 | pdf.addPage(Page( | 125 | pdf.addPage(Page( |
126 | + pageFormat: PdfPageFormat.standard.landscape, | ||
123 | build: (Context context) => SizedBox( | 127 | build: (Context context) => SizedBox( |
124 | width: 200, | 128 | width: 200, |
125 | height: 100, | 129 | height: 100, |
@@ -141,6 +145,29 @@ void main() { | @@ -141,6 +145,29 @@ void main() { | ||
141 | ), | 145 | ), |
142 | )); | 146 | )); |
143 | }); | 147 | }); |
148 | + | ||
149 | + test('LineChart with curved lines', () { | ||
150 | + pdf.addPage(Page( | ||
151 | + pageFormat: PdfPageFormat.standard.landscape, | ||
152 | + build: (Context context) => Chart( | ||
153 | + grid: LinearGrid( | ||
154 | + xAxis: <double>[0, 1, 2, 3, 4, 5, 6], | ||
155 | + yAxis: <double>[0, 3, 6, 9], | ||
156 | + ), | ||
157 | + data: <DataSet>[ | ||
158 | + LineDataSet( | ||
159 | + drawPoints: false, | ||
160 | + isCurved: true, | ||
161 | + data: const <LineChartValue>[ | ||
162 | + LineChartValue(1, 1), | ||
163 | + LineChartValue(3, 7), | ||
164 | + LineChartValue(5, 3), | ||
165 | + ], | ||
166 | + ), | ||
167 | + ], | ||
168 | + ), | ||
169 | + )); | ||
170 | + }); | ||
144 | }); | 171 | }); |
145 | 172 | ||
146 | tearDownAll(() { | 173 | tearDownAll(() { |
No preview for this file type
-
Please register or login to post a comment