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

[css-color-5] When mixing hue, there are two ways round the hue range #4735

Closed
smfr opened this issue Feb 4, 2020 · 37 comments
Closed

[css-color-5] When mixing hue, there are two ways round the hue range #4735

smfr opened this issue Feb 4, 2020 · 37 comments

Comments

@smfr
Copy link
Contributor

smfr commented Feb 4, 2020

https://drafts.csswg.org/css-color-5/#colormix

Mixing hue in lch() leads to ambiguity (or undesirable results) because hue is an angle around the circle, so there are two paths between any two values. In some cases, authors may want a hue mix that traverses the 0deg/360deg point, at other times not.

@smfr smfr added the css-color-5 Color modification label Feb 4, 2020
@svgeesus svgeesus self-assigned this Feb 4, 2020
@svgeesus
Copy link
Contributor

svgeesus commented Feb 4, 2020

I have been wondering about that myself. Maybe an extra parameter with two values, to go 'the long way round' or 'the short way round' with shorter path being the default if unspecified? It would also need tie logic, so that both paths are available when the path is exactly 180deg.

@jrus
Copy link

jrus commented Feb 5, 2020

Unless explicitly specified, this should always be the shortest arc.

@tabatkins
Copy link
Member

Once the spec is fixed to not mod the angle eagerly, the consistent answer is to just do a linear interpolation from the start to the end angle. If you want a particular direction, you can specify the angles to cause that direction, just like you do with 'rotate'.

@jrus
Copy link

jrus commented Feb 8, 2020

the consistent answer is to just do a linear interpolation from the start to the end angle

The big problem is that many authors are going to want to set gradient stops or color mix endpoints either (a) from coordinates provided by a graphical tool, (b) in a different coordinate system [mixing in polar-coordinate CIELAB (L*, C*, h*) space is apparently the default irrespective of the space in which a color is specified], or (c) from a specific named color not specified adjacent to the usage in a gradient/color mix.

When this happens for e.g. a red and a purple, the gradient should go the short way through red–purple, but from what I can tell according to this proposal will instead go all the way around via orange→yellow→green→blue.

This is going to be very confusing for authors, and difficult or even impossible for them to fix in a straightforward way.


For creators of graphical tools for creating gradients or color mixtures, outputting to css, this is going to create some sticky edge cases which are difficult to work around. The hue angle specified for each color will need to be adjusted by an arbitrary multiple of 360° based on the specified angle of the previous color.

If trying to make a collection of color mixes between several different colors, each original color may need to be specified multiple times for different numbers of turns around the circle, so that mixtures with others end up behaving as expected.

@faceless2
Copy link

You've mentioned authors using "a graphical tool" consistently in your case for using the shortest path between the angles. Presumably this tool could also convert a gradient from 340° to 20° into -20° to 20° in the generated CSS.

@jrus
Copy link

jrus commented Feb 9, 2020

There are 2 cases: (1) an author uses a graphical tool to obtain coordinates, and then copy/pastes those coordinates into a document. That puts the burden of figuring out what multiple of 360° to get the desired behavior onto the end user; (2) the graphical tool generates a whole gradient or color mix or whatever, and outputs CSS. In that case you are complicating the tool programmer’s job and making it more likely that their tools will have undesired behavior.

For e.g. a gradient, the tool is going to need to keep track of the turning number of every gradient stop, and potentially change every one in response to any change to the gradient. What would previously be a local update to 1 value becomes a fiddly global update.

@svgeesus
Copy link
Contributor

svgeesus commented Mar 11, 2020

@tab wrote:

Once the spec is fixed to not mod the angle eagerly, the consistent answer is to just do a linear interpolation from the start to the end angle. If you want a particular direction, you can specify the angles to cause that direction, just like you do with 'rotate'

Suppose I have

--start: lch(52% 58.1 22.7) 
--end: lch(56% 49.1 257.1)

// currently, this goes the long way around
color-mix(var(--start) var(--end) hue(75.23%));

//  22.7 * 0.7523 + 257.1 * 0.2477 = 80.76
// mixed result is lch(52% 58.1 80.76)

You seem to be saying that going the other way round I would need to inspect the values of the two custom properties and if needed, add or subtract 360 to make it do what I want.

--otherend: lch(56% 49.1 -102.9)

// now it goes the short way around
color-mix(var(--start) var(--otherend) hue(75.23%));

