OpenSCAD implementation of bezier curves of any degrees

Recently, I saw a Youtube video where someone implemented bezier curves for 3 and 4 degrees, and I thought it would be nice if there would be a version with any degrees. I didn’t find something like that, so I implemented it myself. First, you need a definition of a line (which is surprisingly not a standard datatype in OpenSCAD):

module line(p1,p2,w) {
    hull() {
        translate(p1) circle(r=w);
        translate(p2) circle(r=w);
    }
}
module polyline(points, index, w) {
    if(index < len(points)) {
        line(points[index - 1], points[index],w);
        polyline(points, index + 1, w);
    }
}

For creating a bezier curve I had to define a few functions. I’m wondering why OpenSCAD has such a weak function definition, where you basically only can define one statement and not a whole block like in almost every other language. However, I wrote a recursive implementation of sampling one point at the bezier curve and call this function several times to build an array of points.

function choose(n, k)=
     k == 0? 1
    : (n * choose(n - 1, k - 1)) / k;

function _point_on_bezier_rec(points,t,i,c)=
    len(points) == i ? c
    : _point_on_bezier_rec(points,t,i+1,c+choose(len(points)-1,i) * pow(t,i) * pow(1-t,len(points)-i-1) * points[i]);

function _point_on_bezier(points,t)=
    _point_on_bezier_rec(points,t,0,[0,0]);

//a bezier curve with any number of control points
//parameters: 
//points - the control points of the bezier curve (number of points is variable)
//resolution - the sampling resolution of the bezier curve (number of returned points)
//returns:
//resolution number of samples on the bezier curve
function bezier(points,resolution)=[
for (t =[0:1.0/resolution:1+1.0/(resolution/2)]) _point_on_bezier(points,t)
];

The first parameter is just an array of 2D-points, defining the control points. Note, that you can define the resolution of the sampling via the second parameter.

In the following example, I created a simple vase with 6 control points. The whole resolution can be defined as always with the $fn global variable. I recommend using a small value while debugging because it is significantly faster. I just set the number of control points of the bezier curve to the same value. With the variables radius, height and strength, I defined the size of the vase. The control points p0 to p5 are relative to these variables to allow a variable size. I used a cylinder for the base plate because it is difficult to define a really flat bezier curve, which can be printed nicely. Or one could use boolean combination to cut the base flat. However, by calling the bezier function, create a polyline with the result points and extrude this with a rotate_extrude, one gets a pretty nice result.

resolution = 100;    
$fn = resolution;
    
radius = 20;
height = 90;
strength = 1;

p0 = [radius,0];
p1 = [radius*3,height*0.2];
p2 = [-radius,height*0.4];
p3 = [radius*3,height*0.7];
p4 = [0,height*0.8];
p5 = [radius*0.8,height*1];


translate([0,0,-strength]) cylinder(r=radius+strength,h=strength*2);
rotate_extrude()
polyline(bezier([p0,p1,p2,p3,p4,p5],resolution),1,strength);

This could be also combined into one function:

//create a 3D rotational model with a bezier curve of given points, resolution and thickness
module bezier_model(points,resolution,thickness) {
    translate([0,0,thickness/2]) rotate_extrude() polyline(bezier(points,resolution),1,thickness/2);
}

 

Result image:

A vase created by extruding a bezier curve in OpenSCAD
A vase created by extruding a bezier curve in OpenSCAD

Thanks for reading and also thanks for the useful comments, I updated the source code on this post!

5 Replies to “OpenSCAD implementation of bezier curves of any degrees”

  1. Thanks for this. Clever use of openscad functions 🙂
    Definitely really useful.

    Couple of suggested simple changes:
    – Making the minimum number of faces to the number of ‘end points’ – 1.
    – Stop point being created which extends beyond the end point provided.

    I did this as follows:
    18 – function bezier(points,resolution)=[
    19 – for (t =[0:1.0/resolution:1+1.0/resolution]) _point_on_bezier(points,t)

    18 + function bezier(points)=[
    19 + for (t =[0:1.0/max($fn, len(points)-1):1]) _point_on_bezier(points,t)

    Thanks again.

  2. Hello, thank’s for this code. I hope, you would not mind, I have added your code to my small 2d shape generation library for OpenSCAD here: https://www.thingiverse.com/thing:2930492 and here https://github.com/sadr0b0t/2d_points

    with some additions, fixes and minor refactoring.

    meaningful fixes:

    1) In function _point_on_bezier_rec added exit condition “len(points) == undef”
    function _point_on_bezier_rec(points, t, i, c)=
    len(points) == i || len(points) == undef ? c :

    Without this fix OpenSCAD would hang when pass some incorrect value instead of array of control points.

    2) Changed parameter array generation line
    for (t =[0:1.0/fn:1+(1/fn)]) _point_on_bezier(points, t)
    changed to
    for (t =[0:1.0/fn:1+(1/fn/2)]) _point_on_bezier(points, t)

    without this fix the last point on the curve will not match to the last control point with some resolution parameter values (for example, with resolution=100).

    this looks like onenscad bug:
    if you generate list with step=0.1: [0:0.1:1],
    you will get [0, 0.1, 0.2, 0.3 … 1]
    but if you generate list with step=0.01 [0:0.01:1],
    you will get [0, 0.01, 0.02, 0.03 … 0.99] (no 1 at the end)
    so add small delta which is less than step: delta=1/fn/2

Leave a Reply

Your email address will not be published. Required fields are marked *