| 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 |