Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(567)

Side by Side Diff: packages/charted/lib/charts/cartesian_renderers/bar_chart_renderer.dart

Issue 1521693002: Roll Observatory deps (charted -> ^0.3.0) (Closed) Base URL: https://chromium.googlesource.com/external/github.com/dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 BarChartRenderer extends CartesianRendererBase { 11 class BarChartRenderer extends CartesianRendererBase {
12 static const RADIUS = 2; 12 static const RADIUS = 2;
13 13
14 final Iterable<int> dimensionsUsingBand = const[0]; 14 final Iterable<int> dimensionsUsingBand = const [0];
15 final bool alwaysAnimate; 15 final bool alwaysAnimate;
16 16
17 @override 17 @override
18 final String name = "bar-rdr"; 18 final String name = "bar-rdr";
19 19
20 BarChartRenderer({this.alwaysAnimate: false}); 20 BarChartRenderer({this.alwaysAnimate: false});
21 21
22 /// Returns false if the number of dimension axes on the area is 0. 22 /// Returns false if the number of dimension axes on the area is 0.
23 /// Otherwise, the first dimension scale is used to render the chart. 23 /// Otherwise, the first dimension scale is used to render the chart.
24 @override 24 @override
25 bool prepare(ChartArea area, ChartSeries series) { 25 bool prepare(ChartArea area, ChartSeries series) {
26 _ensureAreaAndSeries(area, series); 26 _ensureAreaAndSeries(area, series);
27 return area is CartesianArea; 27 return area is CartesianArea;
28 } 28 }
29 29
30 @override 30 @override
31 void draw(Element element, {Future schedulePostRender}) { 31 void draw(Element element, {Future schedulePostRender}) {
32 _ensureReadyToDraw(element); 32 _ensureReadyToDraw(element);
33 33
34 var verticalBars = !area.config.isLeftAxisPrimary; 34 var verticalBars = !area.config.isLeftAxisPrimary;
35 35
36 var measuresCount = series.measures.length, 36 var measuresCount = series.measures.length,
37 measureScale = area.measureScales(series).first, 37 measureScale = area.measureScales(series).first,
38 dimensionScale = area.dimensionScales.first; 38 dimensionScale = area.dimensionScales.first;
39 39
40 var rows = new List() 40 var rows = new List()
41 ..addAll(area.data.rows.map((e) => 41 ..addAll(area.data.rows.map((e) => new List.generate(
42 new List.generate( 42 measuresCount, (i) => e[series.measures.elementAt(i)])));
43 measuresCount, (i) => e[series.measures.elementAt(i)])));
44 43
45 var dimensionVals = area.data.rows.map( 44 var dimensionVals = area.data.rows
46 (row) => row.elementAt(area.config.dimensions.first)).toList(); 45 .map((row) => row.elementAt(area.config.dimensions.first))
46 .toList();
47 47
48 var bars = new OrdinalScale() 48 var bars = new OrdinalScale()
49 ..domain = new Range(series.measures.length).toList() 49 ..domain = new Range(series.measures.length).toList()
50 ..rangeRoundBands([0, dimensionScale.rangeBand]); 50 ..rangeRoundBands([0, dimensionScale.rangeBand]);
51 51
52 // Create and update the bar groups. 52 // Create and update the bar groups.
53 53
54 var groups = root.selectAll('.bar-rdr-rowgroup').data(rows); 54 var groups = root.selectAll('.bar-rdr-rowgroup').data(rows);
55 var animateBarGroups = alwaysAnimate || !groups.isEmpty; 55 var animateBarGroups = alwaysAnimate || !groups.isEmpty;
56 56
57 groups.enter.append('g') 57 groups.enter.append('g')
58 ..classed('bar-rdr-rowgroup') 58 ..classed('bar-rdr-rowgroup')
59 ..attrWithCallback('transform', (d, i, c) => verticalBars ? 59 ..attrWithCallback(
60 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)' : 60 'transform',
61 'translate(0, ${dimensionScale.scale(dimensionVals[i])})'); 61 (d, i, c) => verticalBars
62 ? 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)'
63 : 'translate(0, ${dimensionScale.scale(dimensionVals[i])})');
62 groups.attrWithCallback('data-row', (d, i, e) => i); 64 groups.attrWithCallback('data-row', (d, i, e) => i);
63 groups.exit.remove(); 65 groups.exit.remove();
64 66
65 if (animateBarGroups) { 67 if (animateBarGroups) {
66 groups.transition() 68 groups.transition()
67 ..attrWithCallback('transform', (d, i, c) => verticalBars ? 69 ..attrWithCallback(
68 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)' : 70 'transform',
69 'translate(0, ${dimensionScale.scale(dimensionVals[i])})') 71 (d, i, c) => verticalBars
72 ? 'translate(${dimensionScale.scale(dimensionVals[i])}, 0)'
73 : 'translate(0, ${dimensionScale.scale(dimensionVals[i])})')
70 ..duration(theme.transitionDurationMilliseconds); 74 ..duration(theme.transitionDurationMilliseconds);
71 } 75 }
72 76
73 // TODO: Test interactions between stroke width and bar width. 77 // TODO: Test interactions between stroke width and bar width.
74 78
75 var barWidth = bars.rangeBand.abs() - 79 var barWidth = bars.rangeBand.abs() -
76 theme.defaultSeparatorWidth - theme.defaultStrokeWidth, 80 theme.defaultSeparatorWidth -
81 theme.defaultStrokeWidth,
77 strokeWidth = theme.defaultStrokeWidth, 82 strokeWidth = theme.defaultStrokeWidth,
78 strokeWidthOffset = strokeWidth ~/ 2; 83 strokeWidthOffset = strokeWidth ~/ 2;
79 84
80 // Create and update the bars 85 // Create and update the bars
81 // Avoids animation on first render unless alwaysAnimate is set to true. 86 // Avoids animation on first render unless alwaysAnimate is set to true.
82 87
83 var bar = groups.selectAll('.bar-rdr-bar').dataWithCallback( 88 var bar =
84 (d, i, c) => rows[i]), 89 groups.selectAll('.bar-rdr-bar').dataWithCallback((d, i, c) => rows[i]),
85 scaled0 = measureScale.scale(0).round(); 90 scaled0 = measureScale.scale(0).round();
86 91
87 var getBarLength = (d) { 92 var getBarLength = (d) {
88 var scaledVal = measureScale.scale(d).round(), 93 var scaledVal = measureScale.scale(d).round(),
89 ht = verticalBars 94 ht = verticalBars
90 ? (d >= 0 ? scaled0 - scaledVal : scaledVal - scaled0) 95 ? (d >= 0 ? scaled0 - scaledVal : scaledVal - scaled0)
91 : (d >= 0 ? scaledVal - scaled0 : scaled0 - scaledVal); 96 : (d >= 0 ? scaledVal - scaled0 : scaled0 - scaledVal);
92 ht = ht - strokeWidth; 97 ht = ht - strokeWidth;
93 return (ht < 0) ? 0 : ht; 98 return (ht < 0) ? 0 : ht;
94 }; 99 };
95 var getBarPos = (d) { 100 var getBarPos = (d) {
96 var scaledVal = measureScale.scale(d).round(); 101 var scaledVal = measureScale.scale(d).round();
97 return verticalBars 102 return verticalBars
98 ? (d >= 0 ? scaledVal : scaled0) + strokeWidthOffset 103 ? (d >= 0 ? scaledVal : scaled0) + strokeWidthOffset
99 : (d >= 0 ? scaled0 : scaledVal) + strokeWidthOffset; 104 : (d >= 0 ? scaled0 : scaledVal) + strokeWidthOffset;
100 }; 105 };
101 var buildPath = (d, int i, bool animate) { 106 var buildPath = (d, int i, bool animate) {
102 if (d == null || d == 0) return ''; 107 if (d == null || d == 0) return '';
103 if (verticalBars) { 108 if (verticalBars) {
104 var fn = d > 0 ? topRoundedRect : bottomRoundedRect; 109 var fn = d > 0 ? topRoundedRect : bottomRoundedRect;
105 return fn( 110 return fn(
106 bars.scale(i).toInt() + strokeWidthOffset, 111 bars.scale(i).toInt() + strokeWidthOffset,
107 animate ? rect.height : getBarPos(d), 112 animate ? rect.height : getBarPos(d),
108 barWidth, animate ? 0 : getBarLength(d), RADIUS); 113 barWidth,
114 animate ? 0 : getBarLength(d),
115 RADIUS);
109 } else { 116 } else {
110 var fn = d > 0 ? rightRoundedRect : leftRoundedRect; 117 var fn = d > 0 ? rightRoundedRect : leftRoundedRect;
111 return fn( 118 return fn(getBarPos(d), bars.scale(i).toInt() + strokeWidthOffset,
112 getBarPos(d), bars.scale(i).toInt() + strokeWidthOffset,
113 animate ? 0 : getBarLength(d), barWidth, RADIUS); 119 animate ? 0 : getBarLength(d), barWidth, RADIUS);
114 } 120 }
115 }; 121 };
116 122
117 bar.enter.appendWithCallback((d, i, e) { 123 bar.enter.appendWithCallback((d, i, e) {
118 var rect = Namespace.createChildElement('path', e), 124 var rect = Namespace.createChildElement('path', e),
119 measure = series.measures.elementAt(i), 125 measure = series.measures.elementAt(i),
120 row = int.parse(e.dataset['row']), 126 row = int.parse(e.dataset['row']),
121 color = colorForValue(measure, row), 127 color = colorForValue(measure, row),
122 filter = filterForValue(measure, row), 128 filter = filterForValue(measure, row),
123 style = stylesForValue(measure, row); 129 style = stylesForValue(measure, row);
124 130
125 if (!isNullOrEmpty(style)) { 131 if (!isNullOrEmpty(style)) {
126 rect.classes.addAll(style); 132 rect.classes.addAll(style);
127 } 133 }
128 rect.classes.add('bar-rdr-bar'); 134 rect.classes.add('bar-rdr-bar');
129 135
130 rect.attributes 136 rect.attributes
131 ..['d'] = buildPath(d, i, animateBarGroups) 137 ..['d'] = buildPath(d, i, animateBarGroups)
132 ..['stroke-width'] = '${strokeWidth}px' 138 ..['stroke-width'] = '${strokeWidth}px'
133 ..['fill'] = color 139 ..['fill'] = color
134 ..['stroke'] = color; 140 ..['stroke'] = color;
135 141
136 if (!isNullOrEmpty(filter)) { 142 if (!isNullOrEmpty(filter)) {
137 rect.attributes['filter'] = filter; 143 rect.attributes['filter'] = filter;
138 } 144 }
139 if (!animateBarGroups) { 145 if (!animateBarGroups) {
140 rect.attributes['data-column'] = '$measure'; 146 rect.attributes['data-column'] = '$measure';
141 } 147 }
142 return rect; 148 return rect;
143 }) 149 })
144 ..on('click', (d, i, e) => _event(mouseClickController, d, i, e)) 150 ..on('click', (d, i, e) => _event(mouseClickController, d, i, e))
145 ..on('mouseover', (d, i, e) => _event(mouseOverController, d, i, e)) 151 ..on('mouseover', (d, i, e) => _event(mouseOverController, d, i, e))
146 ..on('mouseout', (d, i, e) => _event(mouseOutController, d, i, e)); 152 ..on('mouseout', (d, i, e) => _event(mouseOutController, d, i, e));
147 153
148 if (animateBarGroups) { 154 if (animateBarGroups) {
149 bar.each((d, i, e) { 155 bar.each((d, i, e) {
150 var measure = series.measures.elementAt(i), 156 var measure = series.measures.elementAt(i),
151 row = int.parse(e.parent.dataset['row']), 157 row = int.parse(e.parent.dataset['row']),
152 color = colorForValue(measure, row), 158 color = colorForValue(measure, row),
153 filter = filterForValue(measure, row), 159 filter = filterForValue(measure, row),
154 styles = stylesForValue(measure, row); 160 styles = stylesForValue(measure, row);
155 e.attributes 161 e.attributes
156 ..['data-column'] = '$measure' 162 ..['data-column'] = '$measure'
157 ..['fill'] = color 163 ..['fill'] = color
158 ..['stroke'] = color; 164 ..['stroke'] = color;
159 e.classes 165 e.classes
160 ..removeAll(ChartState.VALUE_CLASS_NAMES) 166 ..removeAll(ChartState.VALUE_CLASS_NAMES)
161 ..addAll(styles); 167 ..addAll(styles);
162 if (isNullOrEmpty(filter)) { 168 if (isNullOrEmpty(filter)) {
163 e.attributes.remove('filter'); 169 e.attributes.remove('filter');
164 } else { 170 } else {
165 e.attributes['filter'] = filter; 171 e.attributes['filter'] = filter;
166 } 172 }
167 }); 173 });
168 174
169 bar.transition() 175 bar.transition()
170 ..attrWithCallback('d', 176 ..attrWithCallback('d', (d, i, e) => buildPath(d, i, false));
171 (d, i, e) => buildPath(d, i, false));
172 } 177 }
173 178
174 bar.exit.remove(); 179 bar.exit.remove();
175 } 180 }
176 181
177 @override 182 @override
178 void dispose() { 183 void dispose() {
179 if (root == null) return; 184 if (root == null) return;
180 root.selectAll('.bar-rdr-rowgroup').remove(); 185 root.selectAll('.bar-rdr-rowgroup').remove();
181 } 186 }
182 187
183 @override 188 @override
184 double get bandInnerPadding { 189 double get bandInnerPadding {
185 assert(series != null && area != null); 190 assert(series != null && area != null);
186 var measuresCount = series.measures.length; 191 var measuresCount = series.measures.length;
187 return measuresCount > 2 ? 1 - (measuresCount / (measuresCount + 1)) : 192 return measuresCount > 2
188 area.theme.getDimensionAxisTheme().axisBandInnerPadding; 193 ? 1 - (measuresCount / (measuresCount + 1))
194 : area.theme.getDimensionAxisTheme().axisBandInnerPadding;
189 } 195 }
190 196
191 @override 197 @override
192 double get bandOuterPadding { 198 double get bandOuterPadding {
193 assert(series != null && area != null); 199 assert(series != null && area != null);
194 return area.theme.getDimensionAxisTheme().axisBandOuterPadding; 200 return area.theme.getDimensionAxisTheme().axisBandOuterPadding;
195 } 201 }
196 202
197 @override 203 @override
198 void handleStateChanges(List<ChangeRecord> changes) { 204 void handleStateChanges(List<ChangeRecord> changes) {
199 var groups = host.querySelectorAll('.bar-rdr-rowgroup'); 205 var groups = host.querySelectorAll('.bar-rdr-rowgroup');
200 if (groups == null || groups.isEmpty) return; 206 if (groups == null || groups.isEmpty) return;
201 207
202 for(int i = 0, len = groups.length; i < len; ++i) { 208 for (int i = 0, len = groups.length; i < len; ++i) {
203 var group = groups.elementAt(i), 209 var group = groups.elementAt(i),
204 bars = group.querySelectorAll('.bar-rdr-bar'), 210 bars = group.querySelectorAll('.bar-rdr-bar'),
205 row = int.parse(group.dataset['row']); 211 row = int.parse(group.dataset['row']);
206 212
207 for(int j = 0, barsCount = bars.length; j < barsCount; ++j) { 213 for (int j = 0, barsCount = bars.length; j < barsCount; ++j) {
208 var bar = bars.elementAt(j), 214 var bar = bars.elementAt(j),
209 column = int.parse(bar.dataset['column']), 215 column = int.parse(bar.dataset['column']),
210 color = colorForValue(column, row), 216 color = colorForValue(column, row),
211 filter = filterForValue(column, row); 217 filter = filterForValue(column, row);
212 218
213 bar.classes.removeAll(ChartState.VALUE_CLASS_NAMES); 219 bar.classes.removeAll(ChartState.VALUE_CLASS_NAMES);
214 bar.classes.addAll(stylesForValue(column, row)); 220 bar.classes.addAll(stylesForValue(column, row));
215 bar.attributes 221 bar.attributes
216 ..['fill'] = color 222 ..['fill'] = color
217 ..['stroke'] = color; 223 ..['stroke'] = color;
218 if (isNullOrEmpty(filter)) { 224 if (isNullOrEmpty(filter)) {
219 bar.attributes.remove('filter'); 225 bar.attributes.remove('filter');
220 } else { 226 } else {
221 bar.attributes['filter'] = filter; 227 bar.attributes['filter'] = filter;
222 } 228 }
223 } 229 }
224 } 230 }
225 } 231 }
226 232
227 void _event(StreamController controller, data, int index, Element e) { 233 void _event(StreamController controller, data, int index, Element e) {
228 if (controller == null) return; 234 if (controller == null) return;
229 var rowStr = e.parent.dataset['row']; 235 var rowStr = e.parent.dataset['row'];
230 var row = rowStr != null ? int.parse(rowStr) : null; 236 var row = rowStr != null ? int.parse(rowStr) : null;
231 controller.add( 237 controller.add(new DefaultChartEventImpl(scope.event, area, series, row,
232 new DefaultChartEventImpl(scope.event, area, 238 series.measures.elementAt(index), data));
233 series, row, series.measures.elementAt(index), data));
234 } 239 }
235 } 240 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698