Bézier curve


You may draw any curve if you can find its equation. Besides spirals mentioned in 3D line, I'll talk about how to draw the Archimedean spiral in the later document.

Sometimes, our math is not sufficient, so finding an equation to describe the curve might be hard for us. What should we do? A Bézier curve is suitable for this situation. It can model smooth curves. For example, Customizable Bezier vase uses four points P0, P1, P2 and P3 on the plane to describe a Bézier curve.

Linear Bézier curves

If you still remember calculus, you might have some impression that the derivative of a function at a point is the slope of the tangent line to the function at the point. If you forget, think about it. You can look at a short segment of the curve. If its length approaches 0, the segment is almost a straight line.

Bézier curve

More specifically, if we have the curve function f(x), a point (x. y) and move Δx in the X direction, we'll get Δy = f(x + Δx) - f(x). If Δx approaches 0, you can view the segment of the curve as a line and its slope is Δy / Δx. (This is the definition of the derivative literally, and the line is the tangent line to f(x) at that point.)

From this viewpoint, you can view a curve as being made of infinite lines. (This is the definition of the integral.) Nevertheless, the tangent line to the previous point has a different slope from the next point.

Bézier curve

We can get the slope of f(x) at (x, y) by giving x to f'(x) if we can find the curve function f(x). The question is, however, we can't figure out what the function is.

A Bézier curve also uses a line to describe a curve. What does this mean? Given two points, a linear Bézier curve is simply a straight line between them. Given points P0, P1, and P2, you can find a point Q0 at 1/4 of the line between P0 and P1, a point Q1 at 1/4 of the line between P1 and P2 and connect Q0 and Q1 to get a line. After that, find a point B0 at 1/4 of the line.

Bézier curve

Similarly, change 1/4 to 1/2 and 1/4, find out B1 and B2, and connect P0, B0, B1, B2 and P2. What will it be?

Bézier curve

It looks like a curve. The example above divides lines P0-P1 and P1-P2 into four parts. Every time you move forward one part and find a point Bn at the corresponding position. If you divide lines into more parts, such as eight, sixteen and so on, the polyline is more like a curve. (This is also the concept of the integral.)

Quadratic curves

How can we get the function of the line between Q0 and Q1? Take a look at a linear Bézier curve first. Given P0 and P1, if the length between them is LEN, we move len along the line and t = len / LEN, a function B(t) can be obtained as below.

Bézier curve

Here, P0, P1 and B(t) are vectors. In OpenSCAD, if we can use [x, y, z] to represent a point, the coordinate (x0, y0, z0) of P0 can be written as [x0, y0, z0]. We can use matrix notation to express the above function.

Bézier curve

It's simple to put out the formula above, so I leave it as an exercise for you.

Moreover, given three points P0, P1 and P2. There's a point X0 between P0 and P1, X1 between P1 and P2; the distance between X0 and X1 is LEN. We move len along the line of X0-X1, and t = len / LEN. You can express the point B(t) as:

B(t) = (1 - t) X0 + t X1, t ∈ [0, 1]

According to the formula of linear Bézier curves just mentioned above, X0 can be obtained by the equation below.

X0(t) = (1 - t) P0 + t P1, t ∈ [0, 1]

Similarly, X1 can be obtained by the formula below.

X1(t) = (1 - t) P1 + t P2, t ∈ [0, 1]

Substitute these two formulas for X0 and X1 in B(t), we can derive the formula.

B(t) = (1 - t) * (1 - t) * P0 + 2 * t * (1 - t) * P1 + t * t * P2, t ∈ [0, 1]

It is the formula of a quadratic Bézier curve. You can derive higher-order curves with more intermediate points through this process, or find the equations from Bézier curve directly. (Pascal's triangle provides a good pattern to remember those coefficients).

Implementing cubic Bézier curves

Painting programs commonly provide tools for creating a Bezier curve because it's easy to model smooth curves by just moving those control points.

OpenSCAD, however, doesn't use a mouse for modeling. It's probably the reason why OpenSCAD doesn't provide tools for Bézier curves. Nevertheless, we can implement it by ourselves. It's troublesome to have too many control points, so here we just define functions for cubic Bézier curves, used in Customizable Bezier vase.

function bezier_coordinate(t, n0, n1, n2, n3) = 
    n0 * pow((1 - t), 3) + 3 * n1 * t * pow((1 - t), 2) + 
        3 * n2 * pow(t, 2) * (1 - t) + n3 * pow(t, 3);

function bezier_point(t, p0, p1, p2, p3) = 
    [
        bezier_coordinate(t, p0[0], p1[0], p2[0], p3[0]),
        bezier_coordinate(t, p0[1], p1[1], p2[1], p3[1]),
        bezier_coordinate(t, p0[2], p1[2], p2[2], p3[2])
    ];


function bezier_curve(t_step, p0, p1, p2, p3) = 
    [for(t = [0: t_step: 1 + t_step]) bezier_point(t, p0, p1, p2, p3)];

The calculated points can pass into the polyline module developed in Line. For example.

function bezier_coordinate(t, n0, n1, n2, n3) = 
    n0 * pow((1 - t), 3) + 3 * n1 * t * pow((1 - t), 2) + 
        3 * n2 * pow(t, 2) * (1 - t) + n3 * pow(t, 3);

function bezier_point(t, p0, p1, p2, p3) = 
    [
        bezier_coordinate(t, p0[0], p1[0], p2[0], p3[0]),
        bezier_coordinate(t, p0[1], p1[1], p2[1], p3[1]),
        bezier_coordinate(t, p0[2], p1[2], p2[2], p3[2])
    ];


function bezier_curve(t_step, p0, p1, p2, p3) = 
    [for(t = [0: t_step: 1 + t_step]) bezier_point(t, p0, p1, p2, p3)];


module line(point1, point2, width = 1, cap_round = true) {
    angle = 90 - atan((point2[1] - point1[1]) / (point2[0] - point1[0]));
    offset_x = 0.5 * width * cos(angle);
    offset_y = 0.5 * width * sin(angle);

    offset1 = [-offset_x, offset_y];
    offset2 = [offset_x, -offset_y];

    if(cap_round) {
        translate(point1) circle(d = width, $fn = 24);
        translate(point2) circle(d = width, $fn = 24);
    }

    polygon(points=[
        point1 + offset1, point2 + offset1,  
        point2 + offset2, point1 + offset2
    ]);
}

module polyline(points, width = 1) {
    module polyline_inner(points, index) {
        if(index < len(points)) {
            line(points[index - 1], points[index], width);
            polyline_inner(points, index + 1);
        }
    }

    polyline_inner(points, 1);
}

t_step = 0.05;
width = 2;

p0 = [0, 0];
p1 = [40, 60];
p2 = [-50, 90];
p3 = [0, 200];

points = bezier_curve(t_step, 
    p0, p1, p2, p3
);

polyline(points, width);

The above code models a curve below.

Bézier curve

Here is an exercise for you. How to use the above code to create Customizable Bezier vase?