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

Add color modifying expressions #5676

Open
ryanbaumann opened this issue Nov 14, 2017 · 11 comments
Open

Add color modifying expressions #5676

ryanbaumann opened this issue Nov 14, 2017 · 11 comments

Comments

@ryanbaumann
Copy link
Contributor

ryanbaumann commented Nov 14, 2017

mapbox-gl-js version: 0.42.0

Problem

Developers want to use GL expressions for common color operations. A common use case is to indicate feature selection on a hover or click event of a feature by changing the color saturation.

There are no color functions in Expressions that can calculate changes in colors without using a third-party library such as chroma.js or the built-in color-space interpolations in GL JS Style Spec today.

Proposal

Add two new color expressions:

  1. saturate
    • Changes the saturation of a color by manipulating the Lch chromaticity.
    • ["saturate", color, saturation scale] : color
  2. desaturate
    • Desaturates a color by manipulating the Lch chromaticity. Opposite of saturate.
    • ["desaturate", color, saturation scale] : color

cc @anandthakker

@anandthakker
Copy link
Contributor

Let's broaden this to cover other color operations we may want -- I think it makes sense to consider the whole set together as we decide on how to add these.

As @davidtheclark mentioned previously, the CSS color modifying spec proposal might be good inspiration: https://drafts.csswg.org/css-color/#modifying-colors

@anandthakker anandthakker changed the title Add saturate and desaturate expression color functions Add color modifying expressions Nov 14, 2017
@mkv123
Copy link

mkv123 commented Nov 30, 2017

While this is getting implemented, here is the workaround expression to implement desaturate with graying, i.e. it decreases the color value and makes the color more mid gray.

"text-color": [
            "let", 
                "rgba", ["to-rgba", ["to-color",  ["get", "color_property"]]],

                ["let",
                    "r", ["number", ["*", 255, ["at", 0, ["var", "rgba"]]]],
                    "g", ["number", ["*", 255, ["at", 1, ["var", "rgba"]]]],
                    "b", ["number", ["*", 255, ["at", 2, ["var", "rgba"]]]],
                    "a", ["number", ["at", 3, ["var", "rgba"]]],
                    ["let",
                      "avg", ["+", ["*", 0.299, ["var", "r"]], ["*", 0.587, ["var", "g"]], ["*", 0.114, ["var", "b"]]],
                      ["let",
                        "desat_r", ["+", ["*", 0.4 , ["var", "avg"]], ["*", 0.4 , 128], ["*", 0.2 , ["var", "r"]]],
                        "desat_g", ["+", ["*", 0.4 , ["var", "avg"]], ["*", 0.4 , 128], ["*", 0.2 , ["var", "g"]]],
                        "desat_b", ["+", ["*", 0.4 , ["var", "avg"]], ["*", 0.4 , 128], ["*", 0.2 , ["var", "b"]]],
                        ["rgba", ["var", "desat_r"],  ["var", "desat_g"],  ["var", "desat_b"], ["var", "a"]]
                      ]
                    ]
                ] 
          ]

@anandthakker
Copy link
Contributor

anandthakker commented Nov 30, 2017

Woah, thanks for sharing @mkv123 !

Looking at this, I'm seeing a bug in our to-rgba expression -- I think it should return the rgb components scaled to 0-255, since that's how the rgba expression consumes them. I'll file that as a separate ticket

@jfirebaugh
Copy link
Contributor

Looking over the color-mod spec, one translation into expressions would be something like:

  • ["color-{r,g,b,a,h,s,l}-=", color, number] -- produces a color with the given channel set to the given value
  • ["color-{r,g,b,a,h,s,l}-+", color, number] -- produces a color with the given channel adjusted by the given amount
  • ["color-{r,g,b,a,h,s,l}-*", color, number] -- produces a color with the given channel multiplied by the given amount
  • ["color-rgb{,a}-{+,*}", color, r, g, b] -- shorthand for adding/multiplying in all channels
  • ["color-{tint,shade}", color, percentage] -- implements tint and shade
  • ["color-blend-{rgb,hsl}{,a}", color, color, n] -- implements blend and blenda
  • ["color-contrast", color, percentage?] -- implements contrast

