Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update behavior for sass/sass#3497 #1919

Merged
merged 1 commit into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions lib/src/functions/color.dart
Original file line number Diff line number Diff line change
Expand Up @@ -614,11 +614,6 @@ Value _invert(List<Value> 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);
}
Expand Down Expand Up @@ -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,
Expand Down
75 changes: 53 additions & 22 deletions lib/src/value/color.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
nex3 marked this conversation as resolved.
Show resolved Hide resolved

default:
return false;
Expand Down Expand Up @@ -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) =>
Expand All @@ -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) =>
Expand Down Expand Up @@ -367,29 +369,52 @@ 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].
///
/// Throws a [RangeError] if [alpha] isn't between `0` and `1`.
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].
///
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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<T>(ValueVisitor<T> visitor) => visitor.visitColor(this);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down
40 changes: 2 additions & 38 deletions lib/src/value/color/interpolation_method.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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;

Expand All @@ -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.");
Expand All @@ -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);
Expand Down Expand Up @@ -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".
Expand All @@ -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);
Expand Down