OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 // TODO(regis): Move abstract class pixels to sdk/lib/core/pixels.dart: |
| 6 abstract class pixels extends num { |
| 7 static const pixels NAN = 0px / 0px; |
| 8 static const pixels INFINITY = 1px / 0px; |
| 9 static const pixels NEGATIVE_INFINITY = -INFINITY; |
| 10 // TODO(regis): Expand as needed with API documentation. |
| 11 } |
| 12 |
| 13 class _Pixels implements pixels { |
| 14 static const int _SCALING_BITS = 6; |
| 15 static const int _SCALING_FACTOR = 1 << _SCALING_BITS; |
| 16 static const int _HALF_VALUE = _SCALING_FACTOR >> 1; |
| 17 static const int _NAN_VALUE = (1 << 30) - 1; // Same as kSmiMax32. Positive. |
| 18 static const int _PLUS_INF_VALUE = _NAN_VALUE - 1; // Same as kSmiMax32 - 1. |
| 19 static const int _MINUS_INF_VALUE = -_PLUS_INF_VALUE; // Same as kSmiMin32 +
2. |
| 20 |
| 21 // TODO(regis): Detect _NAN_VALUE, _PLUS_INF_VALUE and _MINUS_INF_VALUE as |
| 22 // operands to all operations and treat as non finite values, rather than min
and max. |
| 23 |
| 24 final int _value; |
| 25 static const pixels _PLUS_ONE = const _Pixels(1 << _SCALING_BITS); |
| 26 static const pixels _MINUS_ONE = const _Pixels(-1 << _SCALING_BITS); |
| 27 static const pixels _PLUS_INF = const _Pixels(_PLUS_INF_VALUE); |
| 28 static const pixels _MINUS_INF = const _Pixels(_MINUS_INF_VALUE); |
| 29 static const pixels _NAN = const _Pixels(_NAN_VALUE); |
| 30 |
| 31 // Argument is already a scaled value in valid range. |
| 32 const _Pixels(int this._value); |
| 33 |
| 34 // Argument is already a scaled value, but not necessarily in valid range. |
| 35 _Pixels._clamp(int this._value) { |
| 36 if (_value > _PLUS_INF_VALUE) _value = _PLUS_INF_VALUE; |
| 37 else if (_value < _MINUS_INF_VALUE) _value = _MINUS_INF_VALUE; |
| 38 } |
| 39 |
| 40 factory _Pixels.fromInteger(int value) => new _Pixels._clamp(value << _SCALING
_BITS); |
| 41 |
| 42 factory _Pixels.fromDouble(double value) => value.toPixels(); |
| 43 |
| 44 _Pixels.fromPixelsLiteral(String value) { |
| 45 final decimalPoint = ".".codeUnits.first; |
| 46 final digit0 = "0".codeUnits.first; |
| 47 final digit9 = "9".codeUnits.first; |
| 48 var numerator = 0; |
| 49 var denominator = 1; |
| 50 bool seenDecimalPoint = false; |
| 51 for (var i = 0; i < value.length; i++) { |
| 52 var c = value.codeUnitAt(i); |
| 53 if (!seenDecimalPoint && c == decimalPoint) { |
| 54 seenDecimalPoint = true; |
| 55 } else if (digit0 <= c && c <= digit9) { |
| 56 numerator = numerator*10 + (c - digit0); |
| 57 if (seenDecimalPoint) denominator *= 10; |
| 58 } else { |
| 59 break; // TODO(regis): Error handling. Expect px or PX here. |
| 60 } |
| 61 } |
| 62 _value = (numerator << _SCALING_BITS) ~/ denominator; |
| 63 if (_value > _PLUS_INF_VALUE) _value = _PLUS_INF_VALUE; |
| 64 } |
| 65 |
| 66 Type get runtimeType => pixels; |
| 67 |
| 68 int get _identityHashCode { |
| 69 return _value; |
| 70 } |
| 71 |
| 72 num operator +(num other) { |
| 73 return other._toDoubleOrPixels()._addFromPixels(this); |
| 74 } |
| 75 pixels _addFromPixels(pixels other) { |
| 76 return other._add(this); |
| 77 } |
| 78 pixels _add(pixels other) { |
| 79 return new _Pixels._clamp(_value + other._value); |
| 80 } |
| 81 |
| 82 num operator -(num other) { |
| 83 return other._toDoubleOrPixels()._subFromPixels(this); |
| 84 } |
| 85 pixels _subFromPixels(pixels other) { |
| 86 return other._sub(this); |
| 87 } |
| 88 pixels _sub(pixels other) { |
| 89 return new _Pixels._clamp(_value - other._value); |
| 90 } |
| 91 |
| 92 num operator *(num other) { |
| 93 return other._toDoubleOrPixels()._mulFromPixels(this); |
| 94 } |
| 95 pixels _mulFromPixels(pixels other) { |
| 96 return other._mul(this); |
| 97 } |
| 98 pixels _mul(pixels other) { |
| 99 // Use rounding. |
| 100 return new _Pixels._clamp((_value*other._value + _HALF_VALUE) >> _SCALING_BI
TS); |
| 101 } |
| 102 |
| 103 num operator /(num other) { |
| 104 return other._toDoubleOrPixels()._divFromPixels(this); |
| 105 } |
| 106 pixels _divFromPixels(pixels other) { |
| 107 return other._div(this); |
| 108 } |
| 109 pixels _div(pixels other) { |
| 110 if (other._value == 0) { |
| 111 if (_value > 0) { |
| 112 return _PLUS_INF; |
| 113 } else if (_value == 0) { |
| 114 return _NAN; |
| 115 } else { |
| 116 return _MINUS_INF; |
| 117 } |
| 118 } |
| 119 return new _Pixels._clamp((_value << _SCALING_BITS) ~/ other._value); |
| 120 } |
| 121 |
| 122 num operator %(num other) { |
| 123 return other._toDoubleOrPixels()._moduloFromPixels(this); |
| 124 } |
| 125 pixels _moduloFromPixels(pixels other) { |
| 126 return other._modulo(this); |
| 127 } |
| 128 pixels _modulo(pixels other) { |
| 129 var q = _div(other); // TODO(regis): Check NAN. |
| 130 if (other._value < 0) { |
| 131 q = q.ceil(); |
| 132 } else { |
| 133 q = q.floor(); |
| 134 } |
| 135 return _sub(q.toPixels()._mul(other)); |
| 136 } |
| 137 |
| 138 int operator ~/(num other) { |
| 139 return other._toDoubleOrPixels()._truncDivFromPixels(this); |
| 140 } |
| 141 int _truncDivFromPixels(pixels other) { |
| 142 return other._truncDiv(this); |
| 143 } |
| 144 int _truncDiv(pixels other) { |
| 145 if (other._value == 0) throw const IntegerDivisionByZeroException(); |
| 146 return _value ~/ other._value; |
| 147 } |
| 148 |
| 149 num remainder(num other) { |
| 150 return other._toDoubleOrPixels()._remainderFromPixels(this); |
| 151 } |
| 152 pixels _remainderFromPixels(pixels other) { |
| 153 return other._remainder(this); |
| 154 } |
| 155 pixels _remainder(pixels other) { |
| 156 if (other._value == 0) throw const IntegerDivisionByZeroException(); |
| 157 return new _Pixels._clamp(_value - other._value*(_value ~/ other._value)); |
| 158 } |
| 159 |
| 160 pixels operator -() { |
| 161 if (isNaN) return this; |
| 162 return new _Pixels(-_value); |
| 163 } |
| 164 |
| 165 bool operator ==(other) { |
| 166 if (!(other is num)) return false; |
| 167 return other._equalToPixels(this); |
| 168 } |
| 169 bool _equalToPixels(pixels other) { |
| 170 return other._equal(this); |
| 171 } |
| 172 bool _equal(pixels other) { |
| 173 return _value == other._value; |
| 174 } |
| 175 |
| 176 bool _equalToInteger(int other) { |
| 177 return _value == (other << _SCALING_BITS); |
| 178 } |
| 179 |
| 180 bool operator <(num other) { |
| 181 return other > this; |
| 182 } |
| 183 bool operator >(num other) { |
| 184 return other._greaterThanFromPixels(this); |
| 185 } |
| 186 bool _greaterThanFromPixels(pixels other) { |
| 187 return other._value > _value; |
| 188 } |
| 189 bool operator >=(num other) { |
| 190 return (this == other) || (this > other); |
| 191 } |
| 192 bool operator <=(num other) { |
| 193 return (this == other) || (this < other); |
| 194 } |
| 195 |
| 196 pixels _addFromInteger(int other) { |
| 197 return new _Pixels._clamp((other << _SCALING_BITS) + _value); |
| 198 } |
| 199 |
| 200 pixels _subFromInteger(int other) { |
| 201 return new _Pixels._clamp((other << _SCALING_BITS) - _value); |
| 202 } |
| 203 |
| 204 pixels _mulFromInteger(int other) { |
| 205 return new _Pixels._clamp(other * _value); |
| 206 } |
| 207 |
| 208 pixels _divFromInteger(int other) { |
| 209 if (_value == 0) { |
| 210 if (other > 0) { |
| 211 return _PLUS_INF; |
| 212 } else if (other == 0) { |
| 213 return _NAN; |
| 214 } else { |
| 215 return _MINUS_INF; |
| 216 } |
| 217 } |
| 218 return new _Pixels._clamp((other << (2*_SCALING_BITS)) ~/ _value); |
| 219 } |
| 220 |
| 221 pixels _moduloFromInteger(int other) { |
| 222 var q = _divFromInteger(other); |
| 223 if (_value < 0) { |
| 224 q = q.ceil(); |
| 225 } else { |
| 226 q = q.floor(); |
| 227 } |
| 228 return _mul(q.toPixels())._subFromInteger(other); |
| 229 } |
| 230 |
| 231 int _truncDivFromInteger(int other) { |
| 232 if (_value == 0) throw const IntegerDivisionByZeroException(); |
| 233 return (other << _SCALING_BITS) ~/ _value; |
| 234 } |
| 235 |
| 236 pixels _remainderFromInteger(int other) { |
| 237 if (_value == 0) throw const IntegerDivisionByZeroException(); |
| 238 return new _Pixels._clamp( |
| 239 (other << _SCALING_BITS) - _value*((other << _SCALING_BITS) ~/ _value)); |
| 240 } |
| 241 |
| 242 bool _greaterThanFromInteger(int other) { |
| 243 return (other << _SCALING_BITS) > _value; |
| 244 } |
| 245 |
| 246 bool get isNegative { |
| 247 return _value < 0; |
| 248 } |
| 249 |
| 250 bool get isInfinite { |
| 251 return (_value == _PLUS_INF_VALUE) || (_value == _MINUS_INF_VALUE); |
| 252 } |
| 253 bool get isNaN => _value == _NAN_VALUE; |
| 254 bool get isFinite { |
| 255 return (_value != _PLUS_INF_VALUE) && |
| 256 (_value != _MINUS_INF_VALUE) && |
| 257 (_value != _NAN_VALUE); |
| 258 } |
| 259 |
| 260 pixels abs() { |
| 261 return isNegative ? -this : this; |
| 262 } |
| 263 |
| 264 pixels get sign { |
| 265 if (_value > 0) return _PLUS_ONE; |
| 266 if (_value < 0) return _MINUS_ONE; |
| 267 return this; |
| 268 } |
| 269 |
| 270 int round() { |
| 271 if (!isFinite) throw new UnsupportedError("Must be finite"); |
| 272 return (_value + _HALF_VALUE) >> _SCALING_BITS; |
| 273 } |
| 274 int floor() { |
| 275 if (!isFinite) throw new UnsupportedError("Must be finite"); |
| 276 return _value >> _SCALING_BITS; |
| 277 } |
| 278 int ceil() { |
| 279 if (!isFinite) throw new UnsupportedError("Must be finite"); |
| 280 return (_value + _SCALING_FACTOR - 1) >> _SCALING_BITS; |
| 281 } |
| 282 int truncate() { |
| 283 if (!isFinite) throw new UnsupportedError("Must be finite"); |
| 284 return (_value + ((_value < 0) ? (_SCALING_FACTOR - 1) : 0)) >> _SCALING_BIT
S; |
| 285 } |
| 286 |
| 287 double roundToDouble() => round().toDouble(); |
| 288 double floorToDouble() => floor().toDouble(); |
| 289 double ceilToDouble() => ceil().toDouble(); |
| 290 double truncateToDouble() => truncate().toDouble(); |
| 291 |
| 292 num clamp(num lowerLimit, num upperLimit) { |
| 293 if (lowerLimit is! num) { |
| 294 throw new ArgumentError.value(lowerLimit, "lowerLimit", "not a number"); |
| 295 } |
| 296 if (upperLimit is! num) { |
| 297 throw new ArgumentError.value(upperLimit, "upperLimit", "not a number"); |
| 298 } |
| 299 |
| 300 if (lowerLimit.compareTo(upperLimit) > 0) { |
| 301 throw new ArgumentError(lowerLimit); |
| 302 } |
| 303 if (lowerLimit.isNaN) return lowerLimit; |
| 304 if (this.compareTo(lowerLimit) < 0) return lowerLimit; |
| 305 if (this.compareTo(upperLimit) > 0) return upperLimit; |
| 306 return this; |
| 307 } |
| 308 |
| 309 int toInt() => truncate(); // TODO(regis): Should we use round() instead? |
| 310 double toDouble() { |
| 311 if (_value == _PLUS_INF_VALUE) return double.INFINITY; |
| 312 if (_value == _MINUS_INF_VALUE) return double.NEGATIVE_INFINITY; |
| 313 if (_value == _NAN_VALUE) return double.NAN; |
| 314 return _value / _SCALING_FACTOR; |
| 315 } |
| 316 pixels toPixels() { return this; } |
| 317 pixels _toDoubleOrPixels() { return this; } |
| 318 num _toBigintIfInteger() { return this; } |
| 319 |
| 320 String toString() => "${toDouble()}px"; |
| 321 |
| 322 int compareTo(num other) { |
| 323 const int EQUAL = 0, LESS = -1, GREATER = 1; |
| 324 if (isNaN) { |
| 325 return GREATER; // TODO(regis): Correct? |
| 326 } |
| 327 if (other is double) { |
| 328 double d = other; |
| 329 if (d.isInfinite) { |
| 330 if (isInfinite) { |
| 331 if (((d == double.NEGATIVE_INFINITY) && (_value < 0)) || |
| 332 ((d == double.INFINITY) && (_value > 0))) { |
| 333 return EQUAL; |
| 334 } |
| 335 } |
| 336 return _value > 0 ? GREATER : LESS; |
| 337 } |
| 338 if (d.isNaN) { |
| 339 return LESS; // TODO(regis): unless this.isNaN? See above. |
| 340 } |
| 341 if ((d == 0.0) && (_value == 0)) { |
| 342 return d.isNegative ? GREATER : EQUAL; |
| 343 } |
| 344 other = other.toPixels(); |
| 345 } |
| 346 if (this < other) { |
| 347 return LESS; |
| 348 } else if (this > other) { |
| 349 return GREATER; |
| 350 } else { |
| 351 return EQUAL; |
| 352 } |
| 353 } |
| 354 } |
| 355 |
OLD | NEW |