Game Development Community

Calculating control points for smooth bezier curve

by WDDG · in Torque Game Builder · 02/21/2006 (10:03 pm) · 7 replies

Hey dudes,

I have a slightly general question. I created a BezierCurve class, which upon input of 4 points returns an array of say 25 length consisting for the 25 point positions along that curve.

If I input the 2 control points manually via guess and check, and eyeballing it. I get a very smooth curve. However my question is, is there some algorithm that given 2 endpoints will spit out 2 control points that allow for a smooth curve?

here's what I have:
function createSnakeBezierCurve (%obj, %endP1, %endP2)
{
   %x0 = getWord(%endP1, 0);
   %y0 = getWord(%endP1, 1);
   %x3 = getWord(%endP2, 0);
   %y3 = getWord(%endP2, 1);
   
   echo("Old: " SPC %x3);
   
   %obj.BezierCurve = new ScriptObject(BezierCurveClass)
   {
      x0 = %x0;
      y0 = -%y1;
      x3 = %x3;
      y3 = %y3;
   };
   
   echo("New: " SPC %obj.BezierCurve.x3);
   
   %rad = mDegToRad(90);
   
   %midX = (%obj.BezierCurve.x0 + %obj.BezierCurve.x3)/2;
   %midY = (%obj.BezierCurve.y0 + %obj.BezierCurve.y3)/2;
   
   %radius = t2dVectorDistance(%obj.BezierCurve.x0 SPC %obj.BezierCurve.y0, %obj.BezierCurve.x3 SPC %obj.BezierCurve.x3)/2;
   %rad -= %radius;
   %obj.BezierCurve.x1 = 0;//%obj.BezierCurve.x0 + %radius * mCos(%rad);
   %obj.BezierCurve.y1 = -15;//%obj.BezierCurve.y0 + %radius * mSin(%rad);
   
   %obj.BezierCurve.x2 = -15;//%obj.BezierCurve.x3 - %radius * mCos(%rad);
   %obj.BezierCurve.y2 = 0;//%obj.BezierCurve.y3 - %radius * mSin(%rad);
   
   //%obj.BezierCurve.points[0] = "0 0";
   %obj.BezierCurve.init(26);
}

function BezierCurveClass::position(%this,%t)
{

   %square        = %t*%t;
   %cube          = %t*%t*%t;
   %inv           = 1 - %t;
   %invsquare     = %inv * %inv;
   %invCube       = %inv * %inv * %inv;
   
   %x = %invcube * %this.x0 + 3 * %t * %invsquare * %this.x1 + 3 * %square * %inv * %this.x2 + %cube * %this.x3;
   %y = %invcube * %this.y0 + 3 * %t * %invsquare * %this.y1 + 3 * %square * %inv * %this.y2 + %cube * %this.y3;
   
   return (%x SPC %y);
}

function BezierCurveClass::init(%this,%numPoints)
{
   for(%i=0; %i<%numPoints; %i++)
   {
       %this.points[%i] = %this.position(%i/%numPoints);
   }      
}

As you can see around line 3, i've been trying various things, to no avail.

#1
02/22/2006 (12:10 am)
Are you looking for a smooth, kinda symmetrical or parabolic curve that would be used for rounding corners and stuff ? you could do a quadratic curve which only requires 3 points ( A B C ), but I'm not sure if thats the result you're after.

You could place the 2 control points at the same location (i think) to mimic this effect.. but you would need to know which side of the line to place them on, depending on what direction you want the curve headed.. and then just a variable to adjust the distance from the line, which should probably result in the size of the curve, although I'm not too sure if beziers would behave that way.


this is what I usually use for curves, just for objects that need to go around a corner/etc but still follow paths.
it could also be set up so that your logic just checks to see if the object is within a certain distance or percent to the next point in it's path, and then fire off this function using the point you grabbed (The distance from B, the corner) and then make C be a point the same distance from B, but along the line formed from B to wherever the line's other point is... and then it stores a few dynamic points created with the function below, for the object to eat up with moveTo's.. creating the curve effect

ehh.. I don't think i explained that very well.. :D