So increasing the saturation by 10 percentage points would be ["color-s-+", color, 10].

This translation:

  • Would obey the semantics of color-mod as far as possible, including the treatment of achromatic colors.
  • Doesn't have an explicit percentage syntax as in CSS. r,g,b values would always be interpreted as numbers in the range 0-255. h values would always be interpreted as angles in the range 0-360. s,l values would always be interpreted as percentages in the range 0-100.
  • Doesn't have subtraction operators. Those would be awkward in a kebab-case naming convention (e.g. color-r--). Add a negative value instead.
  • Doesn't implement hwb-space operations. Are those necessary?

An alternative model is http://lesscss.org/functions/#color-operations.

@1ec5
Copy link
Contributor

1ec5 commented Jan 11, 2018

The proposal in #5676 (comment) adds 32 new operators with five distinct syntaxes. It feels like the operator name is being turned into a mini-language, whereas the structured expression syntax exists because we didn’t want to stuff a mini-language inside a string (mapbox/mapbox-gl-style-spec#362 (comment)).

What if we add a single new operator that applies a “color transform” or “color filter” to a color? A color transform could take the form of an object specifying various parameters for the transformation.

@jfirebaugh
Copy link
Contributor

I have the opposite take, @1ec5 -- stuffing all of the potential color operations inside of a single operator with an object-based parameterization system feels like the sort of meta syntax we're aiming to avoid, while extending the language with additional operators is what we explicitly designed for. (I'm not sure what you mean by "five distinct syntaxes" -- as I see it, my proposal doesn't introduce any new syntax.)

I do think that the color-mod-spec-derived names are cryptic, and would support using more familiar operator names, like "color-saturate" for "color-s-+" and "color-lighten" for "color-l-+".

@davidtheclark
Copy link
Contributor

@jfirebaugh: Why not pre-parse the arguments, in a sense, by making them items in the array? Instead of ["color-r-+", ...] we'd have ["color", "+", "r", ...]. Or instead of ["color-blend-rgb", ...] we'd have ["color", "blend", "rgb", ...]? (Or even ["color", ["blend", "rgb"], ...]?)

I was looking through the spec for something similar that exists and the closest I found was interpolate. There, it seems like we opted for multiple arguments instead of operators like interpolate-linear, interpolate-exponential, interpolate-cubic-bezier. I'm curious if you think there are key features that differentiate this set of expressions from those?

@jfirebaugh
Copy link
Contributor

The major difference is that the type signature of interpolate is the same whether interpolation is linear, exponential, or cubic-bezier. (And the fact that step has a different number of arguments is one reason we split curve into step and interpolate.)

In contrast, the number and types of arguments to color operators vary. This would make a single "uber-operator" difficult to implement and document.

@1ec5
Copy link
Contributor

1ec5 commented Jan 12, 2018

In #5676 (comment), I was thinking of interpolate, specifically the first argument. It may be that the style specification views linear, exponential, and cubic-bezier as context-specific operators in their own right, but to me (and to the NSExpression translation), it’s not much different than an options argument to a function.

@audaciouscode
Copy link

I just wanted to see if there has been any movement on this. I'm currently working on a visualization where the missing piece seems to be the ability to subtract saturation from HSL colors from layers beneath, and I haven't had a lot of success finding what I'm looking for in the official documentation.

@ryanhamley
Copy link
Contributor

@audaciouscode This is not on our roadmap at this time. We have a general interest in extending the expressions system over time but balance that against other necessary work. I'd suggest reaching out to Mapbox Support if you have a specific example as they may be able to help provide an alternate implementation.

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

8 participants