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

Side by Side Diff: charted/lib/charts/cartesian_renderers/line_chart_renderer.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 months 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
(Empty)
1 /*
2 * Copyright 2014 Google Inc. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the LICENSE file or at
6 * https://developers.google.com/open-source/licenses/bsd
7 */
8
9 part of charted.charts;
10
11 class LineChartRenderer extends CartesianRendererBase {
12 final Iterable<int> dimensionsUsingBand = const[];
13 final SubscriptionsDisposer _disposer = new SubscriptionsDisposer();
14
15 final bool alwaysAnimate;
16 final bool trackDataPoints;
17 final bool trackOnDimensionAxis;
18 final int quantitativeScaleProximity;
19
20 bool _trackingPointsCreated = false;
21 List _xPositions = [];
22
23 // Currently hovered row/column
24 int _savedOverRow = 0;
25 int _savedOverColumn = 0;
26
27 int currentDataIndex = -1;
28
29 @override
30 final String name = "line-rdr";
31
32 LineChartRenderer({
33 this.alwaysAnimate: false,
34 this.trackDataPoints: true,
35 this.trackOnDimensionAxis: false,
36 this.quantitativeScaleProximity: 5 });
37
38 /*
39 * Returns false if the number of dimension axes on the area is 0.
40 * Otherwise, the first dimension scale is used to render the chart.
41 */
42 @override
43 bool prepare(ChartArea area, ChartSeries series) {
44 _ensureAreaAndSeries(area, series);
45 if (trackDataPoints != false) {
46 _trackPointerInArea();
47 }
48 return area is CartesianArea;
49 }
50
51 @override
52 void draw(Element element, {Future schedulePostRender}) {
53 _ensureReadyToDraw(element);
54
55 var measureScale = area.measureScales(series).first,
56 dimensionScale = area.dimensionScales.first;
57
58 // Create lists of values in measure columns.
59 var lines = series.measures.map((column) {
60 return area.data.rows.map((values) => values[column]).toList();
61 }).toList();
62
63 // We only support one dimension axes, so we always use the
64 // first dimension.
65 var x = area.data.rows.map(
66 (row) => row.elementAt(area.config.dimensions.first)).toList();
67
68 var rangeBandOffset =
69 dimensionScale is OrdinalScale ? dimensionScale.rangeBand / 2 : 0;
70
71 // If tracking data points is enabled, cache location of points that
72 // represent data.
73 if (trackDataPoints) {
74 _xPositions =
75 x.map((val) => dimensionScale.scale(val) + rangeBandOffset).toList();
76 }
77
78 var line = new SvgLine(
79 xValueAccessor: (d, i) => dimensionScale.scale(x[i]) + rangeBandOffset,
80 yValueAccessor: (d, i) => measureScale.scale(d));
81
82 // Add lines and hook up hover and selection events.
83 var svgLines = root.selectAll('.line-rdr-line').data(lines);
84 svgLines.enter.append('path').each((d, i, e) {
85 e.attributes['fill'] = 'none';
86 });
87
88 svgLines.each((d, i, e) {
89 var column = series.measures.elementAt(i),
90 color = colorForColumn(column),
91 filter = filterForColumn(column),
92 styles = stylesForColumn(column);
93 e.attributes
94 ..['d'] = line.path(d, i, e)
95 ..['stroke'] = color
96 ..['class'] = styles.isEmpty
97 ? 'line-rdr-line'
98 : 'line-rdr-line ${styles.join(' ')}'
99 ..['data-column'] = '$column';
100 if (isNullOrEmpty(filter)) {
101 e.attributes.remove('filter');
102 } else {
103 e.attributes['filter'] = filter;
104 }
105 });
106
107 if (area.state != null) {
108 svgLines
109 ..on('click', (d, i, e) => _mouseClickHandler(d, i, e))
110 ..on('mouseover', (d, i, e) => _mouseOverHandler(d, i, e))
111 ..on('mouseout', (d, i, e) => _mouseOutHandler(d, i, e));
112 }
113
114 svgLines.exit.remove();
115 }
116
117 @override
118 void dispose() {
119 if (root == null) return;
120 root.selectAll('.line-rdr-line').remove();
121 root.selectAll('.line-rdr-point').remove();
122 _disposer.dispose();
123 }
124
125 @override
126 void handleStateChanges(List<ChangeRecord> changes) {
127 var lines = host.querySelectorAll('.line-rdr-line');
128 if (lines == null || lines.isEmpty) return;
129
130 for (int i = 0, len = lines.length; i < len; ++i) {
131 var line = lines.elementAt(i),
132 column = int.parse(line.dataset['column']),
133 filter = filterForColumn(column);
134 line.classes.removeAll(ChartState.COLUMN_CLASS_NAMES);
135 line.classes.addAll(stylesForColumn(column));
136 line.attributes['stroke'] = colorForColumn(column);
137
138 if (isNullOrEmpty(filter)) {
139 line.attributes.remove('filter');
140 } else {
141 line.attributes['filter'] = filter;
142 }
143 }
144 }
145
146 void _createTrackingCircles() {
147 var linePoints = root.selectAll('.line-rdr-point').data(series.measures);
148 linePoints.enter.append('circle').each((d, i, e) {
149 e.classes.add('line-rdr-point');
150 e.attributes['r'] = '4';
151 });
152
153 linePoints
154 ..each((d, i, e) {
155 var color = colorForColumn(d);
156 e.attributes
157 ..['r'] = '4'
158 ..['stroke'] = color
159 ..['fill'] = color
160 ..['data-column'] = '$d';
161 })
162 ..on('click', _mouseClickHandler)
163 ..on('mousemove', _mouseOverHandler) // Ensure that we update values
164 ..on('mouseover', _mouseOverHandler)
165 ..on('mouseout', _mouseOutHandler);
166
167 linePoints.exit.remove();
168 _trackingPointsCreated = true;
169 }
170
171 void _showTrackingCircles(int row) {
172 if (_trackingPointsCreated == false) {
173 _createTrackingCircles();
174 }
175
176 var yScale = area.measureScales(series).first;
177 root.selectAll('.line-rdr-point').each((d, i, e) {
178 var x = _xPositions[row],
179 measureVal = area.data.rows.elementAt(row).elementAt(d);
180 if (measureVal != null && measureVal.isFinite) {
181 var color = colorForColumn(d),
182 filter = filterForColumn(d);
183 e.attributes
184 ..['cx'] = '$x'
185 ..['cy'] = '${yScale.scale(measureVal)}'
186 ..['fill'] = color
187 ..['stroke'] = color
188 ..['data-row'] = '$row';
189 e.style
190 ..setProperty('opacity', '1')
191 ..setProperty('visibility', 'visible');
192 if (isNullOrEmpty(filter)) {
193 e.attributes.remove('filter');
194 } else {
195 e.attributes['filter'] = filter;
196 }
197 } else {
198 e.style
199 ..setProperty('opacity', '$EPSILON')
200 ..setProperty('visibility', 'hidden');
201 }
202 });
203 }
204
205 void _hideTrackingCircles() {
206 root.selectAll('.line-rdr-point')
207 ..style('opacity', '0.0')
208 ..style('visibility', 'hidden');
209 }
210
211 int _getNearestRowIndex(double x) {
212 var lastSmallerValue = 0;
213 var chartX = x - area.layout.renderArea.x;
214 for (var i = 0; i < _xPositions.length; i++) {
215 var pos = _xPositions[i];
216 if (pos < chartX) {
217 lastSmallerValue = pos;
218 } else {
219 return i == 0 ? 0 :
220 (chartX - lastSmallerValue <= pos - chartX) ? i - 1 : i;
221 }
222 }
223 return _xPositions.length - 1;
224 }
225
226 void _trackPointerInArea() {
227 _trackingPointsCreated = false;
228 _disposer.add(area.onMouseMove.listen((ChartEvent event) {
229 if (area.layout.renderArea.contains(event.chartX, event.chartY)) {
230 var row = _getNearestRowIndex(event.chartX);
231 window.animationFrame.then((_) => _showTrackingCircles(row));
232 } else {
233 _hideTrackingCircles();
234 }
235 }));
236 _disposer.add(area.onMouseOut.listen((ChartEvent event) {
237 _hideTrackingCircles();
238 }));
239 }
240
241 void _mouseClickHandler(d, int i, Element e) {
242 if (area.state != null) {
243 area.state.select(int.parse(e.dataset['column']));
244 }
245 if (mouseClickController != null && e.tagName == 'circle') {
246 var row = int.parse(e.dataset['row']),
247 column = int.parse(e.dataset['column']);
248 mouseClickController.add(
249 new DefaultChartEventImpl(scope.event, area, series, row, column, d));
250 }
251 }
252
253 void _mouseOverHandler(d, i, e) {
254 if (area.state != null) {
255 area.state.preview = int.parse(e.dataset['column']);
256 }
257 if (mouseOverController != null && e.tagName == 'circle') {
258 _savedOverRow = int.parse(e.dataset['row']);
259 _savedOverColumn = int.parse(e.dataset['column']);
260 mouseOverController.add(new DefaultChartEventImpl(
261 scope.event, area, series, _savedOverRow, _savedOverColumn, d));
262 }
263 }
264
265 void _mouseOutHandler(d, i, e) {
266 if (area.state != null &&
267 area.state.preview == int.parse(e.dataset['column'])) {
268 area.state.preview = null;
269 }
270 if (mouseOutController != null && e.tagName == 'circle') {
271 mouseOutController.add(new DefaultChartEventImpl(
272 scope.event, area, series, _savedOverRow, _savedOverColumn, d));
273 }
274 }
275 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698