Calculating and Drawing Bezier Curves with BlitzMax
by ImaginaryHuman – 07/07/2009

Introduction

In the world of computer graphics, images on the screen can be created by the computer in a series of drawing commands. BlitzMax is no foreigner to graphics rendering, able to draw pixels, lines, rectangles, ovals and images. Using Plot, DrawLine, DrawRect and DrawOval BlitzMax can render filled shapes to the screen, using DrawImage to draw textured rectangles.

You can even combine these commands with transformations, including SetOrigin, SetHandle, SetScale and SetRotation. This means you can draw rotated, scaled and re-positioned lines, rectangles, ovals and images with ease. BlitzMax is even able to use the graphics hardware to draw these objects via the OpenGL or DirectX interfaces, proving to be capable at fast hardware-based rendering of multiple objects in a variety of blend modes.

While these graphics commands may be sufficient for a variety of presentations, BlitzMax does not by default provide commands for the drawing of curved lines and objects. In addition to the desirable ability to draw curves is also the ability to calculate them, and to use the results of these calculations for other purposes.

Curves can be used to model the animation paths which objects will follow, to smoothly transition from one set of data to another, to smooth out fluctuations in performance, or even to smoothly morph one person’s face into another. They have many uses, both in games and applications, and in this article we’ll look at how we can create curves with BlitzMax.

Types of Splines

A spline is basically another name for a curved line. Typically the curve is designed by specifying some coordinates, called `control points`. Usually some mathematical equation then generates the curve based on the position of the control points. The curve usually begins near to the first control point, ending near the last. Adjusting the curve usually means moving the control points, which then automatically updates the curve.

The shape of the curve is influenced by the position of one or more control points along the way. Sometimes the curve will pass through all of the control points, or sometimes only the first and last, or none, depending on the type of spline. Below is a simple example of what some different types of splines may look like, accompanied by control points:

There are many different types of splines. Following this article, and for a more in-depth study covering most of their different qualities and characteristics, you might enjoy reading this PDF document.

There is also a significant volume of information online about the many different types of splines. You may enjoy browsing through the wikipedia pages.

Or for the more hands-on among you, might try these simple java applets which let you draw and adjust various types of splines.

And here is the Wikipedia page for Bezier curves which shows a nice animated version of the algorithm we are about to use (about 2/3rds of the way down the page).

Different types of splines lend themselves to different uses. For example, the Catmull-Rom spline passes smoothly through every control point. This type of spline can be great for drawing a smooth animation path where an object needs to continually flow through various points without stopping or experiencing sudden changes in direction.

With a few clicks of the mouse you can decide exactly which coordinates an object will pass through and it will be sure to visit all of those locations. Since the curve passes through every control point, clicking to draw the curve is very easy – it does most of the work for you automatically. The curve will smoothly ease into and out of each control point gently. The direction in which the curve eases into a point depends also on the direction it will need to take afterwards toward the next control point.

On the other hand, sometimes you might prefer that the curve does not smoothly pass through every point. You might be designing a logo where you need a sharp right-angled corner rather than a curved one, and yet continue with a curved edge afterwards. Or you might need a sharp point in amongst flowing curves. For this you might prefer to use Bezier curves with multiple individual curve sections joined together.

Sophisticated graphics applications such as used for ray-tracing, vector graphics, 3D modelling and animation often make use of Bezier curves, b-splines and NURBS (Non-Uniform Rational B-Splines). Each offers a unique way to define the curves and to manipulate their shape. Some people prefer different types of splines because of the way you can manipulate them, or because of the subtle control offered over their shape.

Although the many different types of splines each have interesting properties, covering them all is beyond this article and I recommend reading the above articles if you are interested. For our purposes, we’ll take a look at the relatively simple and easy-to-grasp Bezier curves.

Bezier Curves

The Bezier curve was invented by the mathematician Pierre Bezier. A Bezier curve segment begins at a first control point, is influenced by one or more other control points (which it does not pass through), finally ending at the last control point. The number of control points in a single Bezier curve is often 3 or 4, but can be more. At it simplest, we’ll take a look at a 3-point Bezier curve, otherwise known as a quadratic curve. Here is an example of how a 3-point Bezier is put together:

