Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Speed of quadratic curve

Tags:

c++

bezier

I'm writing a 2d game and I have birds in a camera-space. I want to make them fly. So, I generate 3 ~random points. First one is left-upper side, second: middle-bottom, third: right-upper.

As a result I have 180deg rotated triangle.

To move a bird through the curve's path I have a t-parameter which is increased in each frame (render loop) by some delta.

The problem is that in different curves birds have different speed. If the triangle is "wide" (1) they are more slowly, if it's stretched by Y-coordinate (2), the speed is very fast.

enter image description here

But I want to make speed equal at different curves. It's logically, that I have to change delta which is appended each frame for each curve.


I've tried to solve it like this:

Find the ~length of the curve by summing length of 2 vectors: P1P2 and P2P3.

Than I've defined the speed for 1 virtual meter per frame. A little pseudocode:

float pixelsInMeter = 92.f; // One virtual meter equals to this number of pixels
float length = len(P1P2) + len(P2P3)
float speed  = 0.0003f; // m/frame

// (length * speed) / etalon_length
float speedForTheCurve = toPixels( (toMeters(length) * speed) / 1.f);

// ...
// Each frame code:
t += speedForTheCurve;
Vector2 newPos = BezierQuadratic(t, P1, P2, P3);

But birds anyway have different speed. What's wrong? Or maybe there is a better way.

like image 541
Max Frai Avatar asked Feb 09 '13 19:02

Max Frai


1 Answers

The Bezier function you're using is a parametrized function with bounds [0...1]. You're mucking with the step-size, which is why you're getting crazy speeds. Generally speaking, the distance d is the dependent variable in the equation, which says to me that their speeds will be different based on the length of the curve.

Since speed is your dependent variable, we're going to vectorize your function by computing the step-size.

Check out this pseudocode:

P1 = (x1, y1)

P2 = (x2, y2)

P3 = (x3, y3)

int vec[100][2]

int getPoint(int p1, int p2, float stepSize) {
   return p1 + (p2 - p1)*stepSize;
}

for (float i = 0.0; i < 1.0; i += 0.01 ) {
   int newX = getPoint(getPoint(x1, x2, i), getPoint(x2, x3, i), i);
   int newY = getPoint(getPoint(y1, y2, i), getPoint(y2, y3, i), i);
   vec[iter++][0] = newX;
   vec[iter][1] = newY;
}

You can get the delta values by performing a first difference but I don't think that's necessary. As long as you move all the birds the appropriate distance based on the step iteration they will all move different distances but they will start and end their trajectories identically.

From your equation, we can compute the pixel delta step size:

int pixelsToMove = toMeter(sqrt((x2 - x1)^2 + (y2 - y1)^2))/pixelsInMeter;

Which will give you the appropriate amount of pixels to move the bird. That way they'll all move different step sizes, but their speeds will be different. Does that make sense?


Or, try something like this (much harder):

  1. Obtain the actual quadratic function of the three points you chose.
  2. Integrate the quadratic between two xy rectangular coordinate
  3. Convert computed length into pixels or whatever you're using
  4. Obtain dependent variable speed so all curves finish at the same time.

Let's start with quadratic stuff:

y = Ax^2 + Bx + C where A != 0, so since you have three points, you will need three equations. Using algebra, you can solve for the contants:

A = (y3 - y2)/((x3 - x2)(x3 - x1)) - (y1 - y2)/((x1 - x2)(x3 - x1))

B = (y1 - y2 + A(x2^2 - x1^2))/(x1 - x2)

C = y1 - Ax1^2 - Bx1

Then you can use the formula above to obtain a closed-form arc length. Check this website out, wolfram will integrate it for you and you just have to type it:

Closed form solution for quadradic integration

Now that you've computed the arc length, convert actualArcLength to the speed or whatever unit you're using:

float speedForTheCurve = toPixels( (toMeters(actualArcLength) * speed) / 1.f);
like image 135
Tyler Jandreau Avatar answered Nov 08 '22 20:11

Tyler Jandreau