function findPointQuadratic( %ina, %inb, %inc, %percent )
{
   %xA = getWord(%ina, 0);
   %xB = getWord(%inb, 0);
   %xC = getWord(%inc, 0);
   %yA = getWord(%ina, 1);
   %yB = getWord(%inb, 1);
   %yC = getWord(%inc, 1);
   %t = %percent;

   %x = %xA*mPow(%t,2) + %xB*2*%t*(1-%t) + %xC*mPow((1-%t),2);
   %y = %yA*mPow(%t,2) + %yB*2*%t*(1-%t) + %yC*mPow((1-%t),2);

   return %x SPC %y;
}
#2
02/22/2006 (9:33 am)
In your solution how would you find be by just knowing the two endpoints?
#3
02/22/2006 (6:41 pm)
Not really sure, B can be anywhere, it should be possible to make a function that determines a good spot for B though, based on some rules, like the distance between A and C, the angle from A to C, and the angle from you're object to A (using these 2 angles, probably subtracting and then % 360 (mod 360), so that you can find how much the object is going to turn in degrees from A to get to C)

then using that info, you could extend point B out either on the "left" or "right" side of the line A-C.. from the center, and using a distance determined by the length of A and C.. maybe just that line length divided by 2.
Actually you might just be able to extend the line that you're object is traveling on, out past A, half the distance from A to C, and make that the point where B is placed.

so what it would result in is a triangle, and instead of traveling directly from A to C, the object will curve along the direction it was headed, but still end up at point C... I think. Is that kind of what you're wanting ?


There may be a much simpler way to do this though, I'm not really a math person.. :D



also, this would find you a position on a line, %percent is just a number from 0.0 to 1.0

I think you can do the same thing with t2d's vector functions though, which would probably be faster.

function findPointOnLine( %ina, %inb, %percent )
{
   %x1 = getWord(%ina, 0);
   %x2 = getWord(%inb, 0);
   %y1 = getWord(%ina, 1);
   %y2 = getWord(%inb, 1);
   %t = %percent;

   %x = %x1*%t + %x2*(1-%t);
   %y = %y1*%t + %y2*(1-%t);

   return %x SPC %y;
}
#4
02/24/2006 (4:22 pm)
What curve are you trying to get? In other words, you say that you have a start and an endpoint, but in that case, the best path between them is a straight line. Obviously, you're not wanting that, but a curve... but if you know how you want the curve to go, then you're pretty close to having that third point already.

So, for instance, if you were trying to go around a corner, you'd have the point before the corner, and the point after the corner, but then you'd also have the point at the corner, and then you're good to go.

I guess what I'm trying to say is that a curve with only two points doesn't really make sense... you have other points in mind, but maybe you're having trouble thinking about them that way. Maybe post exactly what kind of curve you're trying to get, and that will help you figure out how to come up with three or more points.

You can also use catmull-rom splines, that take four points, and you can pass in duplicate points (so you can pass in two points twice)... this will give you a straight line though. You always need at least three points to get any curving line :/

EDIT
Those are both cool routines to have though.. thanks for posting them guys!
#5
02/28/2006 (11:09 am)
Right that was my original point.

Quote:
However my question is, is there some algorithm that given 2 endpoints will spit out 2 control points that allow for a smooth curve?

I have the two end points, but I was wondering what the algorithm/formula would be to figure out the 2 control points to give me an S curve, or even a single other control point to give me a C like (not as steep) curve.
#6
03/01/2006 (11:45 pm)
For a bezier you need to define your own control points. All points between are then calculated. So if you want an S curve, you will need to know 4 control points, the beginning point, the climax of the first curve, the climax of the second curve, and the end point.

Those 4 control points define your curve. There is no algorithm that can tell you what your control points should be since a computer obviously can't read minds. It is probably possible however to define one that would make a simple S curve based on a sine wave and the start and end point, however you would spend much less time by just defining those two middle control points yourself.
#7
03/02/2006 (8:26 pm)
Try looking at the vector functions. If you have two points A and B you could find vector AB and BA, then rotate each one 90 degrees. That should give you some kind of S curve that scales with the distance between the two points.