P1 is the first point of the curve. P3 is the last point of the curve. P2 is an in-between point which, although the curve does not pass through it, attracts the curve to it as if magnetic. If you imagine a straight line from P1 to P3, you can then imagine that P2 is attracting that line towards itself, causing it to bend. Essentially, curves are generated by calculating the influence of each control point along the way.

The positions of the control points determines the shape of the curve. The curve is adjusted by moving the control points. You generally cannot just click on the curve to drag it into a new shape, although such higher-level functionality can be programmed. To modify the curve, you would normally click on a control point and move it to a new position – the curve adjusts and updates automatically. Curve editing is somewhat indirect with Beziers – only the first and last control points are passed through. They are suited to a little more detailed tweaking than the Catmull-Rom spline which makes them good for generating graphics.

With three points defining a curve, you generally can create an arc covering up to 180 degrees of curvature. A curve which starts out by heading in one direction can end by returning in the opposite direction – the two end-points can be at the same location. To create more complex curves you can simply create further Beziers following on from each other, sharing the end-point of the previous curve and re-using it as the starting point of a new curve.

Drawing a 3-Point Bezier Curve

The drawing of a Bezier curve is not as easy as simply drawing lots of pixels. It’s also not as easy as just asking the graphics hardware to draw a curve – it’s generally not capable of drawing anything other than points, lines or triangles. How does a straight-edged line represent a curve? Depending on the algorithm, there is usually some math involved, and it’s not always easy to understand, especially for a beginner. We won’t be getting into any difficult math here!

We also need to consider that a curve is really a virtual mathematical line, with potentially infinite accuracy, and no width. Zooming into the curve really should continue to show us perfectly smooth curvature no matter how far we zoom in. And indeed that is one of the most desirable properties of curves – vector graphics can be zoomed in or out significantly and still show accurate smooth curves – not the jagged pixels which you’d see from a zoomed bitmap.

A curve is really a single continuous entity, which does not automatically map to a screen of separate digital pixels. It’s kind of like the difference between analog and digital. The only way we can draw it on the screen is to approximate where the curve will be as it crosses each individual pixel at a specific resolution and size. We need to `modulate` it (sample it), like a modem would modulate an analog signal and turn it into a digital one. Drawing a curve means converting from an analog curve to a digital curve.

Basically, almost all methods of drawing a curve involve starting at the beginning point, gradually progressing along the curve by varying a value in an equation, and stopping at the end point. By just varying one value – the `position along the curve`, we can `step` along the curve, drawing as we go. This means that to draw it, we have to draw lots of little `pieces` of the curve in succession, breaking the continuous curve into little chunks, as below:

In order to calculate the curve – or rather, several fixed points along the curve, we have to make some kind of decision about what size the step will be, and thus how much distance we travel in each step along the curve. This distance will determine how we draw each little piece of the curve.

Unfortunately we can’t very easily say `step 1 pixel along the curve`. It’s not as easy as drawing straight lines or rectangles, where you can just step one pixel at a time. The curve has a different length which is not necessarily obvious based on the control points. Prior to actually calculating the whole curve, it is difficult or impossible to know how many pixels it will cover – a catch 22.

To know the curve length first, you’d end up having to calculate the whole curve twice, which is undesirable. The more mathematically heavy approaches to curve calculation can use a large number of additions and multiplications – hundreds or even thousands per curve. We need a way to both calculate and draw the curve as we travel along it but without knowing the length – which usually means guessing the step size.

We have to decide upon some kind of step value which we think will be an acceptable size, and this can vary depending on screen resolution, performance, the sharpness of the curve and how smooth we want the curve to look. We need a value which produces a good enough accuracy to be able to represent the curve as `smooth` visually. Obviously in most cases we don’t want the curve to look jagged or angular – unless it doesn’t matter in what you’re using them for.

If we make our step very small and use Plot to draw the pixels, we are hoping that the step will be small enough not to produce any gaps in the curve. If we happen to pick a step which isn’t short enough we might skip a few pixels along the way, breaking the curve. Then again, too small a step when it isn’t necessary can mean you’re drawing the same pixel over and over again. That especially doesn’t work well when you’re also drawing in a blend mode like LightBlend or ShadeBlend, yet alone being inefficient.

