-1

Okay, so I've been creating a UI framework and I added animations to it. I wanted to allow users to control the timing functions of their animations using cubicBeziers, so I decided to use p5.js' bezierPoint() function. I spent hours making it work, and finally got it working in a way that I thought was perfect..... Until now I find out that it's (somehow) not working as expected.

When I pass in a cubic bezier with start/end points at 0,1 and 1,0 and control points at 0.5,0.5 and 0.5,0.5 (this is a linear cubic bezier), I get an eased animation for no reason... This should be yielding a linear animation (which can be demonstrated here) but somehow it's not.

So I looked into it further just to prove that I wasn't just seeing things, and sure enough, it is in fact giving non-linear numbers. I wrote this tiny code snippet:

<script src = "https://cdn.jsdelivr.net/npm/p5@1.3.1/lib/p5.min.js"></script>

<script>
    function setup(){
        createCanvas(100,100);
    }
    
    function draw(){
        background(255,255,0);
                
        noFill();
        stroke(0,0,0);
        strokeWeight(3);
        bezier(0,100,50,50,50,50,100,0);

        ellipse(bezierPoint(0,50,50,100,frameCount / 100),bezierPoint(100,50,50,0,frameCount / 100),5,5);
        
        ellipse(50,bezierPoint(100,50,50,0,frameCount / 100),5,5);
        ellipse(60,100 - frameCount,5,5);
    }
</script>

And you can see very clearly that the dot following bezierPoint() (the one on the left) is moving at a non-linear speed when compared to the linear one (the one on the right)

Why is this happening?


Just thought I'd include this here for convenience. This is p5.js' bezierPoint() function:

function bezierPoint(e,t,r,i,n){
    var a = 1 - n;
    return(Math.pow(a,3) * e + 3 * Math.pow(a,2) * n * t + 3 * a * Math.pow(n,2) * r + Math.pow(n,3) * i);
}
OOPS Studio
  • 732
  • 1
  • 8
  • 25
  • There is no such thing as a "linear cubic curve". A cubic curve _is a cubic curve_, even if you put all the coordinates on the same line. The mapping from your "time" input to the actual on-curve coordinates will _by definition_ exhibit cubic behaviour. If you want linear values, use an actual line. Don't use a Bezier curve. – Mike 'Pomax' Kamermans Apr 11 '21 at 03:41
  • While this may be accurate from a mathematical standpoint, it does not apply to my question. The entire basis of animating to a cubic curve is built on the fact that a "linear cubic curve" exists, whether it be mathematically sound or not. Simply look up "CSS transition-timing-function documentation" and you'll see countless examples of "linear" cubic beziers. Even "bezier maker" sites have options for "linear" – OOPS Studio Apr 11 '21 at 03:49

2 Answers2

0

Okay, so after continued research, it appears as though this is expected behavior somehow.

I found this question: Custom animation using cubic Bezier function

Which states "I saw on a question [that] the answerer recommended simply eliminating x and define the function as y against t. However, this will give pretty inaccurate results, as a linear Bezier curve from 0.0 to 1.0 will not animate linearly."

Which clues me into the fact that what I'm seeing here is somehow expected behavior... I just can't wrap my brain around it.

How does taking a linear curve and animating value t linearly give non-linear results?! There's nothing in my code which is non-linear, yet somehow I'm getting a non-linear animation. That really doesn't make sense to me at all, but now that I know that it's expected behavior I guess I can expect to not receive the answer that I'm looking for.

