I'm trying to create a system similar to Blender's f-curves or Unity's animation curves (google it, I'm not allowed to post more than two links yet). It's basically a 2D spline where time is on the X-axis and the value is Y. Given any X value there's only one Y, so no loops. The user can then add points and modify the tangents for those points. Something like this:

enter image description here

My current implementation uses cubic bezier curves, but I'm kinda stuck on how to calculate the Y value given a certain X (which is the time). After looking around a bit I found two interesting descriptions of it here and here, and also some code for one solution (h**ps://github.com/hfink/pixelnoir/blob/master/player/src/roots.h). The problem is that I need to calculate t by solving the cubic bezier curve for X, which seems like a lot of work (for the CPU).

Is this the way it's usually done? Are there any better ways to represent the spline that avoids solving the equation?

My usecase is simple scaling/rotation/translation animations for 2D sprites to get some nice groovy feeling (like most 2D games released for mobile).


I'll describe the problem a bit more in depth to avoid confusion.

In my implementation the curve is 2D, i.e. the control points are (x,y) pairs. This means that I can freely move around all of them on a 2D plane. I'd say this is the most common case of bezier curves in graphics (think Illustrator, HTML5 canvas, QGraphicsScene, etc). This makes it possible to create the following two curves by just changing one of the control points:

enter image description here enter image description here

These are drawn with Y on the vertical axis and X on the horizontal.

When evaluating a 2D bezier curve equation you're actually evaluating two equations, Y(u) and X(u) (I'm using u instead of t to avoid confusion further down). So when moving one of the control points (P1 or P2, using wikipedia's names) along the X-axis I'm changing X' (the X derivative) in its corresponding point.

When editing the curve it's usually presented with the X axis as time and Y as the actual value. I.e. X increases by some dt each tick. So to calculate the Y value at a certain point in time I need to find which u to use since Y is defined as Y(u). In my case I know that X(u) = t which means that I need to solve that equation to get u and then plug that in to Y(u).

I could switch to a 1D curve, but that doesn't give the same flexibility since the control points in the middle would be scalars. I.e. I wouldn't be able to scale them, just define the start/end derivative in the point.

So the questions are:

  • Can I convert this 2D curve to 1D given my contraints?
  • Is there any other way increase the flexibility? Maya seems to have "weighted" and "non-weighted" tangents.
  • \$\begingroup\$ The code you have linked to will find X where Y = 0. I believe you want to solve for Y at a given X value. You say that your current implementation uses cubic bezier curves? How are these implemented? Could you reuse that code to achieve what you want? \$\endgroup\$ – Ethan Worley Jan 6 '14 at 0:08
  • \$\begingroup\$ As stated in the first answer to your gamedev.net link, you don't need to solve for t from one curve and then solve for x. Doing that would imply you already know the output of one curve, which you don't. To solve a single curve, you only need your single input value (your term X). If you need to solve one curve each for several values, so be it. But at no point do you need to calculate the input value for curve's function from the output of another one. \$\endgroup\$ – Seth Battin Jan 6 '14 at 0:50
  • \$\begingroup\$ @EthanWorley There's also another chunk of code that uses the solver to do exactly what I want What I want to do is find t for a certain X and use that to find Y(t). I'm using regular cubic bezier curves \$\endgroup\$ – user408952 Jan 6 '14 at 13:00
  • \$\begingroup\$ @SethBattin I've updated the question to make it clearer. In my case the time is X(u), which is the one thing I do know. From this I need to find u to be able to evaluate Y(u). \$\endgroup\$ – user408952 Jan 6 '14 at 13:05

You didn't specify language, but this is my implementation, P1 and P2 are curve points, while CP is control point for that segment, you would probably need only Y coordinate is my guess:

float temp1 = 1.0f - position;
float temp2 = temp1*temp1;
float temp3 = position*position;

float x = temp2*p1.x + 2*temp1*position*cp.x + temp3*p2.x;
float y = temp2*p1.y + 2*temp1*position*cp.y + temp3*p2.y;
  • \$\begingroup\$ Note: It's "solving the equation", but it's really fast. I'm using it on old cell phones (crap), and it's still lightning fast. In your case all the points will have condition, that they cannot have same X, and every next point in the curve must have Xn > Xn-1. \$\endgroup\$ – SmartK8 Jan 6 '14 at 11:09
  • \$\begingroup\$ More notes: Position should be in your situation X within the segment (Pn to Pn+1). It is in range [0.0, 1.0] where 0.0 is time at Pn, and 1.0 is time at Pn+1. \$\endgroup\$ – SmartK8 Jan 6 '14 at 11:30
  • \$\begingroup\$ setting position (u in my description) to the normalized X distance between Pn and Pn+1 isn't correct. X(u), which is the time, also varies based on u. So if I increase u with a fixed dt, X(u) will change according to a curve which, most likely, won't be at the same speed. I your case float x would actually be the time, so I would known which time's Y I got, but that isn't of much use unless I do it iteratively. \$\endgroup\$ – user408952 Jan 6 '14 at 13:13
  • \$\begingroup\$ What you need then is called easing function, or interpolation. It adds the derivation of time to the curve sampling. Example link: greweb.me/2012/02/… \$\endgroup\$ – SmartK8 Jan 6 '14 at 13:18
  • \$\begingroup\$ This answer seem to do it: stackoverflow.com/questions/8217346/… \$\endgroup\$ – SmartK8 Jan 6 '14 at 13:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.