// 22.7 * 0.7523 + -102.9 * ( 1 - 0.7523 ) = -8.41112
// mixed result is lch(52% 58.1 -8.41112)

while what I am suggesting does not require making adjusted copies of custom properties:

// this would go the short way around
color-mix(var(--start) var(--end) hue(75.23% short));
// this would go the long way around
color-mix(var(--start) var(--end) hue(75.23% long));
// new default if unspecified, go the short way around
color-mix(var(--start) var(--end) hue(75.23%));

@svgeesus
Copy link
Contributor

svgeesus commented Mar 11, 2020

Also, doing calculations in LCH does not mean that the colors being mixed were originally specified in LCH. So tweaking the angle by adding or subtracting 360 is not even an option in those cases.

Consider mixing a hex color (thus, sRGB) with a color in prophoto-rgb:

color-mix(#123456 color(prophoto-rgb 0.9137 0.5882 0.4784));

The two input colors are auto-converted to LCH and then mixed. Relying on the user tweaking hue angle implies always requiring the user to first convert the colors to LCH.

@tabatkins
Copy link
Member

Yeah, there's tradeoffs.

Just doing a linear interpolation is simpler and more consistent with how all other values in CSS transition, and gives authors that are hand-authoring the values simple, straightforward control over how their transition proceeds. It does mean that authors doing generic work on custom property values don't have insight into how it'll work (tho the users of the component in question can always hand-tweak their values to get the desired result), and yes, when your endpoints aren't in a cylindrical space you don't have any control.

Doing a "shortest-path" interpolation is a break from that: it might be more commonly what's desired, but it means we have to make an arbitrary decision for 180deg separation, and we have to add further controls to let the author opt into the other modes when that's desired (at least four settings, as {longest path, shortest path}×{CW when 180deg, CCW when 180deg} are all possible).

@faceless2
Copy link

After reading Chris's posts, I'm going to change my opinion. Under any other circumstances I'm very strongly for normal linear interpolation like we do everywhere else, but for color-mix, I think you have to take the shortest route.

The critical difference is that colors are likely being mixed in a different space to the one they're specified - even if both colors are specified in the same space. So the user has no real control over the components being mixed.

I expect the vast, vast majority of people using this will be doing so in RGB, and most of them won't have even heard of Lch. If someone tries to mix from green to yellow and find it goes "the long way round", it's going to seem wrong and won't be obvious how to solve it.

One solution is to explain the Lch colorwheel and give them a flag to set; another is for them to specify both colors in Lch. Neither is good.

I think the principle of least surprise requires color-mix to interpolate in the shortest direction, certainly when either color is not specified in Lch. Myself, I think the 180° decision should be arbitrary and fixed (e.g. to clockwise).

If both colors are Lch to start with, you could go either way - shortest path for consistency, normal interpolation for full control. I lean towards the latter, as it means for most people in most colorspaces it will do the right thing, and those that really care about the details can specify their colors in Lch.

@svgeesus
Copy link
Contributor

So I'm thinking of an optional second argument to the hue adjuster. Instead of

hue: 'hue(' <percent> ')'

it will be

hue: 'hue(' <percent> [shorter | longer | clockwise | anticlockwise]? ')'

with the default, if omitted, being 'shorter' and the direction, if the hue difference is exactly 180deg, being 'clockwise'. So you get what you want most of the time, and you can get exactly what you want by being specific.

@tabatkins
Copy link
Member

shorter and longer still have the problem that they're ambiguous if the difference is 180deg; we'd either have to make an arbitrary choice or make the grammar [shorter | longer] || [clockwise | anticlockwise].

@svgeesus
Copy link
Contributor

@tabatkins wrote:

shorter and longer still have the problem that they're ambiguous if the difference is 180deg;

which is why I wrote, earlier:

and the direction, if the hue difference is exactly 180deg, being 'clockwise'

@tabatkins
Copy link
Member

I would swear I didn't read that, but it's in my email client, so it was part of the original too. Sorry about that. ^_^

@LeaVerou
Copy link
Member

LeaVerou commented May 16, 2020

After playing a little bit with interpolation code, I stumbled on the same issue @svgeesus is describing, and it's nasty.
Consider this (actual real example with real colors):

--color-red: hsl(0 80% 50%);
--color-blue: hsl(210 80% 55%);
--color-red-blue: color-mix(--color-red, --color-blue); /* using default lch interpolation */

--color-red is hsl(0 80% 50%) so lch(49.857% 91.826 38.012) in LCH.
--color-blue is hsl(210 80% 55%) so lch(56.522% 55.117 267.397) in LCH
Therefore, --color-red-blue is:

  • With simple linear interpolation: lch(53.19% 73.471 152.704) which is hsl(154.025 100.002% 28.685%) (after gamut mapping).
  • With shortest arc interpolation: Blue becomes lch(57% 55 -93) (hsl(210, 65.455%, 55%)) and their midpoint is lch(52.706%, 64.871, 332.17) which is hsl(309 61% 54%).

Green is certainly not what I have in mind when combining red and blue. And basically, based on what angle you start with, you could get any angle back. The result is nonsensical, and if your color is not lch there is no way to control that.

My calculations:
image

@svgeesus
Copy link
Contributor

As I was defining the keywords

clockwise is the direction of decreasing hue angle; anticlockwise is the direction of increasing hue angle

I realized that those are stupid names that people will get mixed up so I went with increasing and decreasing instead.

@LeaVerou
Copy link
Member

I realized that those are stupid names that people will get mixed up so I went with increasing and decreasing instead.

Indeed!

The hue adjuster takes optional keywords, because there are two ways around the hue circle. If no keyword is specified, it is as if ''shorter'' were specified. ''increasing'' is the direction of increasing hue angle; ''decreasing'' is the direction of decreasing hue angle. If the hue difference is exactly 180 degrees,
it is as if ''decreasing'' were specified.

The recent edits are a good start, but there are still a bunch of things we need to define in regards to how these algorithms work.

If interpolating between e.g. -360 and 720, which of those keywords give us 3 rainbows? It's obvious that shorter doesn't, but what about all the others? Does longer give you one or three rainbows? Does longer just mean "the long arc around the hue wheel or "unclipped angles as specified"". And if the former (which seems more reasonable), do we need another keyword that just does dumb numerical interpolation between the coordinates as specified? I can't really think of use cases, but that doesn't mean there aren't any.

I wonder if these are correct (and optimal) for the pre-interpolation fixup. If so, I can put them in the spec.

// angle1, angle2 are hue angles in degrees
angle1 = ((angle1 % 360) + 360) % 360; // constrain to [0, 360)
angle2 = ((angle1 % 360) + 360) % 360; // constrain to [0, 360)


// Increasing:
if (angle2 < angle1) {
	angle2 += 360;
}

// Decreasing:
if (angle1 < angle2) {
	angle1 += 360;
}

// Longer:
if (angle2 - angle1 < 180) {
	angle2 += 360;
}
else if (angle2 - angle1 > -180) {
	angle1 += 360;
}

// Shorter:
if (angle2 - angle1 > 180) {
	angle1 += 360;
}
else if (angle2 - angle1 < -180) {
	angle2 += 360;
}

@svgeesus
Copy link
Contributor

The recent edits are a good start, but there are still a bunch of things we need to define in regards to how these algorithms work.

@svgeesus svgeesus reopened this May 31, 2020
@LeaVerou
Copy link
Member

LeaVerou commented May 31, 2020

Alright, since these seem to work in my experiments, and nobody has expressed any dissent about them, I'm keen to put them in the spec, and clarify that longer and shorter still start with angle constraining to [0, 360). I think we should also have a value that leaves the values as specified and does dumb interpolation (raw? intact? specified?) if that's possible implementation-wise. I'm not sure however if color-mix() is the right place for all this, we do interpolation in many other places.
I'm thinking of adding an interpolation section describing how colors interpolate, optional interpolation parameters if the context supports them, and other things like e.g. does gamut mapping happen before or after interpolation?
Thoughts?

@LeaVerou
Copy link
Member

LeaVerou commented Jun 2, 2020

Agenda+ to discuss whether longer, increasing, decreasing also constrain the arc to < 360 degrees, and if so, whether we need a hue adjuster for "as specified".

@svgeesus
Copy link
Contributor

svgeesus commented Jun 5, 2020

I'm thinking of adding an interpolation section describing how colors interpolate, optional interpolation parameters if the context supports them, and other things like e.g. does gamut mapping happen before or after interpolation?

I agree it makes sense to split this out into another sections, so it can then be referenced both in Color 5 and also outside it.

I suggest gamut mapping should happen before interpolation. This is because we wish to avoid multiple mapping stages, so it should happen as late as possible and ideally, once.

@svgeesus
Copy link
Contributor

I agree the gamut mapping is a large and separate issue.

The mathematical definitions proposed by @LeaVerou are working well for me in testing.

@LeaVerou
Copy link
Member

LeaVerou commented Jun 11, 2020

I went ahead and added an Interpolation section for now.
@smfr let me know if this solves your issue!

@dbaron
Copy link
Member

dbaron commented Jun 25, 2020

Two comments on the pseudo-javascript in the Hue interpolation section:

First, above you wrote:

// angle1, angle2 are hue angles in degrees
angle1 = ((angle1 % 360) + 360) % 360; // constrain to [0, 360)
angle2 = ((angle1 % 360) + 360) % 360; // constrain to [0, 360)

I think it would be good to include this explicitly in the definitions of shorter, longer, increasing, and decreasing, both since I think that's the intent, and I think it's the intent that it not be done for specified.

Second, I think the pseudo-code for longer is incorrect. For example, it adjusts θ₁=315 and θ₂=45 using the first conditional branch into θ₂=405 and θ₁=315, when I think it should be left untouched. I think the code should instead be (in addition to the change in my first comment):

if (θ₂ > θ₁ && θ₂ - θ₁ < 180) {
  θ₁ += 360;
}
else if (θ₁ > θ₂ && θ₁ - θ₂ < 180) {
  θ₂ += 360;
}

@dbaron
Copy link
Member

dbaron commented Jun 25, 2020

(Though it's not clear to me how longer should handle the case of hue angles that are identical to start with!)

@dbaron
Copy link
Member

dbaron commented Jun 25, 2020

... or how shorter should handle hue angles that differ by exactly 180.

@LeaVerou
Copy link
Member

LGTM. For readability, I would change it to:

if (0 < θ₂ - θ₁ >  && θ₂ - θ₁ < 180) {
  θ₁ += 360;
}
else if (0 < θ₁ - θ₂ && θ₁ - θ₂ < 180) {
  θ₂ += 360;
}

or even (since this is pseudo-JS):

if (0 < θ₂ - θ₁ < 180) {
  θ₁ += 360;
}
else if (0 < θ₁ - θ₂ < 180) {
  θ₂ += 360;
}

@svgeesus
Copy link
Contributor

Earlier, I wrote:

with the default, if omitted, being 'shorter' and the direction, if the hue difference is exactly 180deg, being 'clockwise'.

The clockwise keyword soon became the more descriptive decreasing. I agree that this logic should be apparent from the pseudo-code.

@LeaVerou
Copy link
Member

LeaVerou commented Jul 1, 2020

Done in 4e88f2c, forgot to cross-link in commit message.

dbaron added a commit to dbaron/csswg-drafts that referenced this issue Jul 1, 2020
…rpolation types.

This makes a few adjustments to the definitions of hue interpolation
types to follow up on the discussions in w3c#4735.

First, it adjusts the pseudo-code so that for 'shorter' and 'longer',
angles that differ by 180 degrees will always go in the decreasing
direction, as described in
w3c#4735 (comment)

Second, it adjusts the set notation to match the pseudo-code.  (They
were not matching even prior to this change.)  The set notation
previously seemed to assume that there were absolute-value functions
present that were not actually there.  However, given the asymmetry of
always going in the decreasing direction, it's more correct to write it
without absolute values.
@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-color-5] When mixing hue, there are two ways round the hue range, and agreed to the following:

  • RESOLVED: Publish a version with all keywords but longer
The full IRC log of that discussion <dael> Topic: [css-color-5] When mixing hue, there are two ways round the hue range
<dael> github: https://github.com//issues/4735
<dael> leaverou: When interpolate between hues usually you don't want interpolate in same way. If going between hue 0 and hue 400 you don't want a whole rainbow
<dael> leaverou: What we put in spec is by dfault use shortest arc which does expected in common. Have keywords for longest arc etc and also as-specified keyword to allow raw interp
<dael> leaverou: Wasn't sure if all needed. Esp specified one. If impl want to store value as normalized keyword doesn't allow
<dael> leaverou: I put algo in spec which tweaked by dbaron. Good to get sanity check.
<smfr> https://drafts.csswg.org/css-color-5/#hue-interpolation
<Rossen_> q?
<dael> fantasai: Can you summerize the proposal?
<dael> leaverou: Do we need all 5 keywords?
<dael> leaverou: We need shorter b/c that's what you expect in most cases. Do we need specified which is interp as specified so if you go between 0 and 720 2 rainbows. Need increasing, decreasing, longest or is that completist
<dael> fantasai: Are there use cases? We can add keywords. If there's not a use case might want to note possibility for future reference in case we need to add later. If not a use case don't need to add.
<dael> fantasai: I think it's usefult o think of all and makes sure keywords are a set that make sense even if we only include 1 or 2 in spec
<dael> dbaron: Intent is these would eventually apply to all gradients, animations, and color mix funct or only some?
<dael> leaverou: Good to design with that in mind. Not sure how text for animation snad gradients but if we have a syntax making sense it would be good to have the option
<miriam> q+
<astearns> for gradients and animations the workaround would be to add more steps/stops to mimic the non-short behavior?
<dael> fantasai: My suggestion is draft all in spec, put an issue in saying we're not sure if we need all and we might limit to a subset with the subset that makes sense to you and also note might expand to gradients. Encourage people to think what that would look like
<tantek> +1 to publishing at least one draft with more keywords to get the ideas published
<dael> fantasai: Early stage WD so makes sense to put ideas and poke at them with people like Una to make cases
<leaverou> http://localhost:8002/csswg-drafts/css-color-5/Overview.html#hue-interpolation
<leaverou> https://drafts.csswg.org/css-color-5/#hue-interpolation
<dael> leaverou: Does math make sense? This is the section ^
<florian> q+
<Rossen_> ack miriam
<dael> miriam: THinking of specified I'd have use cases when comes to gradient. As pointed out in chat that could be do with extra stops.
<dael> miriam: Can't think of cases when mixing colors. I don't know if that's separate but might be. Math makes sense. Shorter and longer fall apart at 180 which maybe implies need to determine direction without them
<Rossen_> ack florian
<dael> florian: I haven't reviewed math for correctnss, but intuitive seems right. Longer seems least useful. Wanting longer for being longer seems odd. Might pick if gives right thing.
<miriam> +1 to longer being less useful than increase/decrease
<dael> florian: Approach about putting in spec now with note for use cases sounds good
<dael> dbaron: On math have a PR to tweak. I think set notation doesn't match pseudo code and I think pseudo code is right. I have some weaks for 180 case but it's not clear that's what we want
<dael> leaverou: 180 chris said we can pick one as long as it's well defined. Doesn't matter increasing or decreasing
<dael> florian: Makes sense. If you have a preference you can say it.
<dael> fantasai: We use 'closest' in radial gradients so maybe that instead of 'shorter'?
<dael> leaverou: Than what longer?
<dael> fantasai: 'father'?
<dael> florian: I don't think longer is needed so I don't mind not having a good replacement
<fantasai> s/father/farthest/
<tantek> near and far, close and distant, short and long ?
<dael> fantasai: We have farthest and closest side
<dael> leaverou: That's differ than angles
<dael> Rossen_: Apart from bikeshedding I hear 2 proposals. 1) let's push a version of the spec with all the keywords initially or as many as we want so we encourage more incubation.
<dael> Rossen_: 2) I hear agreement that longer doesn't seem useful. I didn't hear a use case to prove otherwise.
<dael> Rossen_: I don't want to bikeshed.
<dael> Rossen_: SHould we resolve to keep the keywords becides longer and publish?
<dael> leaverou: I'd rather hear from Una and Adam before we resolve.
<dael> fantasai: This isn't final. We're drafting for dicussion to encourage participation. I think it's fine to put it all in the draft, explain the thoughts, and enougage feedback. We can publish often
<dael> Rossen_: Objections to Publish a version with all keywords but longer?
<fantasai> "Publish early, publish often"
<tantek> +1
<dael> RESOLVED: Publish a version with all keywords but longer

