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

Side by Side Diff: packages/charted/lib/charts/src/cartesian_area_impl.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 /// Displays either one or two dimension axes and zero or more measure axis. 11 /// Displays either one or two dimension axes and zero or more measure axis.
12 /// The number of measure axes displayed is zero in charts like bubble chart 12 /// The number of measure axes displayed is zero in charts like bubble chart
13 /// which contain two dimension axes. 13 /// which contain two dimension axes.
14 class DefaultCartesianAreaImpl implements CartesianArea { 14 class DefaultCartesianAreaImpl implements CartesianArea {
15 /// Default identifiers used by the measure axes 15 /// Default identifiers used by the measure axes
16 static const MEASURE_AXIS_IDS = const['_default']; 16 static const MEASURE_AXIS_IDS = const ['_default'];
17 17
18 /// Orientations used by measure axes. First, when "x" axis is the primary 18 /// Orientations used by measure axes. First, when "x" axis is the primary
19 /// and the only dimension. Second, when "y" axis is the primary and the only 19 /// and the only dimension. Second, when "y" axis is the primary and the only
20 /// dimension. 20 /// dimension.
21 static const MEASURE_AXIS_ORIENTATIONS = const[ 21 static const MEASURE_AXIS_ORIENTATIONS = const [
22 const[ORIENTATION_LEFT, ORIENTATION_RIGHT], 22 const [ORIENTATION_LEFT, ORIENTATION_RIGHT],
23 const[ORIENTATION_BOTTOM, ORIENTATION_TOP] 23 const [ORIENTATION_BOTTOM, ORIENTATION_TOP]
24 ]; 24 ];
25 25
26 /// Orientations used by the dimension axes. First, when "x" is the 26 /// Orientations used by the dimension axes. First, when "x" is the
27 /// primary dimension and the last one for cases where "y" axis is primary 27 /// primary dimension and the last one for cases where "y" axis is primary
28 /// dimension. 28 /// dimension.
29 static const DIMENSION_AXIS_ORIENTATIONS = const[ 29 static const DIMENSION_AXIS_ORIENTATIONS = const [
30 const[ORIENTATION_BOTTOM, ORIENTATION_LEFT], 30 const [ORIENTATION_BOTTOM, ORIENTATION_LEFT],
31 const[ORIENTATION_LEFT, ORIENTATION_BOTTOM] 31 const [ORIENTATION_LEFT, ORIENTATION_BOTTOM]
32 ]; 32 ];
33 33
34 /// Mapping of measure axis Id to it's axis. 34 /// Mapping of measure axis Id to it's axis.
35 final _measureAxes = new LinkedHashMap<String, DefaultChartAxisImpl>(); 35 final _measureAxes = new LinkedHashMap<String, DefaultChartAxisImpl>();
36 36
37 /// Mapping of dimension column index to it's axis. 37 /// Mapping of dimension column index to it's axis.
38 final _dimensionAxes = new LinkedHashMap<int, DefaultChartAxisImpl>(); 38 final _dimensionAxes = new LinkedHashMap<int, DefaultChartAxisImpl>();
39 39
40 /// Disposer for all change stream subscriptions related to data. 40 /// Disposer for all change stream subscriptions related to data.
41 final _dataEventsDisposer = new SubscriptionsDisposer(); 41 final _dataEventsDisposer = new SubscriptionsDisposer();
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 StreamController<ChartEvent> _valueMouseClickController; 92 StreamController<ChartEvent> _valueMouseClickController;
93 StreamController<ChartArea> _chartAxesUpdatedController; 93 StreamController<ChartArea> _chartAxesUpdatedController;
94 94
95 DefaultCartesianAreaImpl( 95 DefaultCartesianAreaImpl(
96 this.host, 96 this.host,
97 ChartData data, 97 ChartData data,
98 ChartConfig config, 98 ChartConfig config,
99 bool autoUpdate, 99 bool autoUpdate,
100 this.useTwoDimensionAxes, 100 this.useTwoDimensionAxes,
101 this.useRowColoring, 101 this.useRowColoring,
102 this.state) : _autoUpdate = autoUpdate { 102 this.state)
103 : _autoUpdate = autoUpdate {
103 assert(host != null); 104 assert(host != null);
104 assert(isNotInline(host)); 105 assert(isNotInline(host));
105 106
106 this.data = data; 107 this.data = data;
107 this.config = config; 108 this.config = config;
108 theme = new QuantumChartTheme(); 109 theme = new QuantumChartTheme();
109 110
110 Transition.defaultEasingType = theme.transitionEasingType; 111 Transition.defaultEasingType = theme.transitionEasingType;
111 Transition.defaultEasingMode = theme.transitionEasingMode; 112 Transition.defaultEasingMode = theme.transitionEasingMode;
112 Transition.defaultDurationMilliseconds = 113 Transition.defaultDurationMilliseconds =
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
187 } 188 }
188 189
189 @override 190 @override
190 bool get autoUpdate => _autoUpdate; 191 bool get autoUpdate => _autoUpdate;
191 192
192 /// Gets measure axis from cache - creates a new instance of _ChartAxis 193 /// Gets measure axis from cache - creates a new instance of _ChartAxis
193 /// if one was not already created for the given [axisId]. 194 /// if one was not already created for the given [axisId].
194 DefaultChartAxisImpl _getMeasureAxis(String axisId) { 195 DefaultChartAxisImpl _getMeasureAxis(String axisId) {
195 _measureAxes.putIfAbsent(axisId, () { 196 _measureAxes.putIfAbsent(axisId, () {
196 var axisConf = config.getMeasureAxis(axisId), 197 var axisConf = config.getMeasureAxis(axisId),
197 axis = axisConf != null ? 198 axis = axisConf != null
198 new DefaultChartAxisImpl.withAxisConfig(this, axisConf) : 199 ? new DefaultChartAxisImpl.withAxisConfig(this, axisConf)
199 new DefaultChartAxisImpl(this); 200 : new DefaultChartAxisImpl(this);
200 return axis; 201 return axis;
201 }); 202 });
202 return _measureAxes[axisId]; 203 return _measureAxes[axisId];
203 } 204 }
204 205
205 /// Gets a dimension axis from cache - creates a new instance of _ChartAxis 206 /// Gets a dimension axis from cache - creates a new instance of _ChartAxis
206 /// if one was not already created for the given dimension [column]. 207 /// if one was not already created for the given dimension [column].
207 DefaultChartAxisImpl _getDimensionAxis(int column) { 208 DefaultChartAxisImpl _getDimensionAxis(int column) {
208 _dimensionAxes.putIfAbsent(column, () { 209 _dimensionAxes.putIfAbsent(column, () {
209 var axisConf = config.getDimensionAxis(column), 210 var axisConf = config.getDimensionAxis(column),
210 axis = axisConf != null ? 211 axis = axisConf != null
211 new DefaultChartAxisImpl.withAxisConfig(this, axisConf) : 212 ? new DefaultChartAxisImpl.withAxisConfig(this, axisConf)
212 new DefaultChartAxisImpl(this); 213 : new DefaultChartAxisImpl(this);
213 return axis; 214 return axis;
214 }); 215 });
215 return _dimensionAxes[column]; 216 return _dimensionAxes[column];
216 } 217 }
217 218
218 /// All columns rendered by a series must be of the same type. 219 /// All columns rendered by a series must be of the same type.
219 bool _isSeriesValid(ChartSeries s) { 220 bool _isSeriesValid(ChartSeries s) {
220 var first = data.columns.elementAt(s.measures.first).type; 221 var first = data.columns.elementAt(s.measures.first).type;
221 return s.measures.every((i) => 222 return s.measures.every((i) =>
222 (i < data.columns.length) && data.columns.elementAt(i).type == first); 223 (i < data.columns.length) && data.columns.elementAt(i).type == first);
223 } 224 }
224 225
225 @override 226 @override
226 Iterable<Scale> get dimensionScales => 227 Iterable<Scale> get dimensionScales =>
227 config.dimensions.map((int column) => _getDimensionAxis(column).scale); 228 config.dimensions.map((int column) => _getDimensionAxis(column).scale);
228 229
229 @override 230 @override
230 Iterable<Scale> measureScales(ChartSeries series) { 231 Iterable<Scale> measureScales(ChartSeries series) {
231 var axisIds = isNullOrEmpty(series.measureAxisIds) 232 var axisIds = isNullOrEmpty(series.measureAxisIds)
232 ? MEASURE_AXIS_IDS 233 ? MEASURE_AXIS_IDS
233 : series.measureAxisIds; 234 : series.measureAxisIds;
234 return axisIds.map((String id) => _getMeasureAxis(id).scale); 235 return axisIds.map((String id) => _getMeasureAxis(id).scale);
235 } 236 }
236 237
237 /// Computes the size of chart and if changed from the previous time 238 /// Computes the size of chart and if changed from the previous time
238 /// size was computed, sets attributes on svg element 239 /// size was computed, sets attributes on svg element
239 Rect _computeChartSize() { 240 Rect _computeChartSize() {
240 int width = host.clientWidth, 241 int width = host.clientWidth, height = host.clientHeight;
241 height = host.clientHeight;
242 242
243 if (config.minimumSize != null) { 243 if (config.minimumSize != null) {
244 width = max([width, config.minimumSize.width]); 244 width = max([width, config.minimumSize.width]);
245 height = max([height, config.minimumSize.height]); 245 height = max([height, config.minimumSize.height]);
246 } 246 }
247 247
248 AbsoluteRect padding = theme.padding; 248 AbsoluteRect padding = theme.padding;
249 num paddingLeft = config.isRTL ? padding.end : padding.start; 249 num paddingLeft = config.isRTL ? padding.end : padding.start;
250 Rect current = new Rect(paddingLeft, padding.top, 250 Rect current = new Rect(
251 paddingLeft,
252 padding.top,
251 width - (padding.start + padding.end), 253 width - (padding.start + padding.end),
252 height - (padding.top + padding.bottom)); 254 height - (padding.top + padding.bottom));
253 if (layout.chartArea == null || layout.chartArea != current) { 255 if (layout.chartArea == null || layout.chartArea != current) {
254 _svg.attr('width', width.toString()); 256 _svg.attr('width', width.toString());
255 _svg.attr('height', height.toString()); 257 _svg.attr('height', height.toString());
256 layout.chartArea = current; 258 layout.chartArea = current;
257 259
258 var transform = 'translate(${paddingLeft},${padding.top})'; 260 var transform = 'translate(${paddingLeft},${padding.top})';
259 visualization.first.attributes['transform'] = transform; 261 visualization.first.attributes['transform'] = transform;
260 lowerBehaviorPane.first.attributes['transform'] = transform; 262 lowerBehaviorPane.first.attributes['transform'] = transform;
261 upperBehaviorPane.first.attributes['transform'] = transform; 263 upperBehaviorPane.first.attributes['transform'] = transform;
262 } 264 }
263 return layout.chartArea; 265 return layout.chartArea;
264 } 266 }
265 267
266 @override 268 @override
267 draw({bool preRender:false, Future schedulePostRender}) { 269 draw({bool preRender: false, Future schedulePostRender}) {
268 assert(data != null && config != null); 270 assert(data != null && config != null);
269 assert(config.series != null && config.series.isNotEmpty); 271 assert(config.series != null && config.series.isNotEmpty);
270 272
271 // One time initialization. 273 // One time initialization.
272 // Each [ChartArea] has it's own [SelectionScope] 274 // Each [ChartArea] has it's own [SelectionScope]
273 if (_scope == null) { 275 if (_scope == null) {
274 _scope = new SelectionScope.element(host); 276 _scope = new SelectionScope.element(host);
275 _svg = _scope.append('svg:svg')..classed('chart-canvas'); 277 _svg = _scope.append('svg:svg')..classed('chart-canvas');
276 if (!isNullOrEmpty(theme.filters)) { 278 if (!isNullOrEmpty(theme.filters)) {
277 var element = _svg.first, 279 var element = _svg.first,
278 defs = Namespace.createChildElement('defs', element) 280 defs = Namespace.createChildElement('defs', element)
279 ..append(new SvgElement.svg( 281 ..append(new SvgElement.svg(theme.filters,
280 theme.filters, treeSanitizer: new NullTreeSanitizer())); 282 treeSanitizer: new NullTreeSanitizer()));
281 _svg.first.append(defs); 283 _svg.first.append(defs);
282 } 284 }
283 285
284 lowerBehaviorPane = _svg.append('g')..classed('lower-render-pane'); 286 lowerBehaviorPane = _svg.append('g')..classed('lower-render-pane');
285 visualization = _svg.append('g')..classed('chart-render-pane'); 287 visualization = _svg.append('g')..classed('chart-render-pane');
286 upperBehaviorPane = _svg.append('g')..classed('upper-render-pane'); 288 upperBehaviorPane = _svg.append('g')..classed('upper-render-pane');
287 289
288 if (_behaviors.isNotEmpty) { 290 if (_behaviors.isNotEmpty) {
289 _behaviors.forEach( 291 _behaviors
290 (b) => b.init(this, upperBehaviorPane, lowerBehaviorPane)); 292 .forEach((b) => b.init(this, upperBehaviorPane, lowerBehaviorPane));
291 } 293 }
292 } 294 }
293 295
294 // Compute chart sizes and filter out unsupported series 296 // Compute chart sizes and filter out unsupported series
295 _computeChartSize(); 297 _computeChartSize();
296 var series = config.series.where((s) => 298 var series = config.series
297 _isSeriesValid(s) && s.renderer.prepare(this, s)), 299 .where((s) => _isSeriesValid(s) && s.renderer.prepare(this, s)),
298 selection = visualization.selectAll('.series-group'). 300 selection = visualization
299 data(series, (x) => x.hashCode), 301 .selectAll('.series-group')
302 .data(series, (x) => x.hashCode),
300 axesDomainCompleter = new Completer(); 303 axesDomainCompleter = new Completer();
301 304
302 // Wait till the axes are rendered before rendering series. 305 // Wait till the axes are rendered before rendering series.
303 // In an SVG, z-index is based on the order of nodes in the DOM. 306 // In an SVG, z-index is based on the order of nodes in the DOM.
304 axesDomainCompleter.future.then((_) { 307 axesDomainCompleter.future.then((_) {
305 selection.enter.append('svg:g')..classed('series-group'); 308 selection.enter.append('svg:g')..classed('series-group');
306 String transform = 309 String transform =
307 'translate(${layout.renderArea.x},${layout.renderArea.y})'; 310 'translate(${layout.renderArea.x},${layout.renderArea.y})';
308 311
309 selection.each((ChartSeries s, _, Element group) { 312 selection.each((ChartSeries s, _, Element group) {
310 _ChartSeriesInfo info = _seriesInfoCache[s]; 313 _ChartSeriesInfo info = _seriesInfoCache[s];
311 if (info == null) { 314 if (info == null) {
312 info = _seriesInfoCache[s] = new _ChartSeriesInfo(this, s); 315 info = _seriesInfoCache[s] = new _ChartSeriesInfo(this, s);
313 } 316 }
314 info.check(); 317 info.check();
315 group.attributes['transform'] = transform; 318 group.attributes['transform'] = transform;
316 (s.renderer as CartesianRenderer) 319 (s.renderer as CartesianRenderer)
317 .draw(group, schedulePostRender:schedulePostRender); 320 .draw(group, schedulePostRender: schedulePostRender);
318 }); 321 });
319 322
320 // A series that was rendered earlier isn't there anymore, remove it 323 // A series that was rendered earlier isn't there anymore, remove it
321 selection.exit 324 selection.exit
322 ..each((ChartSeries s, _, __) { 325 ..each((ChartSeries s, _, __) {
323 var info = _seriesInfoCache.remove(s); 326 var info = _seriesInfoCache.remove(s);
324 if (info != null) { 327 if (info != null) {
325 info.dispose(); 328 info.dispose();
326 } 329 }
327 }) 330 })
(...skipping 15 matching lines...) Expand all
343 346
344 // Updates the legend if required. 347 // Updates the legend if required.
345 _updateLegend(); 348 _updateLegend();
346 } 349 }
347 350
348 String _orientRTL(String orientation) => orientation; 351 String _orientRTL(String orientation) => orientation;
349 Scale _scaleRTL(Scale scale) => scale; 352 Scale _scaleRTL(Scale scale) => scale;
350 353
351 /// Initialize the axes - required even if the axes are not being displayed. 354 /// Initialize the axes - required even if the axes are not being displayed.
352 _initAxes({bool preRender: false}) { 355 _initAxes({bool preRender: false}) {
353 Map measureAxisUsers = <String,Iterable<ChartSeries>>{}; 356 Map measureAxisUsers = <String, Iterable<ChartSeries>>{};
354 357
355 // Create necessary measures axes. 358 // Create necessary measures axes.
356 // If measure axes were not configured on the series, default is used. 359 // If measure axes were not configured on the series, default is used.
357 _series.forEach((ChartSeries s) { 360 _series.forEach((ChartSeries s) {
358 var measureAxisIds = isNullOrEmpty(s.measureAxisIds) 361 var measureAxisIds =
359 ? MEASURE_AXIS_IDS 362 isNullOrEmpty(s.measureAxisIds) ? MEASURE_AXIS_IDS : s.measureAxisIds;
360 : s.measureAxisIds;
361 measureAxisIds.forEach((axisId) { 363 measureAxisIds.forEach((axisId) {
362 _getMeasureAxis(axisId); // Creates axis if required 364 _getMeasureAxis(axisId); // Creates axis if required
363 var users = measureAxisUsers[axisId]; 365 var users = measureAxisUsers[axisId];
364 if (users == null) { 366 if (users == null) {
365 measureAxisUsers[axisId] = [s]; 367 measureAxisUsers[axisId] = [s];
366 } else { 368 } else {
367 users.add(s); 369 users.add(s);
368 } 370 }
369 }); 371 });
370 }); 372 });
371 373
372 // Now that we know a list of series using each measure axis, configure 374 // Now that we know a list of series using each measure axis, configure
(...skipping 22 matching lines...) Expand all
395 ? [0, 1] 397 ? [0, 1]
396 : (highest < 0 ? [highest, 0] : [0, highest])) 398 : (highest < 0 ? [highest, 0] : [0, highest]))
397 : (lowest <= 0 ? [lowest, highest] : [0, highest]); 399 : (lowest <= 0 ? [lowest, highest] : [0, highest]);
398 } 400 }
399 axis.initAxisDomain(sampleCol, false, domain); 401 axis.initAxisDomain(sampleCol, false, domain);
400 }); 402 });
401 403
402 // Configure dimension axes. 404 // Configure dimension axes.
403 int dimensionAxesCount = useTwoDimensionAxes ? 2 : 1; 405 int dimensionAxesCount = useTwoDimensionAxes ? 2 : 1;
404 config.dimensions.take(dimensionAxesCount).forEach((int column) { 406 config.dimensions.take(dimensionAxesCount).forEach((int column) {
405 var axis = _getDimensionAxis(column), 407 var axis = _getDimensionAxis(column),
406 sampleColumnSpec = data.columns.elementAt(column), 408 sampleColumnSpec = data.columns.elementAt(column),
407 values = data.rows.map((row) => row.elementAt(column)), 409 values = data.rows.map((row) => row.elementAt(column)),
408 domain; 410 domain;
409 411
410 if (sampleColumnSpec.useOrdinalScale) { 412 if (sampleColumnSpec.useOrdinalScale) {
411 domain = values.map((e) => e.toString()).toList(); 413 domain = values.map((e) => e.toString()).toList();
412 } else { 414 } else {
413 var extent = new Extent.items(values); 415 var extent = new Extent.items(values);
414 domain = [extent.min, extent.max]; 416 domain = [extent.min, extent.max];
415 } 417 }
416 axis.initAxisDomain(column, true, domain); 418 axis.initAxisDomain(column, true, domain);
417 }); 419 });
418 420
419 // See if any dimensions need "band" on the axis. 421 // See if any dimensions need "band" on the axis.
420 dimensionsUsingBands.clear(); 422 dimensionsUsingBands.clear();
421 List<bool> usingBands = [false, false]; 423 List<bool> usingBands = [false, false];
422 _series.forEach((ChartSeries s) => 424 _series.forEach((ChartSeries s) =>
423 (s.renderer as CartesianRenderer).dimensionsUsingBand.forEach((x) { 425 (s.renderer as CartesianRenderer).dimensionsUsingBand.forEach((x) {
424 if (x <= 1 && !(usingBands[x])) { 426 if (x <= 1 && !(usingBands[x])) {
425 usingBands[x] = true; 427 usingBands[x] = true;
426 dimensionsUsingBands.add(config.dimensions.elementAt(x)); 428 dimensionsUsingBands.add(config.dimensions.elementAt(x));
427 } 429 }
428 })); 430 }));
429 431
430 // List of measure and dimension axes that are displayed 432 // List of measure and dimension axes that are displayed
431 assert( 433 assert(isNullOrEmpty(config.displayedMeasureAxes) ||
432 isNullOrEmpty(config.displayedMeasureAxes) ||
433 config.displayedMeasureAxes.length < 2); 434 config.displayedMeasureAxes.length < 2);
434 var measureAxesCount = dimensionAxesCount == 1 ? 2 : 0, 435 var measureAxesCount = dimensionAxesCount == 1 ? 2 : 0,
435 displayedMeasureAxes = (isNullOrEmpty(config.displayedMeasureAxes) 436 displayedMeasureAxes = (isNullOrEmpty(config.displayedMeasureAxes)
436 ? _measureAxes.keys.take(measureAxesCount) 437 ? _measureAxes.keys.take(measureAxesCount)
437 : config.displayedMeasureAxes.take(measureAxesCount)). 438 : config.displayedMeasureAxes.take(measureAxesCount))
438 toList(growable: false), 439 .toList(growable: false),
439 displayedDimensionAxes = 440 displayedDimensionAxes =
440 config.dimensions.take(dimensionAxesCount).toList(growable: false); 441 config.dimensions.take(dimensionAxesCount).toList(growable: false);
441 442
442 // Compute size of the dimension axes 443 // Compute size of the dimension axes
443 if (config.renderDimensionAxes != false) { 444 if (config.renderDimensionAxes != false) {
444 var dimensionAxisOrientations = config.isLeftAxisPrimary 445 var dimensionAxisOrientations = config.isLeftAxisPrimary
445 ? DIMENSION_AXIS_ORIENTATIONS.last 446 ? DIMENSION_AXIS_ORIENTATIONS.last
446 : DIMENSION_AXIS_ORIENTATIONS.first; 447 : DIMENSION_AXIS_ORIENTATIONS.first;
447 for (int i = 0, len = displayedDimensionAxes.length; i < len; ++i) { 448 for (int i = 0, len = displayedDimensionAxes.length; i < len; ++i) {
448 var axis = _dimensionAxes[displayedDimensionAxes[i]], 449 var axis = _dimensionAxes[displayedDimensionAxes[i]],
449 orientation = _orientRTL(dimensionAxisOrientations[i]); 450 orientation = _orientRTL(dimensionAxisOrientations[i]);
450 axis.prepareToDraw(orientation); 451 axis.prepareToDraw(orientation);
(...skipping 23 matching lines...) Expand all
474 // all invisible measure scales. 475 // all invisible measure scales.
475 if (_measureAxes.length != displayedMeasureAxes.length) { 476 if (_measureAxes.length != displayedMeasureAxes.length) {
476 _measureAxes.keys.forEach((String axisId) { 477 _measureAxes.keys.forEach((String axisId) {
477 if (displayedMeasureAxes.contains(axisId)) return; 478 if (displayedMeasureAxes.contains(axisId)) return;
478 _getMeasureAxis(axisId).initAxisScale([layout.renderArea.height, 0]); 479 _getMeasureAxis(axisId).initAxisScale([layout.renderArea.height, 0]);
479 }); 480 });
480 } 481 }
481 482
482 // Draw the visible measure axes, if any. 483 // Draw the visible measure axes, if any.
483 if (displayedMeasureAxes.isNotEmpty) { 484 if (displayedMeasureAxes.isNotEmpty) {
484 var axisGroups = visualization. 485 var axisGroups = visualization
485 selectAll('.measure-axis-group').data(displayedMeasureAxes); 486 .selectAll('.measure-axis-group')
487 .data(displayedMeasureAxes);
486 // Update measure axis (add/remove/update) 488 // Update measure axis (add/remove/update)
487 axisGroups.enter.append('svg:g'); 489 axisGroups.enter.append('svg:g');
488 axisGroups.each((axisId, index, group) { 490 axisGroups.each((axisId, index, group) {
489 _getMeasureAxis(axisId).draw(group, _scope, preRender: preRender); 491 _getMeasureAxis(axisId).draw(group, _scope, preRender: preRender);
490 group.attributes['class'] = 'measure-axis-group measure-${index}'; 492 group.attributes['class'] = 'measure-axis-group measure-${index}';
491 }); 493 });
492 axisGroups.exit.remove(); 494 axisGroups.exit.remove();
493 } 495 }
494 496
495 // Draw the dimension axes, unless asked not to. 497 // Draw the dimension axes, unless asked not to.
496 if (config.renderDimensionAxes != false) { 498 if (config.renderDimensionAxes != false) {
497 var dimAxisGroups = visualization. 499 var dimAxisGroups = visualization
498 selectAll('.dimension-axis-group').data(displayedDimensionAxes); 500 .selectAll('.dimension-axis-group')
501 .data(displayedDimensionAxes);
499 // Update dimension axes (add/remove/update) 502 // Update dimension axes (add/remove/update)
500 dimAxisGroups.enter.append('svg:g'); 503 dimAxisGroups.enter.append('svg:g');
501 dimAxisGroups.each((column, index, group) { 504 dimAxisGroups.each((column, index, group) {
502 _getDimensionAxis(column).draw(group, _scope, preRender: preRender); 505 _getDimensionAxis(column).draw(group, _scope, preRender: preRender);
503 group.attributes['class'] = 'dimension-axis-group dim-${index}'; 506 group.attributes['class'] = 'dimension-axis-group dim-${index}';
504 }); 507 });
505 dimAxisGroups.exit.remove(); 508 dimAxisGroups.exit.remove();
506 } else { 509 } else {
507 // Initialize scale on invisible axis 510 // Initialize scale on invisible axis
508 var dimensionAxisOrientations = config.isLeftAxisPrimary ? 511 var dimensionAxisOrientations = config.isLeftAxisPrimary
509 DIMENSION_AXIS_ORIENTATIONS.last : DIMENSION_AXIS_ORIENTATIONS.first; 512 ? DIMENSION_AXIS_ORIENTATIONS.last
513 : DIMENSION_AXIS_ORIENTATIONS.first;
510 for (int i = 0; i < dimensionAxesCount; ++i) { 514 for (int i = 0; i < dimensionAxesCount; ++i) {
511 var column = config.dimensions.elementAt(i), 515 var column = config.dimensions.elementAt(i),
512 axis = _dimensionAxes[column], 516 axis = _dimensionAxes[column],
513 orientation = dimensionAxisOrientations[i]; 517 orientation = dimensionAxisOrientations[i];
514 axis.initAxisScale(orientation == ORIENTATION_LEFT ? 518 axis.initAxisScale(orientation == ORIENTATION_LEFT
515 [layout.renderArea.height, 0] : [0, layout.renderArea.width]); 519 ? [layout.renderArea.height, 0]
516 }; 520 : [0, layout.renderArea.width]);
521 }
522 ;
517 } 523 }
518 } 524 }
519 525
520 // Compute chart render area size and positions of all elements 526 // Compute chart render area size and positions of all elements
521 _computeLayout(bool notRenderingAxes) { 527 _computeLayout(bool notRenderingAxes) {
522 if (notRenderingAxes) { 528 if (notRenderingAxes) {
523 layout.renderArea = 529 layout.renderArea =
524 new Rect(0, 0, layout.chartArea.height, layout.chartArea.width); 530 new Rect(0, 0, layout.chartArea.height, layout.chartArea.width);
525 return; 531 return;
526 } 532 }
527 533
528 var top = layout.axes[ORIENTATION_TOP], 534 var top = layout.axes[ORIENTATION_TOP],
529 left = layout.axes[ORIENTATION_LEFT], 535 left = layout.axes[ORIENTATION_LEFT],
530 bottom = layout.axes[ORIENTATION_BOTTOM], 536 bottom = layout.axes[ORIENTATION_BOTTOM],
531 right = layout.axes[ORIENTATION_RIGHT]; 537 right = layout.axes[ORIENTATION_RIGHT];
532 538
533 var renderAreaHeight = layout.chartArea.height - 539 var renderAreaHeight = layout.chartArea.height -
534 (top.height + layout.axes[ORIENTATION_BOTTOM].height), 540 (top.height + layout.axes[ORIENTATION_BOTTOM].height),
535 renderAreaWidth = layout.chartArea.width - 541 renderAreaWidth = layout.chartArea.width -
536 (left.width + layout.axes[ORIENTATION_RIGHT].width); 542 (left.width + layout.axes[ORIENTATION_RIGHT].width);
537 543
538 layout.renderArea = new Rect( 544 layout.renderArea =
539 left.width, top.height, renderAreaWidth, renderAreaHeight); 545 new Rect(left.width, top.height, renderAreaWidth, renderAreaHeight);
540 546
541 layout._axes 547 layout._axes
542 ..[ORIENTATION_TOP] = 548 ..[ORIENTATION_TOP] = new Rect(left.width, 0, renderAreaWidth, top.height)
543 new Rect(left.width, 0, renderAreaWidth, top.height) 549 ..[ORIENTATION_RIGHT] = new Rect(
544 ..[ORIENTATION_RIGHT] = 550 left.width + renderAreaWidth, top.y, right.width, renderAreaHeight)
545 new Rect(left.width + renderAreaWidth, top.y, 551 ..[ORIENTATION_BOTTOM] = new Rect(left.width,
546 right.width, renderAreaHeight) 552 top.height + renderAreaHeight, renderAreaWidth, bottom.height)
547 ..[ORIENTATION_BOTTOM] =
548 new Rect(left.width, top.height + renderAreaHeight,
549 renderAreaWidth, bottom.height)
550 ..[ORIENTATION_LEFT] = 553 ..[ORIENTATION_LEFT] =
551 new Rect( 554 new Rect(left.width, top.height, left.width, renderAreaHeight);
552 left.width, top.height, left.width, renderAreaHeight);
553 } 555 }
554 556
555 // Updates the legend, if configuration changed since the last 557 // Updates the legend, if configuration changed since the last
556 // time the legend was updated. 558 // time the legend was updated.
557 _updateLegend() { 559 _updateLegend() {
558 if (!_pendingLegendUpdate) return; 560 if (!_pendingLegendUpdate) return;
559 if (_config == null || _config.legend == null || _series.isEmpty) return; 561 if (_config == null || _config.legend == null || _series.isEmpty) return;
560 562
561 var legend = <ChartLegendItem>[]; 563 var legend = <ChartLegendItem>[];
562 List seriesByColumn = 564 List seriesByColumn =
563 new List.generate(data.columns.length, (_) => new List()); 565 new List.generate(data.columns.length, (_) => new List());
564 566
565 _series.forEach((s) => 567 _series.forEach((s) => s.measures.forEach((m) => seriesByColumn[m].add(s)));
566 s.measures.forEach((m) => seriesByColumn[m].add(s)));
567 568
568 seriesByColumn.asMap().forEach((int i, List s) { 569 seriesByColumn.asMap().forEach((int i, List s) {
569 if (s.length == 0) return; 570 if (s.length == 0) return;
570 legend.add(new ChartLegendItem( 571 legend.add(new ChartLegendItem(
571 index:i, label:data.columns.elementAt(i).label, series:s, 572 index: i,
572 color:theme.getColorForKey(i))); 573 label: data.columns.elementAt(i).label,
574 series: s,
575 color: theme.getColorForKey(i)));
573 }); 576 });
574 577
575 _config.legend.update(legend, this); 578 _config.legend.update(legend, this);
576 _pendingLegendUpdate = false; 579 _pendingLegendUpdate = false;
577 } 580 }
578 581
579 @override 582 @override
580 Stream<ChartEvent> get onMouseUp => 583 Stream<ChartEvent> get onMouseUp =>
581 host.onMouseUp 584 host.onMouseUp.map((MouseEvent e) => new DefaultChartEventImpl(e, this));
582 .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
583 585
584 @override 586 @override
585 Stream<ChartEvent> get onMouseDown => 587 Stream<ChartEvent> get onMouseDown => host.onMouseDown
586 host.onMouseDown 588 .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
587 .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
588 589
589 @override 590 @override
590 Stream<ChartEvent> get onMouseOver => 591 Stream<ChartEvent> get onMouseOver => host.onMouseOver
591 host.onMouseOver 592 .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
592 .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
593 593
594 @override 594 @override
595 Stream<ChartEvent> get onMouseOut => 595 Stream<ChartEvent> get onMouseOut =>
596 host.onMouseOut 596 host.onMouseOut.map((MouseEvent e) => new DefaultChartEventImpl(e, this));
597 .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
598 597
599 @override 598 @override
600 Stream<ChartEvent> get onMouseMove => 599 Stream<ChartEvent> get onMouseMove => host.onMouseMove
601 host.onMouseMove 600 .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
602 .map((MouseEvent e) => new DefaultChartEventImpl(e, this));
603 601
604 @override 602 @override
605 Stream<ChartEvent> get onValueClick { 603 Stream<ChartEvent> get onValueClick {
606 if (_valueMouseClickController == null) { 604 if (_valueMouseClickController == null) {
607 _valueMouseClickController = new StreamController.broadcast(sync: true); 605 _valueMouseClickController = new StreamController.broadcast(sync: true);
608 } 606 }
609 return _valueMouseClickController.stream; 607 return _valueMouseClickController.stream;
610 } 608 }
611 609
612 @override 610 @override
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
647 if (behavior == null || !_behaviors.contains(behavior)) return; 645 if (behavior == null || !_behaviors.contains(behavior)) return;
648 if (upperBehaviorPane != null && lowerBehaviorPane != null) { 646 if (upperBehaviorPane != null && lowerBehaviorPane != null) {
649 behavior.dispose(); 647 behavior.dispose();
650 } 648 }
651 _behaviors.remove(behavior); 649 _behaviors.remove(behavior);
652 } 650 }
653 } 651 }
654 652
655 class _ChartAreaLayout implements ChartAreaLayout { 653 class _ChartAreaLayout implements ChartAreaLayout {
656 final _axes = <String, Rect>{ 654 final _axes = <String, Rect>{
657 ORIENTATION_LEFT: const Rect(), 655 ORIENTATION_LEFT: const Rect(),
658 ORIENTATION_RIGHT: const Rect(), 656 ORIENTATION_RIGHT: const Rect(),
659 ORIENTATION_TOP: const Rect(), 657 ORIENTATION_TOP: const Rect(),
660 ORIENTATION_BOTTOM: const Rect() 658 ORIENTATION_BOTTOM: const Rect()
661 }; 659 };
662 660
663 UnmodifiableMapView<String, Rect> _axesView; 661 UnmodifiableMapView<String, Rect> _axesView;
664 662
665 @override 663 @override
666 get axes => _axesView; 664 get axes => _axesView;
667 665
668 @override 666 @override
669 Rect renderArea; 667 Rect renderArea;
670 668
671 @override 669 @override
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
706 if (_area._valueMouseOverController != null) { 704 if (_area._valueMouseOverController != null) {
707 _area._valueMouseOverController.add(e); 705 _area._valueMouseOverController.add(e);
708 } 706 }
709 } 707 }
710 708
711 _mouseOut(ChartEvent e) { 709 _mouseOut(ChartEvent e) {
712 var state = _area.state; 710 var state = _area.state;
713 if (state != null) { 711 if (state != null) {
714 var current = state.hovered; 712 var current = state.hovered;
715 if (current != null && 713 if (current != null &&
716 current.first == e.column && current.last == e.row) { 714 current.first == e.column &&
715 current.last == e.row) {
717 state.hovered = null; 716 state.hovered = null;
718 } 717 }
719 } 718 }
720 if (_area._valueMouseOutController != null) { 719 if (_area._valueMouseOutController != null) {
721 _area._valueMouseOutController.add(e); 720 _area._valueMouseOutController.add(e);
722 } 721 }
723 } 722 }
724 723
725 check() { 724 check() {
726 if (_renderer != _series.renderer) { 725 if (_renderer != _series.renderer) {
727 dispose(); 726 dispose();
728 if (_series.renderer is ChartRendererBehaviorSource){ 727 if (_series.renderer is ChartRendererBehaviorSource) {
729 _disposer.addAll([ 728 _disposer.addAll([
730 _series.renderer.onValueClick.listen(_click), 729 _series.renderer.onValueClick.listen(_click),
731 _series.renderer.onValueMouseOver.listen(_mouseOver), 730 _series.renderer.onValueMouseOver.listen(_mouseOver),
732 _series.renderer.onValueMouseOut.listen(_mouseOut) 731 _series.renderer.onValueMouseOut.listen(_mouseOut)
733 ]); 732 ]);
734 } 733 }
735 } 734 }
736 _renderer = _series.renderer; 735 _renderer = _series.renderer;
737 } 736 }
738 737
739 dispose() => _disposer.dispose(); 738 dispose() {
739 _renderer?.dispose();
740 _disposer.dispose();
741 }
740 } 742 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698