OOPS Studio
  • 732
  • 1
  • 8
  • 25
  • not "somehow": because _cubic curves have cubic behaviour_. Just because someone calls it a linear curve doesn't matter when you're asking about the maths behaviour. There is no such thing as a "linear cubic curve", there is only "a cubic curve with all its coordinates on the same line", but that's still a cubic curve, and _by definition and implementation_ will exhibit cubic value mapping: linearly increasing `t` gives you nth-degree-polynomial motion on that line. That's the [defining feature](https://pomax.github.io/bezierinfo) of Bezier curves: they're not lines and _can't do_ linear. – Mike 'Pomax' Kamermans Apr 16 '21 at 20:03
  • @Mike'Pomax'Kamermans You're correct. I learned that midway through investigating my issue. The only reason they're called "linear" is because all their points are lined up on a straight line, and when animated over using X, it technically _is_ linear. But I certainly see your point and I agree. The Bezier itself is not linear, it's simply the math that makes the output from it linear. That being said tho, can't you create a linear cubic Bezier by placing its control points at (x: p0 + (p3 - p0) / 3), (y: 2*(p3 - p0) / 3)? So long as you get an infinite amount of precision, it should be linear – OOPS Studio Apr 16 '21 at 20:12
  • At that point, really the question is: what insane drawing system are you using that it can draw Bezier curves, but it can't just... you know... _draw lines_? Even CSS has `linear` =) – Mike 'Pomax' Kamermans Apr 16 '21 at 20:40
  • Nowhere in my post did I mention drawing anything. I'm not drawing lines nor beziers. I'm not sure where you got that assumption? Also, I'm not using CSS... – OOPS Studio Apr 17 '21 at 03:43
  • "drawing" in the conceptual sense: if you need something that acts as a line, you use a line. And you can even encode that line as a linear Bezier (which _does_ exist. unlike "linear cubic", or "quadratic cubic" or any other combination of orders): two points, no control points, with parametric form B(x) = x1*(1-t) + x2*t, and B(y) = y1*(1-t) + y2*t - done. Linear. Also, you very much did talk about CSS, see your own comment to me in the original post. You're writing a UI framework that needs animations: you're drawing "things" based on curves, so "don't use curves when you need a line". – Mike 'Pomax' Kamermans Apr 17 '21 at 16:25
  • Well... Thanks for telling me what I am and amn't doing... However that's not accurate. I'm _animating_ things using curves. I'm not drawing the curves at all, I'm using them to lerp the properties of other elements. Nowhere in my code do I draw any curves, I only hold them in memory as timing functions. But regardless, I have working code now, so this conversation probably won't lead anywhere beneficial. – OOPS Studio Apr 17 '21 at 21:22
  • If you're animating things along the curve, you are "drawing" the curve, with that thing, over time. The maths doesn't care that it's over time (and neither should you or I). Having said that: if you have working code, please update your answer with that code and rephrase its text so that it's a real answer to your own question. That way, all future visitors with the same problem can get the same answer. If not, probably worth deleting the question (because if it only benefits you and no one else: it's not a question for SO) – Mike 'Pomax' Kamermans Apr 17 '21 at 22:49
  • I wouldn't consider lerping a value between one value and another value "drawing", but alright. Regardless, I think I'll be deleting this question. – OOPS Studio Apr 18 '21 at 00:27
  • we can call it "rendering the control value", but that's nowhere near as universally accepted as just calling it drawing. – Mike 'Pomax' Kamermans Apr 18 '21 at 01:02
0

When the 4 control poles of a cubic Bezier curve are collinear, the Bezier curve indeed will look "linear". But it does not mean this Bezier curve is exactly the same as a line that goes from P0 to P3. The first derivative for a line is a constant but the first derivative for a linear Bezier curve is not a constant (in general).

To obtain a "true" linear cubic Bezier curve, you need to place the two middle control points P1 and P2 at

P1 = P0 + (P3-P0)/3
P2 = P0 + 2*(P3-P0)/3
fang
  • 3,473
  • 1
  • 13
  • 19
  • Does this apply to both X and Y separately? So would I say `p1x = p0x + (p3x-p0x)/3` and then `p1y = p0y + (p3y-p0y)/3` to get p1's position? Or did I misunderstand? Also, would you by any chance know how to create an equation which will return a y position on a cubic bezier given an X position? If I need to go through all this extra hassle to get a linear animation, then that leads me to believe that using `t` to animate along a bezier is the wrong approach in general, and that I need to use X instead. Would you by chance know of a way to accomplish this? (It's alright if you don't.) – OOPS Studio Apr 11 '21 at 00:50
  • *I notice a mistake in my above comment, I left out the `2*` in the second control point equation. I tried to edit my other comment but it won't allow me. – OOPS Studio Apr 11 '21 at 01:20
  • Alright, so I simply changed my bezier from `(0,1,0.5,0.5,0.5,0.5,1,0)` to `(0,1,0.333,0.666,0.666,0.333,1,0)` and it now gives me a linear animation. Thank you very much for your answer! However, if you look at this site: (https://cubic-bezier.com/#.5,.5,.5,.5) then you can see that inputting `0.5` as the control points _yields a linear animation._ I believe this is because they solve for X instead of `t`. How would I go about replicating this? How would I solve for X? – OOPS Studio Apr 11 '21 at 01:26
  • If you want a linear function whereas y position is obtained from a given x position, you should go with the simple formula y = x. If you want to use cubic Bezier, then you will have to find t value first from given x, then compute y value from t. – fang Apr 11 '21 at 02:03
  • Yes, thank you. I certainly don't only want to compute a linear value, that would be crazy. But ty for the tip anyway, I appreciate it. As for computing t from x, I kind of assumed I'd need to do that, but I haven't the slightest clue how. Can you give me any pointers? – OOPS Studio Apr 11 '21 at 03:51
  • Here is one reference: https://stackoverflow.com/questions/51879836/cubic-bezier-curves-get-y-for-given-x-special-case-where-x-of-control-points – fang Apr 11 '21 at 06:11