Here are some examples of how drawing individual points might produce desirable or undesirable results (I used DrawOval X,Y,3,3 for each point to make it more obvious, rendering with ShadeBlend to show overlapping points):

Another option is to draw the curve by linking those spaced-out Plots together – drawing lots of short straight lines one after the after. Obviously if these lines are long, or not short enough to represent a shaper curve, we’re going to start noticing that we’ve drawn a bunch of straight edges and not perfectly curved ones – although overall it would approximately represent a curve.

With some experimentation you can arrive at a step size which produces short enough lines as to not be very noticeable for most curves. In general, the sharper the angle of the curve the smaller the step must be in order to smoothly represent it. Gradual curves work okay with longer straight line segments. The following diagram demonstrates the effect of different curves with the same step size (10 segments per curve ? a step of 0.1):

It is quite difficult visually to tell the difference between a curve made of short straight lines and one made of short curves. In fact, BlitzMax’s DrawOval command doesn’t really draw perfectly curved ovals, it draws lots of straight edged shapes which, together, approximately looks like an oval – and the size of each of these sub-shapes is small enough that you can’t very easily tell the difference. It might not be obvious, but BlitzMax’s ovals are actually made of straight-edged triangles.

We need to use a similar trick to draw our curves, converting from a perfect mathematical curve to an approximate curve made of little straight lines – something the graphics hardware can actually draw.

The Bezier Curve Algorithm

Now we are ready to look at an algorithm for calculating a Bezier curve. We are not going to use the traditional mathematical equations for Bezier curves. Instead, there is a simpler and easier-to-understand method for calculating them. So long as you know how to add, subtract and multiply floating-point numbers with BlitzMax, you should be all set.

This simpler method, and usually more efficient also, was first publicized by a guy called De Casteljau. I also arrived at this method independently, but he beat me to it. What he observed is that you can produce a Bezier curve through a simple series of subdivisions of straight lines.

To understand this, first take a look at this diagram, showing a new line and some new points, which we will need to calculate in order to arrive at a point on the curve. In this example we’ll show the mid-point of the curve, found by simply dividing the length of the two sides by 2 (or multiplying by 0.5) and then finding the middle of the line between those two points:

Although in this diagram we are finding the middle of the curve with a position along the curve of 0.5, we can use any other values between 0 and 1 to find the whole curve. If we wanted to find a point 25% of the way along the curve, we would first find the length of the line `P1 to P2` and then step along that line by multiplying the length by 0.25 instead of 0.5. We do the same with the line from P2 to P3. The new line which joins these points is then on a diagonal. We can find the point on the curve by multiplying the length of the new line by 0.25 instead of 0.5.

Since we are going to be drawing short lines to build our curve, it is worth realizing that when we’re at the start of the curve, at point P1, we can’t draw our first line yet. It isn’t until we’ve taken a step along the curve and calculated a second coordinate that we can then draw our first line. Also the first point P1 is already calculated (it’s the control point’s coordinates), so you don’t need to calculate it as a point on the curve.

First of all we need a step value – so for simplicity let’s say we want to represent our curve with 5 short lines. For handy maths purposes we would like our step value to begin at 0 and end at 1.0. Later we can just multiply by this number to do part of the algorithm. 0 means we are at the start of the line, and 1.0 means we are at the end of the line.

So if we’re going to have 5 line segments, our step will be 1.0/5.0 which is a step of 0.2. Our Bezier will actually consist of 6 points along the curve, which will be 0, 0.2, 0.4, 0.6, 0.8 and 1.0. These will be controlled by three control points, represented by 5 straight lines.

The algorithm in general has you gradually incrementing a `position on the curve` value, ranging from 0 to 1. We will be adding our step value to it each time and then will use that result to calculate the actual coordinates of a point on the curve.

For each step we take, we start out by measuring the distance between the first point P1 and the second point P2 – we’ll call it P12. Since coordinates have both X and Y components, we need to find the difference between P1 and P2’s X coordinates, and then also the difference between P1 and P2’s Y coordinates. This gives us two distances, and we can find these with a couple of simple subtractions:

