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

Return values of 0 and 1 are not always as expected #27

Closed
danmarshall opened this issue Jul 21, 2020 · 2 comments · Fixed by #28
Closed

Return values of 0 and 1 are not always as expected #27

danmarshall opened this issue Jul 21, 2020 · 2 comments · Fixed by #28

Comments

@danmarshall
Copy link

I ran into an issue animating with easeExpInOut where the end result was not exactly 1. The readme states "A good easing type should return 0 if t = 0 and 1 if t = 1", but a few of these do not.

https://observablehq.com/@danmarshall/d3-easing-at-0-1

easeBack 0: 0, 1: 1
easeBackIn 0: 0, 1: 0.9999999999999998
easeBackInOut 0: 0, 1: 1
easeBackOut 0: 2.220446049250313e-16, 1: 1
easeBounce 0: 0, 1: 1
easeBounceIn 0: 0, 1: 1
easeBounceInOut 0: 0, 1: 1
easeBounceOut 0: 0, 1: 1
easeCircle 0: 0, 1: 1
easeCircleIn 0: 0, 1: 1
easeCircleInOut 0: 0, 1: 1
easeCircleOut 0: 0, 1: 1
easeCubic 0: 0, 1: 1
easeCubicIn 0: 0, 1: 1
easeCubicInOut 0: 0, 1: 1
easeCubicOut 0: 0, 1: 1
easeElastic 0: 0, 1: 1.00048828125
easeElasticIn 0: -0.00048828124999999875, 1: 1
easeElasticInOut 0: -0.00024414062499999938, 1: 1.000244140625
easeElasticOut 0: 0, 1: 1.00048828125
easeExp 0: 0.00048828125, 1: 0.99951171875
easeExpIn 0: 0.0009765625, 1: 1
easeExpInOut 0: 0.00048828125, 1: 0.99951171875
easeExpOut 0: 0, 1: 0.9990234375
easeLinear 0: 0, 1: 1
easePoly 0: 0, 1: 1
easePolyIn 0: 0, 1: 1
easePolyInOut 0: 0, 1: 1
easePolyOut 0: 0, 1: 1
easeQuad 0: 0, 1: 1
easeQuadIn 0: 0, 1: 1
easeQuadInOut 0: 0, 1: 1
easeQuadOut 0: 0, 1: 1
easeSin 0: 0, 1: 1
easeSinIn 0: 0, 1: 0.9999999999999999
easeSinInOut 0: 0, 1: 1
easeSinOut 0: 0, 1: 1

Not sure if this is a bug or not, but hopefully this helps anyone else with this issue.

@Fil
Copy link
Member

Fil commented Jul 22, 2020

I think it's a bug.

For back it's just a matter of reordering the operations to avoid bad rounding.

backIn(t) is computed as t * t * ((s + 1) * t - s) where s is the "overshoot" parameter. If computed as t * t * (s * (t - 1) + t) it will return 1 for t=1, with no additional cost (same number of multiplications and additions).

For elastic, and exp the formula is not really expected to give 0 in 0 or 1 in 1 as it uses Math.pow(2,-10) instead of 0.

A solution for these would be to introduce easePow(x) = (Math.pow(2, 10 * (x-1)) - 0.0009765625) * 1.0009775171065494 (the two constants being 2^-10 and 1/(1-2^-10)) and use it internally in lieu of Math.pow(2, 10 * (x-1)). This would have an added cost of 1 multiplication and 1 addition. It also means we have to change all the tests.

Finally for sinIn there doesn't seem to be a solution other than an equality test (t === 1 ? 1 : Math.cos(…))

Fil added a commit that referenced this issue Jul 22, 2020
partly addresses #27

(a fix for elastic and exp requires a change to *all* the corresponding tests)
@Fil
Copy link
Member

Fil commented Jul 22, 2020

I've pushed a rather simple solution for back and for sin, and a second commit solves it for elastic and exp (but breaks all the tests).

mbostock pushed a commit that referenced this issue Aug 19, 2020
partly addresses #27

(a fix for elastic and exp requires a change to *all* the corresponding tests)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

2 participants