Revert "Revert "Adds built-in clamp() and hypot() (#906)""

This reverts commit 76280409bd.
This commit is contained in:
awjin 2020-01-14 11:23:40 -08:00
parent 9f2b75b013
commit 5f72acb14d
3 changed files with 123 additions and 42 deletions

View File

@ -1,3 +1,11 @@
## 1.25.0
* Add functions to the built-in "sass:math" module.
* `clamp()`: given a `$min`, $number`, and `$max` values, clamps the `$number`
in between `$min` and `$max`.
* `hypot()`: given *n* numbers, outputs the length of the *n*-dimensional
vector that has components equal to each of the inputs.
## 1.24.4
### JavaScript API

View File

@ -13,34 +13,60 @@ import '../module/built_in.dart';
import '../util/number.dart';
import '../value.dart';
/// A random number generator.
final _random = math.Random();
/// The global definitions of Sass math functions.
final global = UnmodifiableListView([
_round, _ceil, _floor, _abs, _max, _min, _randomFunction, _unit, //
_percentage,
_abs, _ceil, _floor, _max, _min, _percentage, _randomFunction, _round,
_unit, //
_compatible.withName("comparable"),
_isUnitless.withName("unitless"),
_compatible.withName("comparable")
]);
/// The Sass math module.
final module = BuiltInModule("math", functions: [
_round, _ceil, _floor, _abs, _max, _min, _randomFunction, _unit,
_isUnitless, //
_percentage, _compatible
_abs, _ceil, _clamp, _compatible, _floor, _hypot, _isUnitless, _max, _min, //
_percentage, _randomFunction, _round, _unit,
]);
final _percentage = BuiltInCallable("percentage", r"$number", (arguments) {
var number = arguments[0].assertNumber("number");
number.assertNoUnits("number");
return SassNumber(number.value * 100, '%');
/// Returns a [Callable] named [name] that transforms a number's value
/// using [transform] and preserves its units.
BuiltInCallable _numberFunction(String name, num transform(num value)) {
return BuiltInCallable(name, r"$number", (arguments) {
var number = arguments[0].assertNumber("number");
return SassNumber.withUnits(transform(number.value),
numeratorUnits: number.numeratorUnits,
denominatorUnits: number.denominatorUnits);
});
}
///
/// Bounding functions
///
final _ceil = _numberFunction("ceil", (value) => value.ceil());
final _clamp = BuiltInCallable("clamp", r"$min, $number, $max", (arguments) {
var min = arguments[0].assertNumber("min");
var number = arguments[1].assertNumber("number");
var max = arguments[2].assertNumber("max");
if (min.hasUnits == number.hasUnits && number.hasUnits == max.hasUnits) {
if (min.greaterThanOrEquals(max).isTruthy) return min;
if (min.greaterThanOrEquals(number).isTruthy) return min;
if (number.greaterThanOrEquals(max).isTruthy) return max;
return number;
}
var arg2 = min.hasUnits != number.hasUnits ? number : max;
var arg2Name = min.hasUnits != number.hasUnits ? "\$number" : "\$max";
var unit1 = min.hasUnits ? "has unit ${min.unitString}" : "is unitless";
var unit2 = arg2.hasUnits ? "has unit ${arg2.unitString}" : "is unitless";
throw SassScriptException(
"\$min $unit1 but $arg2Name $unit2. Arguments must all have units or all "
"be unitless.");
});
final _round = _numberFunction("round", fuzzyRound);
final _ceil = _numberFunction("ceil", (value) => value.ceil());
final _floor = _numberFunction("floor", (value) => value.floor());
final _abs = _numberFunction("abs", (value) => value.abs());
final _max = BuiltInCallable("max", r"$numbers...", (arguments) {
SassNumber max;
@ -62,24 +88,51 @@ final _min = BuiltInCallable("min", r"$numbers...", (arguments) {
throw SassScriptException("At least one argument must be passed.");
});
final _randomFunction = BuiltInCallable("random", r"$limit: null", (arguments) {
if (arguments[0] == sassNull) return SassNumber(_random.nextDouble());
var limit = arguments[0].assertNumber("limit").assertInt("limit");
if (limit < 1) {
throw SassScriptException("\$limit: Must be greater than 0, was $limit.");
final _round = _numberFunction("round", fuzzyRound);
///
/// Distance functions
///
final _abs = _numberFunction("abs", (value) => value.abs());
final _hypot = BuiltInCallable("hypot", r"$numbers...", (arguments) {
var numbers =
arguments[0].asList.map((argument) => argument.assertNumber()).toList();
if (numbers.isEmpty) {
throw SassScriptException("At least one argument must be passed.");
}
return SassNumber(_random.nextInt(limit) + 1);
var numeratorUnits = numbers[0].numeratorUnits;
var denominatorUnits = numbers[0].denominatorUnits;
var subtotal = 0.0;
for (var i = 0; i < numbers.length; i++) {
var number = numbers[i];
if (number.hasUnits != numbers[0].hasUnits) {
var unit1 = numbers[0].hasUnits
? "has unit ${numbers[0].unitString}"
: "is unitless";
var unit2 =
number.hasUnits ? "has unit ${number.unitString}" : "is unitless";
throw SassScriptException(
"Argument 1 $unit1 but argument ${i + 1} $unit2. Arguments must all "
"have units or all be unitless.");
}
number = number.coerce(numeratorUnits, denominatorUnits);
subtotal += math.pow(number.value, 2);
}
return SassNumber.withUnits(math.sqrt(subtotal),
numeratorUnits: numeratorUnits, denominatorUnits: denominatorUnits);
});
final _unit = BuiltInCallable("unit", r"$number", (arguments) {
var number = arguments[0].assertNumber("number");
return SassString(number.unitString, quotes: true);
});
final _isUnitless = BuiltInCallable("is-unitless", r"$number", (arguments) {
var number = arguments[0].assertNumber("number");
return SassBoolean(!number.hasUnits);
});
///
/// Unit functions
///
final _compatible =
BuiltInCallable("compatible", r"$number1, $number2", (arguments) {
@ -88,13 +141,33 @@ final _compatible =
return SassBoolean(number1.isComparableTo(number2));
});
/// Returns a [Callable] named [name] that transforms a number's value
/// using [transform] and preserves its units.
BuiltInCallable _numberFunction(String name, num transform(num value)) {
return BuiltInCallable(name, r"$number", (arguments) {
var number = arguments[0].assertNumber("number");
return SassNumber.withUnits(transform(number.value),
numeratorUnits: number.numeratorUnits,
denominatorUnits: number.denominatorUnits);
});
}
final _isUnitless = BuiltInCallable("is-unitless", r"$number", (arguments) {
var number = arguments[0].assertNumber("number");
return SassBoolean(!number.hasUnits);
});
final _unit = BuiltInCallable("unit", r"$number", (arguments) {
var number = arguments[0].assertNumber("number");
return SassString(number.unitString, quotes: true);
});
///
/// Other functions
///
final _percentage = BuiltInCallable("percentage", r"$number", (arguments) {
var number = arguments[0].assertNumber("number");
number.assertNoUnits("number");
return SassNumber(number.value * 100, '%');
});
final _random = math.Random();
final _randomFunction = BuiltInCallable("random", r"$limit: null", (arguments) {
if (arguments[0] == sassNull) return SassNumber(_random.nextDouble());
var limit = arguments[0].assertNumber("limit").assertInt("limit");
if (limit < 1) {
throw SassScriptException("\$limit: Must be greater than 0, was $limit.");
}
return SassNumber(_random.nextInt(limit) + 1);
});

View File

@ -1,5 +1,5 @@
name: sass
version: 1.24.4
version: 1.25.0-dev
description: A Sass implementation in Dart.
author: Sass Team
homepage: https://github.com/sass/dart-sass