P12X = P2X – P1X
P12Y = P2Y – P1Y

We then need to find the point along each of these distances where we’ve stepped to so far. This is a simple matter of multiplying the two distances by our `position on the curve` value. So let’s say we just added the step of 0.2 to our position (which begins at 0). Now we’re at 0.2 on the curve. We multiply 0.2 by the distance between P1’s X and P2’s X, and then we multiply 0.2 separately by the distance between P1’s Y and P2’s Y, as such:

P12X = 0.2 * P12X
P12Y = 0.2 * P12Y

This basically has us stepping along the line between P1 and P2 by the same amount that we have stepped along our curve so far. In a way, once we get to 1.0 on the curve, we will have also arrived at point P2 along the line between P1 and P2.

Now we find the distance between P2 and P3 in the same way as before – just subtracting the X coordinates from each other, and then the same with the Y coordinates. Then we just need to multiply each of these distances by our `position on the curve value`. This gives us point P23, for example:

P23X = P3X – P2X
P23Y = P3Y – P2Y
P23X = 0.2 * P23X
P23Y = 0.2 * P23Y

We can now visualize an imaginary straight line between our two new points – from P12 to P23. What I can now tell you is that somewhere along this new line is a new point, P123, which is exactly on the Bezier curve, in this case it will be 20% of the way along the line. All we have to do is find that point.

We simply do the same thing we did before with the other distances, using the new line between P12 and P23. You just find the difference between P12’s X and P23’s X, then the difference between P12’s Y and P23’s Y. This time around, however, we also have to add-in the points P1 and P2 so that our distance will be correct (because P12 and P23 are relative, we need them to be absolute coordinates). Then it’s just a matter of multiplying these two distances by our `position on the curve` value, for example:

P123X = (P2X+P23X) – (P1X+P12X)
P123Y = (P2Y+P23Y) – (P1Y+P12Y)
P123X = 0.2 * P123X
P123Y = 0.2 * P123Y

This produces a final point, P123. Before we can think of it in terms of screen coordinates, we simply need to add some coordinates to it to change back from relative distances to absolute screen coordinates, like so:

P123X = P123X + P1X + P12X
P123Y = P123Y + P1Y + P12Y

We add P1 and P12 to the coordinates of the curve point. P1 is added so that the whole curve starts at P1 and not at 0. Then we add in P12 because we need to step down to the next level of subdivision and add that point as an offset. We’re just adding up the steps we need to take to get to P123 by taking the shortest route from P1. ie we add P1, then P12, then finally P123. The coordinate P123X,P123Y is now the actual point on the curve in screen coordinates.

The basic broken-down BlitzMax sourcecode to calculate a single point on a 3-point quadratic Bezier curve is as follows:

Function QuadraticBezierPoint(P1X:Float,P1Y:Float,P2X:Float,P2Y:Float,P3X:Float,P3Y:Float,PositionOnCurve:Float,P123X:Float Var,P123Y:Float Var)
‘Calculate a point on a quadratic bezier curve
‘Requires three control points with X and Y coordinates
‘and a current position on the curve in the range 0..1
‘Coordinates are returned in P123X and P123Y variables

‘Calculate distances between control points
Local P12X:Float=P2X-P1X ‘Distance in X between P1X and P2X
Local P12Y:Float=P2Y-P1Y ‘Distance in Y between P1Y and P2Y
Local P23X:Float=P3X-P2X ‘Distance in X between P2X and P3X
Local P23Y:Float=P3Y-P2Y ‘Distance in Y between P2Y and P3Y

‘Find position along the lines from P1 to P2, and from P2 to P3
P12X:*PositionOnCurve ‘Multiply position by P12 X distance
P12Y:*PositionOnCurve ‘Multiply position by P12 Y distance
P23X:*PositionOnCurve ‘Multiply position by P23 X distance
P23Y:*PositionOnCurve ‘Multiply position by P23 Y distance

‘Calculate distance between the two positions
P123X=(P2X+P23X)-(P1X+P12X) ‘Distance in X between P12X and P23X
P123Y=(P2Y+P23Y)-(P1Y+P12Y) ‘Distance in Y between P12X And P23X

