OLD | NEW |
1 // | 1 // |
2 // Copyright 2014 Google Inc. All rights reserved. | 2 // Copyright 2014 Google Inc. All rights reserved. |
3 // | 3 // |
4 // Use of this source code is governed by a BSD-style | 4 // Use of this source code is governed by a BSD-style |
5 // license that can be found in the LICENSE file or at | 5 // license that can be found in the LICENSE file or at |
6 // https://developers.google.com/open-source/licenses/bsd | 6 // https://developers.google.com/open-source/licenses/bsd |
7 // | 7 // |
8 | 8 |
9 part of charted.charts; | 9 part of charted.charts; |
10 | 10 |
11 class PieChartRenderer extends LayoutRendererBase { | 11 class PieChartRenderer extends LayoutRendererBase { |
12 static const STATS_PERCENTAGE = 'percentage-only'; | 12 static const STATS_PERCENTAGE = 'percentage-only'; |
13 static const STATS_VALUE = 'value-only'; | 13 static const STATS_VALUE = 'value-only'; |
14 static const STATS_VALUE_PERCENTAGE = 'value-percentage'; | 14 static const STATS_VALUE_PERCENTAGE = 'value-percentage'; |
15 | 15 |
16 final Iterable<int> dimensionsUsingBand = const[]; | 16 final Iterable<int> dimensionsUsingBand = const []; |
17 final String statsMode; | 17 final String statsMode; |
18 final num innerRadiusRatio; | 18 final num innerRadiusRatio; |
19 final int maxSliceCount; | 19 final int maxSliceCount; |
20 final String otherItemsLabel; | 20 final String otherItemsLabel; |
21 final String otherItemsColor; | 21 final String otherItemsColor; |
22 final showLabels; | 22 final showLabels; |
23 final sortDataByValue; | 23 final sortDataByValue; |
24 | 24 |
25 @override | 25 @override |
26 final String name = "pie-rdr"; | 26 final String name = "pie-rdr"; |
27 | 27 |
28 final List<ChartLegendItem> _legend = []; | 28 final List<ChartLegendItem> _legend = []; |
29 | 29 |
30 Iterable otherRow; | 30 Iterable otherRow; |
31 | 31 |
32 PieChartRenderer({ | 32 PieChartRenderer( |
33 num innerRadiusRatio: 0, | 33 {num innerRadiusRatio: 0, |
34 bool showLabels, | 34 bool showLabels, |
35 this.sortDataByValue: true, | 35 this.sortDataByValue: true, |
36 this.statsMode: STATS_PERCENTAGE, | 36 this.statsMode: STATS_PERCENTAGE, |
37 this.maxSliceCount: SMALL_INT_MAX, | 37 this.maxSliceCount: SMALL_INT_MAX, |
38 this.otherItemsLabel: 'Other', | 38 this.otherItemsLabel: 'Other', |
39 this.otherItemsColor: '#EEEEEE'}) | 39 this.otherItemsColor: '#EEEEEE'}) |
40 : showLabels = showLabels == null ? innerRadiusRatio == 0 : showLabels, | 40 : showLabels = showLabels == null ? innerRadiusRatio == 0 : showLabels, |
41 innerRadiusRatio = innerRadiusRatio; | 41 innerRadiusRatio = innerRadiusRatio; |
42 | 42 |
43 /// Returns false if the number of dimension axes != 0. Pie chart can only | 43 /// Returns false if the number of dimension axes != 0. Pie chart can only |
44 /// be rendered on areas with no axes. | 44 /// be rendered on areas with no axes. |
45 @override | 45 @override |
46 bool prepare(ChartArea area, ChartSeries series) { | 46 bool prepare(ChartArea area, ChartSeries series) { |
47 _ensureAreaAndSeries(area, series); | 47 _ensureAreaAndSeries(area, series); |
48 return area is LayoutArea; | 48 return area is LayoutArea; |
49 } | 49 } |
50 | 50 |
51 @override | 51 @override |
52 Iterable<ChartLegendItem> layout( | 52 Iterable<ChartLegendItem> layout(Element element, |
53 Element element, {Future schedulePostRender}) { | 53 {Future schedulePostRender}) { |
54 _ensureReadyToDraw(element); | 54 _ensureReadyToDraw(element); |
55 | 55 |
56 var radius = math.min(rect.width, rect.height) / 2; | 56 var radius = math.min(rect.width, rect.height) / 2; |
57 root.attr('transform', 'translate(${rect.width / 2}, ${rect.height / 2})'); | 57 root.attr('transform', 'translate(${rect.width / 2}, ${rect.height / 2})'); |
58 | 58 |
59 // Pick only items that are valid - non-null and don't have null value | 59 // Pick only items that are valid - non-null and don't have null value |
60 var measure = series.measures.first, | 60 var measure = series.measures.first, |
61 dimension = area.config.dimensions.first, | 61 dimension = area.config.dimensions.first, |
62 indices = new List.generate(area.data.rows.length, (i) => i); | 62 indices = new List.generate(area.data.rows.length, (i) => i); |
63 | 63 |
(...skipping 10 matching lines...) Expand all Loading... |
74 : bRow.elementAt(measure); | 74 : bRow.elementAt(measure); |
75 return bVal.compareTo(aVal); | 75 return bVal.compareTo(aVal); |
76 }); | 76 }); |
77 } | 77 } |
78 | 78 |
79 // Limit items to the passed maxSliceCount | 79 // Limit items to the passed maxSliceCount |
80 if (indices.length > maxSliceCount) { | 80 if (indices.length > maxSliceCount) { |
81 var displayed = indices.take(maxSliceCount).toList(); | 81 var displayed = indices.take(maxSliceCount).toList(); |
82 var otherItemsValue = 0; | 82 var otherItemsValue = 0; |
83 for (int i = displayed.length; i < indices.length; ++i) { | 83 for (int i = displayed.length; i < indices.length; ++i) { |
84 var index = indices.elementAt(i), | 84 var index = indices.elementAt(i), row = area.data.rows.elementAt(index); |
85 row = area.data.rows.elementAt(index); | |
86 otherItemsValue += row == null || row.elementAt(measure) == null | 85 otherItemsValue += row == null || row.elementAt(measure) == null |
87 ? 0 | 86 ? 0 |
88 : row.elementAt(measure); | 87 : row.elementAt(measure); |
89 } | 88 } |
90 otherRow = new List(max([dimension, measure]) + 1) | 89 otherRow = new List(max([dimension, measure]) + 1) |
91 ..[dimension] = otherItemsLabel | 90 ..[dimension] = otherItemsLabel |
92 ..[measure] = otherItemsValue; | 91 ..[measure] = otherItemsValue; |
93 indices = displayed..add(SMALL_INT_MAX); | 92 indices = displayed..add(SMALL_INT_MAX); |
94 } else { | 93 } else { |
95 otherRow = null; | 94 otherRow = null; |
(...skipping 23 matching lines...) Expand all Loading... |
119 if (!isNullOrEmpty(styles)) { | 118 if (!isNullOrEmpty(styles)) { |
120 e.classes.addAll(styles); | 119 e.classes.addAll(styles); |
121 } | 120 } |
122 e.attributes | 121 e.attributes |
123 ..['fill'] = colorForData(d.data, i) | 122 ..['fill'] = colorForData(d.data, i) |
124 ..['d'] = arc.path(d, i, host) | 123 ..['d'] = arc.path(d, i, host) |
125 ..['stroke-width'] = '1px' | 124 ..['stroke-width'] = '1px' |
126 ..['stroke'] = '#ffffff'; | 125 ..['stroke'] = '#ffffff'; |
127 | 126 |
128 e.append( | 127 e.append( |
129 Namespace.createChildElement('text', e) | 128 Namespace.createChildElement('text', e)..classes.add('pie-label')); |
130 ..classes.add('pie-label')); | |
131 }) | 129 }) |
132 ..on('click', (d, i, e) => _event(mouseClickController, d, i, e)) | 130 ..on('click', (d, i, e) => _event(mouseClickController, d, i, e)) |
133 ..on('mouseover', (d, i, e) => _event(mouseOverController, d, i, e)) | 131 ..on('mouseover', (d, i, e) => _event(mouseOverController, d, i, e)) |
134 ..on('mouseout', (d, i, e) => _event(mouseOutController, d, i, e)); | 132 ..on('mouseout', (d, i, e) => _event(mouseOutController, d, i, e)); |
135 | 133 |
136 pie.exit.remove(); | 134 pie.exit.remove(); |
137 | 135 |
138 _legend.clear(); | 136 _legend.clear(); |
139 var items = new List.generate(data.length, (i) { | 137 var items = new List.generate(data.length, (i) { |
140 SvgArcData d = data.elementAt(i); | 138 SvgArcData d = data.elementAt(i); |
141 Iterable row = d.data == SMALL_INT_MAX | 139 Iterable row = |
142 ? otherRow | 140 d.data == SMALL_INT_MAX ? otherRow : area.data.rows.elementAt(d.data); |
143 : area.data.rows.elementAt(d.data); | |
144 | 141 |
145 return new ChartLegendItem(index: d.data, color: colorForData(d.data, i), | 142 return new ChartLegendItem( |
146 label: row.elementAt(dimension), series: [series], | 143 index: d.data, |
147 value: '${(((d.endAngle - d.startAngle) * 50) / math.PI).toStringAsFix
ed(2)}%'); | 144 color: colorForData(d.data, i), |
| 145 label: row.elementAt(dimension), |
| 146 series: [series], |
| 147 value: |
| 148 '${(((d.endAngle - d.startAngle) * 50) / math.PI).toStringAsFixed(
2)}%'); |
148 }); | 149 }); |
149 return _legend..addAll(area.config.isRTL ? items.reversed : items); | 150 return _legend..addAll(area.config.isRTL ? items.reversed : items); |
150 } | 151 } |
151 | 152 |
152 String colorForData(int row, int index) => | 153 String colorForData(int row, int index) => |
153 colorForValue(row, isTail: row == SMALL_INT_MAX); | 154 colorForValue(row, isTail: row == SMALL_INT_MAX); |
154 | 155 |
155 Iterable<String> stylesForData(int row, int i) => | 156 Iterable<String> stylesForData(int row, int i) => |
156 stylesForValue(row, isTail: row == SMALL_INT_MAX); | 157 stylesForValue(row, isTail: row == SMALL_INT_MAX); |
157 | 158 |
(...skipping 11 matching lines...) Expand all Loading... |
169 | 170 |
170 @override | 171 @override |
171 void dispose() { | 172 void dispose() { |
172 if (root == null) return; | 173 if (root == null) return; |
173 root.selectAll('.pie-path').remove(); | 174 root.selectAll('.pie-path').remove(); |
174 } | 175 } |
175 | 176 |
176 void _event(StreamController controller, data, int index, Element e) { | 177 void _event(StreamController controller, data, int index, Element e) { |
177 // Currently, events are not supported on "Other" pie | 178 // Currently, events are not supported on "Other" pie |
178 if (controller == null || data.data == SMALL_INT_MAX) return; | 179 if (controller == null || data.data == SMALL_INT_MAX) return; |
179 controller.add(new DefaultChartEventImpl( | 180 controller.add(new DefaultChartEventImpl(scope.event, area, series, |
180 scope.event, area, series, data.data, series.measures.first, data.value
)); | 181 data.data, series.measures.first, data.value)); |
181 } | 182 } |
182 } | 183 } |
OLD | NEW |