# Bézier surface

As mentioned in “Bézier curve“, you may draw any curve if you can find its equation. For any surface, it's valid as well. But sometimes, our math is not sufficient, so finding an equation to describe the surface might be hard for us. What should we do?

Think about it. We can use Bézier curves to create smooth curves. Can we expand Bézier curves into Bézier surfaces?

The above surface uses 16 control points (the green points) to create a Bézier surface. You can change control points to change the shape of the surface.

# Four Bézier curves

Many graphics editors use cubic Bézier curves to create a curve. A cubic Bézier curve needs four control points. A complex curve may be composed of several Bézier curves that share some control points. For example:

The above curve is composed of three cubic Bézier curves. The 4th control point of the 1st Bézier curve is actually the 1st control point of the 2nd Bézier curve. Three control points compose a tangent at the shared control point.

For easily control, I'll use cubic Bézier curves as a base and expand them into a cubic Bézier surface. We need four cubic Bézier curves so there are 16 control points.

Before we move on, we need to encapsulate the example code shown in “Bézier curve” into a bezier_curve function. It returns all points on the path of a Bézier curve. Combined with the code listed in “3D line“, we can draw a Bézier curve. However, I'll use the polyline3d module which is faster when drawing a polyline. The bezier_curve function and the polyline3d module are both contained in my dotSCAD library.

First, we simply use 16 control points to draw four Bézier curves.

ctrl_pts = [
[[0, 0, 20],  [60, 0, -35],   [90, 0, 60],    [200, 0, 5]],
[[0, 50, 30], [100, 60, -25], [120, 50, 120], [200, 50, 5]],
[[0, 100, 0], [60, 120, 35],  [90, 100, 60],  [200, 100, 45]],
[[0, 150, 0], [60, 150, -35], [90, 180, 60],  [200, 150, 45]]
];

thickness = 2;
t_step = 0.05;

for(i = [0:len(ctrl_pts) - 1]) {
bezier_pts = bezier_curve(t_step, ctrl_pts[i]);
polyline3d(bezier_pts, thickness);
}

# From curves to a surface

We have four Bézier curves now. It seems that we may use these generated points to create a surface. Let's try it. We need the solution mentioned in “Function grapher“. Similarly, I create a function_grapher module as well for focusing our concern on how to create a Bézier surface.

ctrl_pts = [
[[0, 0, 20],  [60, 0, -35],   [90, 0, 60],    [200, 0, 5]],
[[0, 50, 30], [100, 60, -25], [120, 50, 120], [200, 50, 5]],
[[0, 100, 0], [60, 120, 35],  [90, 100, 60],  [200, 100, 45]],
[[0, 150, 0], [60, 150, -35], [90, 180, 60],  [200, 150, 45]]
];
thickness = 2;
t_step = 0.05;

g_pts = [for(i = [0:len(ctrl_pts) - 1])
bezier_curve(t_step, ctrl_pts[i])
];

function_grapher(g_pts, thickness);

The surface looks smooth in the x direction; however, it's not smooth in the y direction because they use only four points separately.

# Using Bézier to create Bézier

What will happen if the control points in the y direction were generated by the bezier_curve function? We can use the points of current Bézier curves. For example, we take the 5th point at the path of each curve:

After that, we can use these four points to create a Bézier curve, right? If we go from the first to the last points at each curve, we will have the following (green) curves.

ctrl_pts = [
[[0, 0, 20],  [60, 0, -35],   [90, 0, 60],    [200, 0, 5]],
[[0, 50, 30], [100, 60, -25], [120, 50, 120], [200, 50, 5]],
[[0, 100, 0], [60, 120, 35],  [90, 100, 60],  [200, 100, 45]],
[[0, 150, 0], [60, 150, -35], [90, 180, 60],  [200, 150, 45]]
];

thickness = 2;
t_step = 0.05;

// Four bazier curves
g_pts = [for(i = [0:len(ctrl_pts) - 1])
bezier_curve(t_step, ctrl_pts[i])
];
for(i = [0:len(g_pts) - 1]) {
polyline3d(g_pts[i], thickness);
}

// The curve created by the 5th point at the path of each curve.
crtl_j = 5;
color("red") union() {
polyline3d([for(i = [0:len(g_pts) - 1]) g_pts[i][crtl_j]], thickness);
for(i = [0:3]) {
translate(g_pts[i][5]) sphere(r = thickness * 2);
}
}

// Using Bézier to create Bézier
for(j = [0:len(g_pts[0]) - 1]) {
ctrl_pts2 = [for(i = [0:len(g_pts) - 1]) g_pts[i][j]];
bezier_pts = bezier_curve(t_step, ctrl_pts2);
color(j == crtl_j ? "blue" : "green") polyline3d(bezier_pts, thickness);

}

The green curves look smooth in both x and y directions. The curves are not what we really want. Let's pass these points into the function_grapher module.

ctrl_pts = [
[[0, 0, 20],  [60, 0, -35],   [90, 0, 60],    [200, 0, 5]],
[[0, 50, 30], [100, 60, -25], [120, 50, 120], [200, 50, 5]],
[[0, 100, 0], [60, 120, 35],  [90, 100, 60],  [200, 100, 45]],
[[0, 150, 0], [60, 150, -35], [90, 180, 60],  [200, 150, 45]]
];

thickness = 2;
t_step = 0.05;

bezier_pts = [for(i = [0:len(ctrl_pts) - 1])
bezier_curve(t_step, ctrl_pts[i])
];

g_pts = [for(j = [0:len(bezier_pts[0]) - 1])
bezier_curve(t_step,
[for(i = [0:len(bezier_pts) - 1]) bezier_pts[i][j]]
)
];

function_grapher(g_pts, thickness);

We may try to encapsulate these details. My bezier_surface function is a demonstration. Now that, we can create a Bézier surface easily and clearly.

t_step = 0.05;
thickness = 0.5;

ctrl_pts = [
[[0, 0, 20],  [60, 0, -35],   [90, 0, 60],    [200, 0, 5]],
[[0, 50, 30], [100, 60, -25], [120, 50, 120], [200, 50, 5]],
[[0, 100, 0], [60, 120, 35],  [90, 100, 60],  [200, 100, 45]],
[[0, 150, 0], [60, 150, -35], [90, 180, 60],  [200, 150, 45]]
];

g = bezier_surface(t_step, ctrl_pts);
function_grapher(g, thickness);

When things get complex, it's better to encapsulate some details. Creating a Bézier surface is just an example. Building some fundamental functions and modules will help. That's why I created the dotSCAD. It contains some thoughts while I designed these things and makes me think more about what a better design is.

Of course, it's my library and my thoughts. You can make your own one!