‘Find position along the line from P12 and P23
P123X:*PositionOnCurve ‘Multiply position by P123 X distance
P123Y:*PositionOnCurve ‘Multiply position by P123 Y distance

‘Add up the relative positions to make screen coordinates
P123X:+P1X+P12X
P123Y:+P1Y+P12Y

‘The coordinate P123X,P123Y is the location of the point on the curve
End Function

To use this function you simply need to pass three pairs of coordinates for the control points, a position along the curve (from 0 to 1), and then a couple of variables into which the point’s X and Y coordinates will be returned.

Here is a more compact version of the same code:

Function QuadraticBezierPoint(P1X:Float,P1Y:Float,P2X:Float,P2Y:Float,P3X:Float,P3Y:Float,Position:Float,P123X:Float Var,P123Y:Float Var)
Local P12X:Float=(P2X-P1X)*Position
Local P12Y:Float=(P2Y-P1Y)*Position
Local P23X:Float=(P3X-P2X)*Position
Local P23Y:Float=(P3Y-P2Y)*Position
P123X=(((P2X+P23X)-(P1X+P12X))*Position)+P1X+P12X
P123Y=(((P2Y+P23Y)-(P1Y+P12Y))*Position)+P1Y+P12Y
End Function

Now that we know how to find a point on the curve, we can actually draw a line from the previous `position on the curve` (the coordinate of point P1 in this case), to the new position on the curve – point P123. Our first piece of the curve is then complete. We now repeat this whole process all over again, continually adding the step value to the position on the curve and then multiplying this by the three sets of distances to turn it into a final coordinate. Running the loop 5 times gives us our 5 segments:

Hey presto, a Bezier curve – or something close to one. We could simply make our step size smaller to make this look really smooth – here is an example of how it would look with a step size of 0.05 (with 20 segments):

Here is an interactive program which will allow you to click on and drag the control points to adjust a quadratic Bezier curve, with 20 line segments:

Local NumberOfSegments:Int=20

SetGraphicsDriver GLMax2DDriver()
Graphics 800,600
SetClsColor $FF,$FF,$FF

Local PX:Float[3]
Local PY:Float[3]
PX[0]=50
PY[0]=550
PX[1]=400
PY[1]=80
PX[2]=750
PY[2]=550

Local Position:Float
Local X:Float
Local Y:Float
Local X2:Float
Local Y2:Float
Local Controlling:Int=False
Local ControlPoint:Int=0
Local MX:Int
Local MY:Int
Local Point:Int
Local SegmentSize:Float=1.0/NumberOfSegments
Repeat
‘Control it
MX=MouseX()
MY=MouseY()
If Controlling=False
If MouseDown(1)
For Point=0 To 2
If MX>=PX[Point]-15 And MY>=PY[Point]-15 And MX<=PX[Point]+15 And MY<=PY[Point]+15
Controlling=True
ControlPoint=Point
EndIf
Next
Else
Controlling=False
EndIf
EndIf
If Controlling=True
PX[ControlPoint]=MX
PY[ControlPoint]=MY
If Not MouseDown(1) Then Controlling=False
EndIf

'Draw stuff
Cls
SetColor $88,$88,$88
DrawLine PX[0],PY[0],PX[1],PY[1] 'Draw lines
DrawLine PX[1],PY[1],PX[2],PY[2]
SetColor $FF,$0,$0
DrawOval PX[0]-5,PY[0]-5,10,10 'Draw control points
DrawOval PX[1]-5,PY[1]-5,10,10
DrawOval PX[2]-5,PY[2]-5,10,10
SetColor 121,195,3

'Draw curve
X=PX[0]
Y=PY[0]
Position=SegmentSize
While Position<=1.00001 'Go to 1.00001 to make sure final line is drawn
QuadraticBezierPoint(PX[0],PY[0],PX[1],PY[1],PX[2],PY[2],Position,X2,Y2)
DrawLine X,Y,X2,Y2
X=X2
Y=Y2
Position:+SegmentSize
Wend

Flip 1
Until KeyHit(KEY_ESCAPE) Or AppTerminate()

