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

Side by Side Diff: charted/lib/svg/axis.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
« no previous file with comments | « charted/lib/selection/transition.dart ('k') | charted/lib/svg/shapes.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 library charted.svg.axis;
10
11 import 'dart:html' show Element;
12 import 'dart:math' as math;
13
14 import 'package:charted/core/scales.dart';
15 import 'package:charted/core/utils.dart';
16 import 'package:charted/selection/selection.dart';
17
18 ///
19 /// [SvgAxis] helps draw chart axes based on a given scale.
20 ///
21 class SvgAxis {
22 /// Store of axis roots mapped to currently used scales
23 static final _scales = new Expando<Scale>();
24
25 /// Orientation of the axis. Defaults to [ORIENTATION_BOTTOM].
26 final String orientation;
27
28 /// Scale used on this axis
29 final Scale scale;
30
31 /// Size of all inner ticks
32 final num innerTickSize;
33
34 /// Size of the outer two ticks
35 final num outerTickSize;
36
37 /// Padding on the ticks
38 final num tickPadding;
39
40 /// List of values to be used on the ticks
41 List _tickValues;
42
43 /// Formatter for the tick labels
44 FormatFunction _tickFormat;
45
46 SvgAxis({
47 this.orientation: ORIENTATION_BOTTOM,
48 this.innerTickSize: 6,
49 this.outerTickSize: 6,
50 this.tickPadding: 3,
51 Iterable tickValues,
52 FormatFunction tickFormat,
53 Scale scale }) : scale = scale == null ? new LinearScale() : scale {
54 _tickFormat = tickFormat == null
55 ? this.scale.createTickFormatter()
56 : tickFormat;
57 _tickValues = isNullOrEmpty(tickValues) ? this.scale.ticks : tickValues;
58 }
59
60 Iterable get tickValues => _tickValues;
61
62 FormatFunction get tickFormat => _tickFormat;
63
64 /// Draw an axis on each non-null element in selection
65 draw(Selection g, {SvgAxisTicks axisTicksBuilder, bool isRTL: false}) =>
66 g.each((d, i, e) => create(
67 e, g.scope, axisTicksBuilder: axisTicksBuilder, isRTL: isRTL));
68
69 /// Create an axis on [element].
70 create(Element element, SelectionScope scope, {
71 SvgAxisTicks axisTicksBuilder, bool isRTL: false}) {
72
73 var group = scope.selectElements([element]),
74 older = _scales[element],
75 current = _scales[element] = scale.clone(),
76 isInitialRender = older == null;
77
78 var isLeft = orientation == ORIENTATION_LEFT,
79 isRight = !isLeft && orientation == ORIENTATION_RIGHT,
80 isVertical = isLeft || isRight,
81 isBottom = !isVertical && orientation == ORIENTATION_BOTTOM,
82 isTop = !(isVertical || isBottom) && orientation == ORIENTATION_TOP,
83 isHorizontal = !isVertical;
84
85 if (older == null) older = current;
86 if (axisTicksBuilder == null) {
87 axisTicksBuilder = new SvgAxisTicks();
88 }
89 axisTicksBuilder.init(this);
90
91 var values = axisTicksBuilder.ticks,
92 formatted = axisTicksBuilder.formattedTicks,
93 ellipsized = axisTicksBuilder.shortenedTicks;
94
95 var ticks = group.selectAll('.tick').data(values, current.scale),
96 exit = ticks.exit,
97 transform = isVertical ? _yAxisTransform : _xAxisTransform,
98 sign = isTop || isLeft ? -1 : 1,
99 isEllipsized = ellipsized != formatted;
100
101 var enter = ticks.enter.appendWithCallback((d, i, e) {
102 var group = Namespace.createChildElement('g', e)
103 ..attributes['class'] = 'tick'
104 ..append(Namespace.createChildElement('line', e))
105 ..append(Namespace.createChildElement('text', e)
106 ..attributes['dy'] =
107 isVertical ? '0.32em' : (isBottom ? '0.71em' : '0'));
108 if (!isInitialRender) {
109 group.style.setProperty('opacity', EPSILON.toString());
110 }
111 return group;
112 });
113
114 // All attributes/styles/classes that may change due to theme and scale.
115 // TODO(prsd): Order elements before updating ticks.
116 ticks.each((d, i, e) {
117 Element line = e.firstChild;
118 Element text = e.lastChild;
119 bool isRTLText = false; // FIXME(prsd)
120
121 if (isHorizontal) {
122 line.attributes['y2'] = '${sign * innerTickSize}';
123 text.attributes['y'] =
124 '${sign * (math.max(innerTickSize, 0) + tickPadding)}';
125
126 if (axisTicksBuilder.rotation != 0) {
127 text.attributes
128 ..['transform'] =
129 'rotate(${(isRTL ? -1 : 1) * axisTicksBuilder.rotation})'
130 ..['text-anchor'] = isRTL ? 'end' : 'start';
131 } else {
132 text.attributes
133 ..remove('transform')
134 ..['text-anchor'] = 'middle';
135 }
136 } else {
137 line.attributes['x2'] = '${sign * innerTickSize}';
138 text.attributes
139 ..['x'] = '${sign * (math.max(innerTickSize, 0) + tickPadding)}'
140 ..['text-anchor'] = isLeft
141 ? (isRTLText ? 'start' : 'end')
142 : (isRTLText ? 'end' : 'start');
143 }
144
145 text.text = fixSimpleTextDirection(ellipsized.elementAt(i));
146 if (isEllipsized) {
147 text.attributes['data-detail'] = formatted.elementAt(i);
148 } else {
149 text.attributes.remove('data-detail');
150 }
151
152 if (isInitialRender) {
153 var dx = current is OrdinalScale ? current.rangeBand / 2 : 0;
154 e.attributes['transform'] = isHorizontal
155 ? 'translate(${current.scale(d) + dx},0)'
156 : 'translate(0,${current.scale(d) + dx})';
157 } else {
158 e.style.setProperty('opacity', '1.0');
159 }
160 });
161
162 // Transition existing ticks to right positions
163 if (!isInitialRender) {
164 var transformFn;
165 if (current is OrdinalScale && current.rangeBand != 0) {
166 var dx = current.rangeBand / 2;
167 transformFn = (d) => current.scale(d) + dx;
168 } else if (older is OrdinalScale && older.rangeBand != 0) {
169 older = current;
170 } else {
171 transform(ticks, current.scale);
172 }
173
174 transform(enter, transformFn != null ? transformFn : older.scale);
175 transform(ticks, transformFn != null ? transformFn : current.scale);
176 }
177
178 exit.remove();
179
180 // Append the outer domain.
181 var path = element.querySelector('.domain'),
182 tickSize = sign * outerTickSize,
183 range = current.rangeExtent;
184 if (path == null) {
185 path = Namespace.createChildElement('path', element)
186 ..setAttribute('class', 'domain');
187 }
188 path.attributes['d'] = isLeft || isRight
189 ? 'M${tickSize},${range.min}H0V${range.max}H${tickSize}'
190 : 'M${range.min},${tickSize}V0H${range.max}V${tickSize}';
191 element.append(path);
192 }
193
194 _xAxisTransform(Selection selection, transformFn) {
195 selection.transition()
196 ..attrWithCallback(
197 'transform', (d, i, e) => 'translate(${transformFn(d)},0)');
198 }
199
200 _yAxisTransform(Selection selection, transformFn) {
201 selection.transition()
202 ..attrWithCallback(
203 'transform', (d, i, e) => 'translate(0,${transformFn(d)})');
204 }
205 }
206
207 /// Interface and the default implementation of [SvgAxisTicks].
208 /// SvgAxisTicks provides strategy to handle overlapping ticks on an
209 /// axis. Default implementation assumes that the ticks don't overlap.
210 class SvgAxisTicks {
211 int _rotation = 0;
212 Iterable _ticks;
213 Iterable _formattedTicks;
214
215 void init(SvgAxis axis) {
216 _ticks = axis.tickValues;
217 _formattedTicks = _ticks.map((x) => axis.tickFormat(x));
218 }
219
220 /// When non-zero, indicates the angle by which each tick value must be
221 /// rotated to avoid the overlap.
222 int get rotation => _rotation;
223
224 /// List of ticks that will be displayed on the axis.
225 Iterable get ticks => _ticks;
226
227 /// List of formatted ticks values.
228 Iterable get formattedTicks => _formattedTicks;
229
230 /// List of clipped tick values, if they had to be clipped. Must be same
231 /// as the [formattedTicks] if none of the ticks were ellipsized.
232 Iterable get shortenedTicks => _formattedTicks;
233 }
OLDNEW
« no previous file with comments | « charted/lib/selection/transition.dart ('k') | charted/lib/svg/shapes.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698