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.core.scales; | 9 part of charted.core.scales; |
10 | 10 |
11 /// TimeScale is a linear scale that operates on time. | 11 /// TimeScale is a linear scale that operates on time. |
12 class TimeScale extends LinearScale { | 12 class TimeScale extends LinearScale { |
13 static const _scaleSteps = const [ | 13 static const _scaleSteps = const [ |
14 1e3, // 1-second | 14 1e3, // 1-second |
15 5e3, // 5-second | 15 5e3, // 5-second |
16 15e3, // 15-second | 16 15e3, // 15-second |
17 3e4, // 30-second | 17 3e4, // 30-second |
18 6e4, // 1-minute | 18 6e4, // 1-minute |
19 3e5, // 5-minute | 19 3e5, // 5-minute |
20 9e5, // 15-minute | 20 9e5, // 15-minute |
21 18e5, // 30-minute | 21 18e5, // 30-minute |
22 36e5, // 1-hour | 22 36e5, // 1-hour |
23 108e5, // 3-hour | 23 108e5, // 3-hour |
24 216e5, // 6-hour | 24 216e5, // 6-hour |
25 432e5, // 12-hour | 25 432e5, // 12-hour |
26 864e5, // 1-day | 26 864e5, // 1-day |
27 1728e5, // 2-day | 27 1728e5, // 2-day |
28 6048e5, // 1-week | 28 6048e5, // 1-week |
29 2592e6, // 1-month | 29 2592e6, // 1-month |
30 7776e6, // 3-month | 30 7776e6, // 3-month |
31 31536e6 // 1-year | 31 31536e6 // 1-year |
32 ]; | 32 ]; |
33 | 33 |
34 static final _scaleLocalMethods = [ | 34 static final _scaleLocalMethods = [ |
35 [TimeInterval.second, 1], | 35 [TimeInterval.second, 1], |
36 [TimeInterval.second, 5], | 36 [TimeInterval.second, 5], |
37 [TimeInterval.second, 15], | 37 [TimeInterval.second, 15], |
38 [TimeInterval.second, 30], | 38 [TimeInterval.second, 30], |
39 [TimeInterval.minute, 1], | 39 [TimeInterval.minute, 1], |
40 [TimeInterval.minute, 5], | 40 [TimeInterval.minute, 5], |
41 [TimeInterval.minute, 15], | 41 [TimeInterval.minute, 15], |
42 [TimeInterval.minute, 30], | 42 [TimeInterval.minute, 30], |
43 [TimeInterval.hour, 1], | 43 [TimeInterval.hour, 1], |
44 [TimeInterval.hour, 3], | 44 [TimeInterval.hour, 3], |
45 [TimeInterval.hour, 6], | 45 [TimeInterval.hour, 6], |
46 [TimeInterval.hour, 12], | 46 [TimeInterval.hour, 12], |
47 [TimeInterval.day, 1], | 47 [TimeInterval.day, 1], |
48 [TimeInterval.day, 2], | 48 [TimeInterval.day, 2], |
49 [TimeInterval.week, 1], | 49 [TimeInterval.week, 1], |
50 [TimeInterval.month, 1], | 50 [TimeInterval.month, 1], |
51 [TimeInterval.month, 3], | 51 [TimeInterval.month, 3], |
52 [TimeInterval.year, 1] | 52 [TimeInterval.year, 1] |
53 ]; | 53 ]; |
54 | 54 |
55 static TimeFormatFunction _scaleLocalFormat = new TimeFormat().multi([ | 55 static TimeFormatFunction _scaleLocalFormat = new TimeFormat().multi([ |
56 [".%L", (DateTime d) => d.millisecond > 0], | 56 [".%L", (DateTime d) => d.millisecond > 0], |
57 [":%S", (DateTime d) => d.second > 0], | 57 [":%S", (DateTime d) => d.second > 0], |
58 ["%I:%M", (DateTime d) => d.minute > 0], | 58 ["%I:%M", (DateTime d) => d.minute > 0], |
59 ["%I %p", (DateTime d) => d.hour > 0], | 59 ["%I %p", (DateTime d) => d.hour > 0], |
60 ["%a %d", (DateTime d) => (d.weekday % 7) > 0 && d.day != 1], | 60 ["%a %d", (DateTime d) => (d.weekday % 7) > 0 && d.day != 1], |
61 ["%b %d", (DateTime d) => d.day != 1], | 61 ["%b %d", (DateTime d) => d.day != 1], |
62 ["%B", (DateTime d) => d.month > 1], | 62 ["%B", (DateTime d) => d.month > 1], |
63 ["%Y", (d) => true] | 63 ["%Y", (d) => true] |
64 ]); | 64 ]); |
65 | 65 |
66 TimeScale(); | 66 TimeScale(); |
67 TimeScale._clone(TimeScale source) : super._clone(source); | 67 TimeScale._clone(TimeScale source) : super._clone(source); |
68 | 68 |
69 @override | 69 @override |
70 scale(dynamic val) => | 70 scale(dynamic val) => |
71 super.scale(val is DateTime ? val.millisecondsSinceEpoch : val); | 71 super.scale(val is DateTime ? val.millisecondsSinceEpoch : val); |
72 | 72 |
73 @override | 73 @override |
74 set domain(Iterable value) { | 74 set domain(Iterable value) { |
75 super.domain = value.map( | 75 super.domain = |
76 (d) => d is DateTime ? d.millisecondsSinceEpoch : d).toList(); | 76 value.map((d) => d is DateTime ? d.millisecondsSinceEpoch : d).toList(); |
77 } | 77 } |
78 | 78 |
79 @override | 79 @override |
80 FormatFunction createTickFormatter([String format]) => _scaleLocalFormat; | 80 FormatFunction createTickFormatter([String format]) => _scaleLocalFormat; |
81 | 81 |
82 @override | 82 @override |
83 TimeScale clone() => new TimeScale._clone(this); | 83 TimeScale clone() => new TimeScale._clone(this); |
84 | 84 |
85 List _getTickMethod(Extent extent, int count) { | 85 List _getTickMethod(Extent extent, int count) { |
86 var target = (extent.max - extent.min) / count, | 86 var target = (extent.max - extent.min) / count, |
87 i = ScaleUtils.bisect(_scaleSteps, target); | 87 i = ScaleUtils.bisect(_scaleSteps, target); |
88 | 88 |
89 return i == _scaleSteps.length | 89 return i == _scaleSteps.length |
90 ? [ TimeInterval.year, _linearTickRange( | 90 ? [ |
91 new Extent(extent.min / 31536e6, extent.max / 31536e6)).step ] | 91 TimeInterval.year, |
| 92 _linearTickRange( |
| 93 new Extent(extent.min / 31536e6, extent.max / 31536e6)).step |
| 94 ] |
92 : i == 0 | 95 : i == 0 |
93 ? [ new ScaleMilliSeconds(), _linearTickRange(extent).step ] | 96 ? [new ScaleMilliSeconds(), _linearTickRange(extent).step] |
94 : _scaleLocalMethods[ | 97 : _scaleLocalMethods[target / _scaleSteps[i - 1] < |
95 target / _scaleSteps[i - 1] < _scaleSteps[i] / target ? i - 1 :
i]; | 98 _scaleSteps[i] / target ? i - 1 : i]; |
96 } | 99 } |
97 | 100 |
98 List niceInterval(int ticksCount, [int skip = 1]) { | 101 List niceInterval(int ticksCount, [int skip = 1]) { |
99 var extent = ScaleUtils.extent(domain), | 102 var extent = ScaleUtils.extent(domain), |
100 method = _getTickMethod(extent, ticksCount), | 103 method = _getTickMethod(extent, ticksCount), |
101 interval; | 104 interval; |
102 | 105 |
103 if (method != null) { | 106 if (method != null) { |
104 interval = method[0]; | 107 interval = method[0]; |
105 skip = method[1]; | 108 skip = method[1]; |
106 } | 109 } |
107 | 110 |
108 bool skipped(var date) { | 111 bool skipped(var date) { |
109 if (date is DateTime) date = date.millisecondsSinceEpoch; | 112 if (date is DateTime) date = date.millisecondsSinceEpoch; |
110 return (interval as TimeInterval) | 113 return (interval as TimeInterval).range(date, date + 1, skip).length == 0; |
111 .range(date, date + 1, skip).length == 0; | |
112 } | 114 } |
113 | 115 |
114 if (skip > 1) { | 116 if (skip > 1) { |
115 domain = ScaleUtils.nice(domain, new RoundingFunctions( | 117 domain = ScaleUtils.nice( |
116 (date) { | 118 domain, |
117 while (skipped(date = (interval as TimeInterval).floor(date))) { | 119 new RoundingFunctions((date) { |
118 date = new DateTime.fromMillisecondsSinceEpoch( | 120 while (skipped(date = (interval as TimeInterval).floor(date))) { |
119 date.millisecondsSinceEpoch - 1); | 121 date = new DateTime.fromMillisecondsSinceEpoch( |
120 } | 122 date.millisecondsSinceEpoch - 1); |
121 return date.millisecondsSinceEpoch; | 123 } |
122 }, | 124 return date.millisecondsSinceEpoch; |
123 (date) { | 125 }, (date) { |
124 while (skipped(date = (interval as TimeInterval).ceil(date))) { | 126 while (skipped(date = (interval as TimeInterval).ceil(date))) { |
125 date = new DateTime.fromMillisecondsSinceEpoch( | 127 date = new DateTime.fromMillisecondsSinceEpoch( |
126 date.millisecondsSinceEpoch + 1); | 128 date.millisecondsSinceEpoch + 1); |
127 } | 129 } |
128 return date.millisecondsSinceEpoch; | 130 return date.millisecondsSinceEpoch; |
129 } | 131 })); |
130 )); | |
131 } else { | 132 } else { |
132 domain = ScaleUtils.nice( | 133 domain = ScaleUtils.nice( |
133 domain, new RoundingFunctions( | 134 domain, |
| 135 new RoundingFunctions( |
134 (date) => interval.floor(date).millisecondsSinceEpoch, | 136 (date) => interval.floor(date).millisecondsSinceEpoch, |
135 (date) => interval.ceil(date).millisecondsSinceEpoch)); | 137 (date) => interval.ceil(date).millisecondsSinceEpoch)); |
136 } | 138 } |
137 return domain; | 139 return domain; |
138 } | 140 } |
139 | 141 |
140 @override | 142 @override |
141 set nice(bool value) { | 143 set nice(bool value) { |
142 assert(value != null); | 144 assert(value != null); |
143 if (value != null && _nice != value) { | 145 if (value != null && _nice != value) { |
(...skipping 12 matching lines...) Expand all Loading... |
156 } | 158 } |
157 return interval.range(extent.min, extent.max + 1, skip < 1 ? 1 : skip); | 159 return interval.range(extent.min, extent.max + 1, skip < 1 ? 1 : skip); |
158 } | 160 } |
159 | 161 |
160 @override | 162 @override |
161 List get ticks => ticksInterval(ticksCount); | 163 List get ticks => ticksInterval(ticksCount); |
162 } | 164 } |
163 | 165 |
164 class ScaleMilliSeconds implements TimeInterval { | 166 class ScaleMilliSeconds implements TimeInterval { |
165 DateTime _toDateTime(x) { | 167 DateTime _toDateTime(x) { |
166 assert (x is int || x is DateTime); | 168 assert(x is int || x is DateTime); |
167 return x is num ? new DateTime.fromMillisecondsSinceEpoch(x) : x; | 169 return x is num ? new DateTime.fromMillisecondsSinceEpoch(x) : x; |
168 } | 170 } |
| 171 |
169 DateTime floor(dynamic val) => _toDateTime(val); | 172 DateTime floor(dynamic val) => _toDateTime(val); |
170 DateTime ceil(dynamic val) => _toDateTime(val); | 173 DateTime ceil(dynamic val) => _toDateTime(val); |
171 DateTime round(dynamic val) => _toDateTime(val); | 174 DateTime round(dynamic val) => _toDateTime(val); |
172 | 175 |
173 DateTime offset(dynamic val, num dt) { | 176 DateTime offset(dynamic val, num dt) { |
174 assert(val is int || val is DateTime); | 177 assert(val is int || val is DateTime); |
175 return new DateTime.fromMillisecondsSinceEpoch( | 178 return new DateTime.fromMillisecondsSinceEpoch( |
176 val is int ? val + dt : (val as DateTime).millisecondsSinceEpoch + dt); | 179 val is int ? val + dt : (val as DateTime).millisecondsSinceEpoch + dt); |
177 } | 180 } |
178 | 181 |
179 List range(var t0, var t1, int step) { | 182 List range(var t0, var t1, int step) { |
180 int start = t0 is DateTime ? t0.millisecondsSinceEpoch : t0, | 183 int start = t0 is DateTime ? t0.millisecondsSinceEpoch : t0, |
181 stop = t1 is DateTime ? t1.millisecondsSinceEpoch : t1; | 184 stop = t1 is DateTime ? t1.millisecondsSinceEpoch : t1; |
182 return new Range((start / step).ceil() * step, stop, step).map( | 185 return new Range((start / step).ceil() * step, stop, step) |
183 (d) => new DateTime.fromMillisecondsSinceEpoch(d)).toList(); | 186 .map((d) => new DateTime.fromMillisecondsSinceEpoch(d)) |
| 187 .toList(); |
184 } | 188 } |
185 } | 189 } |
OLD | NEW |