Function QuadraticBezierPoint(P1X:Float,P1Y:Float,P2X:Float,P2Y:Float,P3X:Float,P3Y:Float,Position:Float,P123X:Float Var,P123Y:Float Var)
Local P12X:Float=(P2X-P1X)*Position
Local P12Y:Float=(P2Y-P1Y)*Position
Local P23X:Float=(P3X-P2X)*Position
Local P23Y:Float=(P3Y-P2Y)*Position
P123X=(((P2X+P23X)-(P1X+P12X))*Position)+P1X+P12X
P123Y=(((P2Y+P23Y)-(P1Y+P12Y))*Position)+P1Y+P12Y
End Function

A Sequence of Beziers

Sometimes it is desirable to have much longer smooth curves with many twists and turns. Obviously you can't do this with a single 3-point Bezier curve – it can only draw an arc and can't reverse the direction of its curvature like a cubic Bezier can.

To create more complex, smoothly-flowing curves using 3-point Beziers, we are faced with the fact that even if two curves share an end-point the curve won't necessarily flow smoothly between them, as in the diagram below (which looks like the logo for Motorola):

There is one special case where the two curves actually would flow smoothly, and that’s when the line between P2 and P3 in the first curve is extended exactly into the second curve’s P1-to-P2 line. So long as these two lines are at the same angle, forming a straight line between the first curve’s P2 and the second curves P2, passing through the shared P3, the Bezier curve will flow naturally and `continuously`. This is an emulation of what is called `continuity` in spline lingo. While Bezier’s don’t naturally have continuity at their end points, we can emulate it with a bit of extra work.

A Catmull-Rom spline has this kind of smooth continuity all the time for every control point. We can emulate a Catmull-Rom spline, ie making it seem like the curve always flows smoothly through every control point, using Bezier curves. We would have to make the two lines which join to the shared control point be at the same straight angle and make the length of those two lines the same. Technically we might also have to calculate the middle control points automatically (e.g. P2) based on the angle of the two lines and where they coincide, and adjust the position of the middle control points, but in general here’s how it would look:

And here is an interactive program to show how we can join two quadratic Bezier curves together using this technique – we simply force the position of the shared point to be calculated based on the distance between the two surrounding points, divided in half:

Local NumberOfSegments:Int=20

SetGraphicsDriver GLMax2DDriver()
Graphics 800,600
SetClsColor $FF,$FF,$FF

Local PX:Float[5]
Local PY:Float[5]
PX[0]=50
PY[0]=550
PX[1]=225
PY[1]=50
PX[2]=370
PY[2]=300
PX[3]=525
PY[3]=575
PX[4]=750
PY[4]=50

Local Position:Float
Local X:Float
Local Y:Float
Local X2:Float
Local Y2:Float
Local Controlling:Int=False
Local ControlPoint:Int=0
Local MX:Int
Local MY:Int
Local Point:Int
Local SegmentSize:Float=1.0/NumberOfSegments
Repeat
Cls

‘Control it
MX=MouseX()
MY=MouseY()
If Controlling=False
If MouseDown(1)
For Point=0 To 4
If MX>=PX[Point]-15 And MY>=PY[Point]-15 And MX<=PX[Point]+15 And MY<=PY[Point]+15 And Point2
‘Control the point so long as it’s not the middle shared point
Controlling=True
ControlPoint=Point
EndIf
Next
Else
Controlling=False
EndIf
EndIf
If Controlling=True
SetColor $0,$0,$0
DrawText MX+”,”+MY,0,32
PX[ControlPoint]=MX
PY[ControlPoint]=MY
If Not MouseDown(1) Then Controlling=False
EndIf

‘Force continuity – calculate the middle shared point automatically
PX[2]=PX[1]+((PX[3]-PX[1])*0.5)
PY[2]=PY[1]+((PY[3]-PY[1])*0.5)

