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-values] Emulate conditionals with discontinuous function(s) in calc() #6638

Open
litherum opened this issue Sep 18, 2021 · 5 comments
Open

Comments

@litherum
Copy link
Contributor

litherum commented Sep 18, 2021

calc() doesn't support conditionals (e.g. if statements) today, and that is a good thing. We shouldn't add conditionals.

However, it is pretty natural to want to write piecewise functions with calc(). Conditionals are certainly sufficient to express piecewise functions, but they aren't actually necessary - all you need is a discontinuous basis function. min() and max() are almost expressive enough, but not really. Edit: This could be polyfilled by max(), but we might want to do better here.

(This kind of thing is actually pretty common in GPU programming, where programmers often try to write branchless code because branches are particularly slow on many GPUs. I'm cribbing from common patterns in GPU software.)

One way to do this would be if we added a step() function, which looks like this:

Screen Shot 2021-09-17 at 11 15 57 PM

For example, if the author wanted to express "use x^2 if x is positive but use 2*x if x is negative" they could do it like this:

calc(step(var(--x)) * pow(var(--x), 2) + (1 - step(var(--x))) * 2 * var(--x))

A simpler way for authors, but just as expressive, would be to add something like select(a, b, condition). This would result in a if condition is greater than 0, and b otherwise. The same example written this way would be:

calc(select(pow(var(--x), 2), 2 * var(--x), var(--x))

This seems like not much implementation burden, but a significant increase in power / flexibility for authors.

@litherum litherum added the css-values-4 Current Work label Sep 18, 2021
@litherum
Copy link
Contributor Author

litherum commented Sep 18, 2021

Oh, I guess step() could be polyfilled by max(0, var(--x)) / var(--x). That's kind of yucky, though.

@Loirooriol
Copy link
Contributor

No, max(0, var(--x)) / var(--x) is NaN for 0. Use sign() instead, this usecase was the reason I proposed it in #4673

So "use x^2 if x is positive but use 2*x if x is negative (and use +0 if x is ±0)" is

calc(max(0, sign(var(--x))) * pow(var(--x), 2) - min(0, sign(var(--x))) * 2 * var(--x))

In #4731 (comment) I wrote a summary for writing if(cond, then, else) in terms of sign().

@litherum
Copy link
Contributor Author

litherum commented Sep 24, 2021

Yeah, sign() is almost sufficient, except for the 0 case.

@litherum
Copy link
Contributor Author

litherum commented Sep 24, 2021

I guess if you could use math in @when that would be sufficient 🤔 These rules are outside of a particular selector though, so I'm not sure how that would work...

@when var(--x) > 0 {
    --result: pow(var(--x), 2)
} @else {
    --result: calc(2 * var(--x));
}

@Loirooriol
Copy link
Contributor

Yeah, sign() is almost sufficient, except for the 0 case.

What's the problem for 0? If you want something special for 0, you can use (1 - abs(sign(var(--x)))) * value

Having proper conditionals would be better of course, but for now sign() seems good enough.

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

3 participants