From 216ceb797be6dc17601c299f7be5418b4ef30530 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 22 Mar 2023 17:19:14 -0700 Subject: [PATCH] Update specs for sass/sass#3497 (#1919) --- lib/src/functions/color.dart | 17 +++-- lib/src/value/color.dart | 75 +++++++++++++------ lib/src/value/color/interpolation_method.dart | 40 +--------- 3 files changed, 67 insertions(+), 65 deletions(-) diff --git a/lib/src/functions/color.dart b/lib/src/functions/color.dart index 6c02361f7..b78488139 100644 --- a/lib/src/functions/color.dart +++ b/lib/src/functions/color.dart @@ -614,11 +614,6 @@ Value _invert(List arguments) { } if (fuzzyEquals(weight, 1)) return inverted; - if (!InterpolationMethod.supportedSpaces.contains(space)) { - throw SassScriptException( - "Color space $space can't be used for interpolation.", "space"); - } - return color.interpolate(inverted, InterpolationMethod(space), weight: 1 - weight); } @@ -1290,6 +1285,18 @@ SassColor _colorFromChannels(ColorSpace space, SassNumber? channel0, alpha, fromRgbFunction ? ColorFormat.rgbFunction : null); + case ColorSpace.lab: + case ColorSpace.lch: + case ColorSpace.oklab: + case ColorSpace.oklch: + return SassColor.forSpaceInternal( + space, + _channelFromValue(space.channels[0], channel0) + .andThen((lightness) => fuzzyClamp(lightness, 0, 100)), + _channelFromValue(space.channels[1], channel1), + _channelFromValue(space.channels[2], channel2), + alpha); + default: return SassColor.forSpaceInternal( space, diff --git a/lib/src/value/color.dart b/lib/src/value/color.dart index 85e2bad85..922662591 100644 --- a/lib/src/value/color.dart +++ b/lib/src/value/color.dart @@ -123,7 +123,7 @@ class SassColor extends Value { case ColorSpace.oklab: case ColorSpace.lch: case ColorSpace.oklch: - return fuzzyEquals(channel0, 0); + return fuzzyEquals(channel0, 0) || fuzzyEquals(channel0, 100); default: return false; @@ -156,11 +156,13 @@ class SassColor extends Value { switch (space) { case ColorSpace.lab: case ColorSpace.oklab: - return fuzzyEquals(channel0, 0); + return fuzzyEquals(channel0, 0) || fuzzyEquals(channel0, 100); case ColorSpace.lch: case ColorSpace.oklch: - return fuzzyEquals(channel0, 0) || fuzzyEquals(channel1, 0); + return fuzzyEquals(channel0, 0) || + fuzzyEquals(channel0, 100) || + fuzzyEquals(channel1, 0); default: return false; @@ -285,7 +287,7 @@ class SassColor extends Value { [num? alpha]) => SassColor.forSpaceInternal( ColorSpace.hsl, - hue?.toDouble(), + _normalizeHue(hue?.toDouble()), saturation.andThen((saturation) => fuzzyAssertRange(saturation.toDouble(), 0, 100, "saturation")), lightness.andThen((lightness) => @@ -299,7 +301,7 @@ class SassColor extends Value { [num? alpha]) => SassColor.forSpaceInternal( ColorSpace.hwb, - hue?.toDouble(), + _normalizeHue(hue?.toDouble()), whiteness.andThen((whiteness) => fuzzyAssertRange(whiteness.toDouble(), 0, 100, "whiteness")), blackness.andThen((blackness) => @@ -367,21 +369,39 @@ class SassColor extends Value { /// Throws a [RangeError] if [alpha] isn't between `0` and `1`. factory SassColor.lab(double? lightness, double? a, double? b, [double? alpha]) => - SassColor.forSpaceInternal(ColorSpace.lab, lightness, a, b, alpha); + SassColor.forSpaceInternal( + ColorSpace.lab, + lightness.andThen( + (lightness) => fuzzyAssertRange(lightness, 0, 100, "lightness")), + a, + b, + alpha); /// Creates a color in [ColorSpace.lch]. /// /// Throws a [RangeError] if [alpha] isn't between `0` and `1`. factory SassColor.lch(double? lightness, double? chroma, double? hue, [double? alpha]) => - SassColor.forSpaceInternal(ColorSpace.lch, lightness, chroma, hue, alpha); + SassColor.forSpaceInternal( + ColorSpace.lch, + lightness.andThen( + (lightness) => fuzzyAssertRange(lightness, 0, 100, "lightness")), + chroma, + _normalizeHue(hue), + alpha); /// Creates a color in [ColorSpace.oklab]. /// /// Throws a [RangeError] if [alpha] isn't between `0` and `1`. factory SassColor.oklab(double? lightness, double? a, double? b, [double? alpha]) => - SassColor.forSpaceInternal(ColorSpace.oklab, lightness, a, b, alpha); + SassColor.forSpaceInternal( + ColorSpace.oklab, + lightness.andThen( + (lightness) => fuzzyAssertRange(lightness, 0, 100, "lightness")), + a, + b, + alpha); /// Creates a color in [ColorSpace.oklch]. /// @@ -389,7 +409,12 @@ class SassColor extends Value { factory SassColor.oklch(double? lightness, double? chroma, double? hue, [double? alpha]) => SassColor.forSpaceInternal( - ColorSpace.oklch, lightness, chroma, hue, alpha); + ColorSpace.oklch, + lightness.andThen( + (lightness) => fuzzyAssertRange(lightness, 0, 100, "lightness")), + chroma, + _normalizeHue(hue), + alpha); /// Creates a color in the color space named [space]. /// @@ -401,14 +426,17 @@ class SassColor extends Value { throw RangeError.value(channels.length, "channels.length", 'must be exactly ${space.channels.length} for color space "$space"'); } else { - var clampChannels = space == ColorSpace.hsl || space == ColorSpace.hwb; + var clampChannel0 = space.channels[0].name == "lightness"; + var clampChannel12 = space == ColorSpace.hsl || space == ColorSpace.hwb; return SassColor.forSpaceInternal( space, - channels[0], - clampChannels + clampChannel0 + ? channels[0].andThen((value) => fuzzyClamp(value, 0, 100)) + : channels[0], + clampChannel12 ? channels[1].andThen((value) => fuzzyClamp(value, 0, 100)) : channels[1], - clampChannels + clampChannel12 ? channels[2].andThen((value) => fuzzyClamp(value, 0, 100)) : channels[2], alpha); @@ -432,6 +460,12 @@ class SassColor extends Value { "[BUG] Tried to create " "$_space(${channel0OrNull ?? 'none'}, ${channel1OrNull ?? 'none'}, " "${channel2OrNull ?? 'none'})"); + assert( + space.channels[0].name != "lightness" || + fuzzyCheckRange(channel0, 0, 100) != null, + "[BUG] Tried to create " + "$_space(${channel0OrNull ?? 'none'}, ${channel1OrNull ?? 'none'}, " + "${channel2OrNull ?? 'none'})"); assert(space != ColorSpace.lms); _checkChannel(channel0OrNull, space.channels[0].name); @@ -449,6 +483,12 @@ class SassColor extends Value { } } + /// If [hue] isn't null, normalizes it to the range `[0, 360)`. + static double? _normalizeHue(double? hue) { + if (hue == null) return hue; + return (hue % 360 + 360) % 360; + } + /// @nodoc @internal T accept(ValueVisitor visitor) => visitor.visitColor(this); @@ -894,11 +934,6 @@ class SassColor extends Value { double _interpolateHues( double hue1, double hue2, HueInterpolationMethod method, double weight) { // Algorithms from https://www.w3.org/TR/css-color-4/#hue-interpolation - if (method != HueInterpolationMethod.specified) { - hue1 = (hue1 % 360 + 360) % 360; - hue2 = (hue2 % 360 + 360) % 360; - } - switch (method) { case HueInterpolationMethod.shorter: var difference = hue2 - hue1; @@ -925,10 +960,6 @@ class SassColor extends Value { case HueInterpolationMethod.decreasing: if (hue1 < hue2) hue1 += 360; break; - - case HueInterpolationMethod.specified: - // Use the hues as-is. - break; } return hue1 * weight + hue2 * (1 - weight); diff --git a/lib/src/value/color/interpolation_method.dart b/lib/src/value/color/interpolation_method.dart index eb25e68ac..c77c4f3d6 100644 --- a/lib/src/value/color/interpolation_method.dart +++ b/lib/src/value/color/interpolation_method.dart @@ -2,8 +2,6 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import 'package:meta/meta.dart'; - import '../../exception.dart'; import '../../value.dart'; @@ -14,23 +12,6 @@ import '../../value.dart'; /// /// {@category Value} class InterpolationMethod { - /// The set of color spaces that can be used for color interpolation. - /// - /// @nodoc - @internal - static const supportedSpaces = { - ColorSpace.srgb, - ColorSpace.srgbLinear, - ColorSpace.lab, - ColorSpace.oklab, - ColorSpace.xyzD50, - ColorSpace.xyzD65, - ColorSpace.hsl, - ColorSpace.hwb, - ColorSpace.lch, - ColorSpace.oklch - }; - /// The color space in which to perform the interpolation. final ColorSpace space; @@ -41,10 +22,7 @@ class InterpolationMethod { InterpolationMethod(this.space, [HueInterpolationMethod? hue]) : hue = space.isPolar ? hue ?? HueInterpolationMethod.shorter : null { - if (!supportedSpaces.contains(space)) { - throw ArgumentError( - "Color space $space can't be used for interpolation."); - } else if (!space.isPolar && hue != null) { + if (!space.isPolar && hue != null) { throw ArgumentError( "Hue interpolation method may not be set for rectangular color space " "$space."); @@ -66,11 +44,6 @@ class InterpolationMethod { var space = ColorSpace.fromName( (list.first.assertString(name)..assertUnquoted(name)).text, name); - if (!supportedSpaces.contains(space)) { - throw SassScriptException( - "Color space $space can't be used for interpolation.", name); - } - if (list.length == 1) return InterpolationMethod(space); var hueMethod = HueInterpolationMethod._fromValue(list[1], name); @@ -124,14 +97,7 @@ enum HueInterpolationMethod { /// Angles are adjusted so that `θ₂ - θ₁ ∈ (-360, 0]`. /// /// https://www.w3.org/TR/css-color-4/#hue-decreasing - decreasing, - - /// No fixup is performed. - /// - /// Angles are interpolated in the same way as every other component. - /// - /// https://www.w3.org/TR/css-color-4/#hue-specified - specified; + decreasing; /// Parses a SassScript value representing a hue interpolation method, not /// ending with "hue". @@ -150,8 +116,6 @@ enum HueInterpolationMethod { return HueInterpolationMethod.increasing; case 'decreasing': return HueInterpolationMethod.decreasing; - case 'specified': - return HueInterpolationMethod.specified; default: throw SassScriptException( 'Unknown hue interpolation method $value.', name);