‘Draw stuff
SetColor $88,$88,$88
DrawLine PX[0],PY[0],PX[1],PY[1] ‘Draw lines
DrawLine PX[1],PY[1],PX[2],PY[2]
DrawLine PX[2],PY[2],PX[3],PY[3]
DrawLine PX[3],PY[3],PX[4],PY[4]
SetColor $FF,$0,$0
DrawOval PX[0]-5,PY[0]-5,10,10 ‘Draw control points
DrawOval PX[1]-5,PY[1]-5,10,10
‘DrawOval PX[2]-5,PY[2]-5,10,10 ‘Don’t draw middle shared point
DrawOval PX[3]-5,PY[3]-5,10,10
DrawOval PX[4]-5,PY[4]-5,10,10
SetColor 121,195,3

‘Draw curve 1
X=PX[0]
Y=PY[0]
Position=SegmentSize
While Position<=1.00001 'Go to 1.00001 to make sure final line is drawn
QuadraticBezierPoint(PX[0],PY[0],PX[1],PY[1],PX[2],PY[2],Position,X2,Y2)
DrawLine X,Y,X2,Y2
X=X2
Y=Y2
Position:+SegmentSize
Wend

'Draw curve 2
X=PX[2]
Y=PY[2]
Position=SegmentSize
While Position<=1.00001 'Go to 1.00001 to make sure final line is drawn
QuadraticBezierPoint(PX[2],PY[2],PX[3],PY[3],PX[4],PY[4],Position,X2,Y2)
DrawLine X,Y,X2,Y2
X=X2
Y=Y2
Position:+SegmentSize
Wend

Flip 1
Until KeyHit(KEY_ESCAPE) Or AppTerminate()

Function QuadraticBezierPoint(P1X:Float,P1Y:Float,P2X:Float,P2Y:Float,P3X:Float,P3Y:Float,Position:Float,P123X:Float Var,P123Y:Float Var)
Local P12X:Float=(P2X-P1X)*Position
Local P12Y:Float=(P2Y-P1Y)*Position
Local P23X:Float=(P3X-P2X)*Position
Local P23Y:Float=(P3Y-P2Y)*Position
P123X=(((P2X+P23X)-(P1X+P12X))*Position)+P1X+P12X
P123Y=(((P2Y+P23Y)-(P1Y+P12Y))*Position)+P1Y+P12Y
End Function

We could use this to create an animation path for a sprite – the sprite would be sure to always pass smoothly through every *shared* control point, but it would not pass through the middle control points. We could also keep adding more and more Beziers, each sharing the end point of the previous Bezier, forming a long and complicated winding curve.

When we join together quadratic Bezier curves like this, they look and act similar to how a Cubic Bezier curve would act, based on four control points – they technically might not be exactly the same shape but the editing functionality is similar. Alternatively, it is fairly trivial to add extra intermediary points and lines to the algorithm to provide support for cubic beziers. You could even use more than four points, as shown in the animations on the Wikipedia page for bezier curves.

Conclusion

There are many uses for Bezier curves and you can even create different ways to adjust them based on indirectly moving the control points. Applications like Inkscape allow you click on the curve itself and drag it to a new position. This of course requires some extra programming. You could also come up with other ways to adjust the curve suited to your needs.

It is also possible to do other fancy things with Beziers such as splitting the curve at a given point, joining curves together into one, and adding further editing features such as are found in programs like Inkscape.

Beyond this is the challenging topic of how to fill a solid object composed from Bezier curves. This requires a process of tessellation to turn the object into renderable triangles. We can even create three-dimensional curved surfaces (commonly called patches) using a grid of Beziers, which are then tessellated into a triangle mesh. These topics are big enough that we'll leave them for a future article.

Hopefully, following this article you will be able to implement Bezier curves into your future games, applications, and editors without too much trouble. Remember there are many resources online and the BlitzMax community is always ready to help.

2 Responses to “Calculating and Drawing Bezier Curves with BlitzMax”


  1. 1 Peter September 23, 2009 at 8:17 am

    Great article! So easy when you know how! Thanks!


  1. 1 Guru Meditations Trackback on July 7, 2009 at 7:50 pm

Leave a Reply




Welcome

Enjoy the fun and excitement of computer games, but even moreso, the creative expression of developing your own. And the graphics are cool too. :-)

What was that?

Categorically speaking...

Game Development Graphics Uncategorized

What just happened?

  • 1,196 hits