@danburzo
Copy link

danburzo commented Jul 5, 2020

I'd like to experiment with adding these five hue interpolation types to my library, and I wondered — at the hue fixup step, should we also do something about grays, which in LCh have c: 0 and h: 0? The hue value is misleading here, in the absence of a chroma.

In the case of mixing two colors, the achromatic one can inherit the hue from the other color, if it has chroma. But if we were to extend hue interpolation to gradients, which accept more than one color stop, how should hues for achromatic colors be handled?

Edit: Oops, found a separate discussion in #4928

@danburzo
Copy link

danburzo commented Jul 5, 2020

I've added an interactive visualization of the various hue fixup methods, and changed the fixup algorithms to work with any number of hues. Here are the formulas I use currently; they need to be tested more thoroughly, but at first blush they seem correct.

// shorter: 
θ₂ = Math.abs(θ₂ - θ₁) <= 180 ? θ₂ : θ₂ - 360 * Math.sign(θ₂ - θ₁);

// longer: 
θ₂ = Math.abs(θ₂ - θ₁) >= 180 || θ₂ === θ₁ ? θ₂ : θ₂ - 360 * Math.sign(θ₂ - θ₁);

// increasing:
θ₂ = θ₂ >= θ₁ ? θ₂ : θ₂ + 360 * (1 + Math.floor(Math.abs(θ₂ - θ₁) / 360));

// decreasing:
θ₂ = θ₂ <= θ₁ ? θ₂ : θ₂ - 360 * (1 + Math.floor(Math.abs(θ₂ - θ₁) / 360));

In the case of longer, repeated equal values produce a zero-width interval, to remain consistent with the other methods, but that only works in some cases:

[0, 190, 190, 360] // => [0, 190, 190, 0]
[0, 160, 160, 360] // => [0, -200, 160, 360]

Edit: To get it to work consistently, I ended up converting the list of hues from absolutes to relative values (deltas), apply the fixup rules, then convert back to absolutes.

@danburzo
Copy link

danburzo commented Jul 6, 2020

Trying to find justifying use-cases for each of the fixup methods, a couple of questions came up:

  • To allow specified to produce more than "one rainbow", does that mean that hsl(), lch() etc. will not normalize their hue to the interval [0, 360)?
  • Should there be a normalized fixup method that just normalizes the hues? (like specified but with normalized hues)

dbaron added a commit to dbaron/csswg-drafts that referenced this issue Jul 8, 2020
…rpolation types.

This makes a few adjustments to the definitions of hue interpolation
types to follow up on the discussions in w3c#4735.

First, it adjusts the pseudo-code so that for 'shorter' and 'longer',
angles that differ by 180 degrees will always go in the decreasing
direction, as described in
w3c#4735 (comment)

Second, it adjusts the set notation to match the pseudo-code.  (They
were not matching even prior to this change.)  The set notation
previously seemed to assume that there were absolute-value functions
present that were not actually there.  However, given the asymmetry of
always going in the decreasing direction, it's more correct to write it
without absolute values.
@svgeesus svgeesus added css-color-4 Current Work and removed css-color-5 Color modification labels Jan 30, 2021
@svgeesus
Copy link
Contributor

Re-tagging to css color 4 because the color interpolation section moved.

@svgeesus
Copy link
Contributor

svgeesus commented Feb 6, 2021

does that mean that hsl(), lch() etc. will not normalize their hue to the interval [0, 360)

Correct. Normalization happens as a first stage of interpolation. The specified value of an hsl()color is as specified, including angles outside [0,360). The computed value is rgb().

I thought that the specification was clear about hue angles outside the range [0, 360) but it isn't.

The existence and validity of such angles is mentioned in passing for LCH:

The third argument is the hue angle. It’s interpreted similarly to the argument of hsl(), but doesn’t map hues to angles in the same way because they are evenly spaced perceptually. Instead, 0deg points along the positive "a" axis (toward purplish red), (as does 360deg, 720deg, etc.); 90deg points along the positive "b" axis (toward mustard yellow), 180deg points along the negative "a" axis (toward greenish cyan), and 270deg points along the negative "b" axis (toward sky blue).

and, again in passing, for HSL:

In HSL (and HWB) the angle 0deg represents sRGB primary red (as does 360deg, 720deg, etc.), and the rest of the hues are spread around the circle, so 120deg represents sRGB primary green, 240deg represents sRGB primary blue, etc.

However the main section on the <hue> type is entirely silent on this. It should be explicit.

@svgeesus
Copy link
Contributor

svgeesus commented Feb 6, 2021

Should there be a normalized fixup method that just normalizes the hues? (like specified but with normalized hues)

The normalization range depends on the method though. So for decreasing, it is:

Angles are adjusted so that θ₂ - θ₁ ∈ (-360, 0]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants