B-Spline Curves

Our B-spline curves are made up with cubic B-spline functions based on the integer knots.  First we construct cubic B-splines.

Cubic B-splines

Let B(x)=(Subscript[B, 3])(x) denote the cubic B-spline that has support set on [0, 4], which is defined inductively by
         (Subscript[B, 0])(x) = 1, if x in [0,1], and (Subscript[B, 0])(x) = 0 if x is outside [0,1],
         (Subscript[B, 1])(x) =  x (Subscript[B, 0])(x) + (2- x) (Subscript[B, 0])(x -1),  and    (Subscript[B, 2])(x) = (x/2) (Subscript[B, 2])(x) + ((3-x)/2) (Subscript[B, 2])(x -1),  and finally
        B(x) = (x/3) (Subscript[B, 2])(x) + ((4-x)/3) (Subscript[B, 2])(x -1). 
Other B-splines based on the integer knots are just translations of B(x). For example, the cubic B-spline that has the support set [n, n+4] is  B(x - n).

B0[x_] = If[ 0<= x && x <1, 1,0];   
   B1[x_] = If[0 <= x  && x <2, x B0[x]+ (2-x) B0[x-1], 0];  
   B2[x_] = If[0 <= x  && x <3, x B1[x]/2+(3-x) B1[x-1]/2, 0];  
   B3[x_] = If[0 <= x  && x <4, x B2[x]/3+(4-x) B2[x-1]/3, 0];

Plot[{B3[x-1],B3[x],B3[x+1], B3[x+2], B3[x+3]}, {x, -3,5}, AspectRatio->0.5]

Cubic B-spline curves (first attempt)

In order to use B-splines to draw a curve, we consider B-splines with non-zero support on [0,1] with the knots (Subscript[t, i]) = (i/n), i= 0,1,... n.  The B-splines based on these knots are then B(n(t- (Subscript[t, i]))) = B(nt - i). 

Given a sequence of "control points" XY = {((Subscript[x, 0]), (Subscript[y, 0])), ((Subscript[x, 1]), (Subscript[y, 1])) ..., ((Subscript[x, n]), (Subscript[y, n]))}, the simplest way to construct a B-spline curve is using 
                 B(t) =  (∑_(i=1)^n) XY[i]B3[nt-i+2]                           
Note that B(t) is actually a vector. We call its x component BX(t) and its y component BY(t). Then B(t) = (BX(t), BY(t)) gives the parametric form of the curve.

Algorithm

Bspline[XY0_,t_] := Block[{n,XY},
XY = XY0; n = Length[XY]; 
B[t]= Sum[XY[[i]]B3[n*t-i+2],{i,n}];
Return[B[t]]; ];

To use the algorithm, all it needs is to have a given set of data XY in the form of  XY ={((Subscript[x, 0]), (Subscript[y, 0])), ((Subscript[x, 1]), (Subscript[y, 1])) ..., ((Subscript[x, n]), (Subscript[y, n]))}, then run   
            Bspline[XY, t]  
 The two components of the spline are given by 
            BX[t_] = B[t][[1]];  BY[t_] = B[t][[2]];

Example

Example 1.

XY = {{0.,0.2},{1.,0.},{2.,0.2}, {3.,1.},{4.,1.3},{5.,1.3}, {6.,1.}, {5.,0.8}};  
B[t_] = Bspline[XY,t]; 
BX[t_] = B[t][[1]]; 
BY[t_] = B[t][[2]];

dots = ListPlot[XY,PlotStyle->{PointSize[0.02]},DisplayFunction->Identity];
gr = ParametricPlot[{BX[t],BY[t]},{t,0,1},AspectRatio ->0.5, DisplayFunction->Identity];
Show[gr,Graphics[Line[XY]],dots,DisplayFunction->$DisplayFunction, Axes->False]

Example 2. Script D:

XY = {{-1,4},{-0.8,0},{-1.5,-6},{-1.8,-5}, {-1.5,-3},{-0.5,-5},{0,-4},{0.7,2}, {0,7},{-1,8},{-1.5,2},{0,-8}};  
B[t_] = Bspline[XY,t]; 
BX[t_] = B[t][[1]]; 
BY[t_] = B[t][[2]];

curve=ParametricPlot[{BX[t],BY[t]},{t,0,1}, Axes->False,AspectRatio ->1]

nodes=ListPlot[XY,Frame->False,Axes->False, PlotStyle->PointSize[.02],DisplayFunction->Identity]; 
Show[curve,nodes,Graphics[Line[XY]], 
DisplayFunction->$DisplayFunction]

Discussion

Note that these curve have problems at the end points. This happens because the sum (∑_(i=1)^n) B3[nt-i+2] = 1 does not hold near the end point. To overcove this problem, we need to introduct boundary splines in the following.

Cubic B-spline with support in [0,1]

We need all B-splines that do not vanish on the interval [0,1]. There are, however, several spline functions that do not vanish on [0,1] but their support sets are not entirely in [0,1]. To keep all support inside, we make up several boundary B-spine pieces that uses repeated knots. These are denoted as C[x] in the following. For example C3[x] is based on the knots 0, 0, 1, 2, 3.

C3[x_] = (1-x)^3 B0[x];
C2[x_] = x(1-x)(2-3x/2) B0[x]+(2-x)^2 B1[x]/4;
C1[x_] = x^2(1-x)B0[x]/2 + x(2-x)B1[x]/4 + (3-x)B2[x]/3;

Plot[{C1[x],C2[x], C3[x], B3[x], C1[4-x],C2[4-x], C3[4-x]}, {x, 0,4}]

Cubic B-spline curves

Given a sequence of "control points" XY = {((Subscript[x, 0]), (Subscript[y, 0])), ((Subscript[x, 1]), (Subscript[y, 1])) ..., ((Subscript[x, n]), (Subscript[y, n]))}, the B-spline curve is given by  
                 B(t) =XY[1]C3[n t]+XY[2] C2[n t]+ XY[3] C1[nt] + (∑_(i=1)^(n-3)) XY[i+3]B3[nt-i+1]
                           + XY[n+1]C1[n-nt]+XY[n+2]C2[n-nt]+ XY[n+3]C3[n-nt]
Note that B(t) is actually a vector. We call its x component BX(t) and its y component BY(t). Then B(t) = (BX(t), BY(t)) gives the parametric form of the curve.

Algorithm

Bspline[XY0_,t_] := Block[{n,XY},
XY = XY0; n = Length[XY]-3; 
B[t]= XY[[1]]C3[n*t]+XY[[2]]C2[n*t]+ XY[[3]]C1[n*t]+
 Sum[XY[[i+3]]B3[n*t-i+1],{i,n-3}]+ XY[[n+1]]C1[n-n*t]+XY[[n+2]]*C2[n-n*t]+ XY[[n+3]]C3[n-n*t];
Return[B[t]]; ];

To use the algorithm, all it needs is to have a given set of data XY in the form of  XY ={((Subscript[x, 0]), (Subscript[y, 0])), ((Subscript[x, 1]), (Subscript[y, 1])) ..., ((Subscript[x, n]), (Subscript[y, n]))}, then run   
            Bspline[XY, t]  
 The two components of the spline are given by 
            BX[t_] = B[t][[1]];  BY[t_] = B[t][[2]];

Example

Example 1.

XY = {{0.,0.2},{1.,0.},{2.,0.2}, {3.,1.},{4.,1.3},{5.,1.3}, {6.,1.}, {5.,0.8}};  
B[t_] = Bspline[XY,t]; 
BX[t_] = B[t][[1]]; 
BY[t_] = B[t][[2]];

dots = ListPlot[XY,PlotStyle->{PointSize[0.02]},DisplayFunction->Identity];
gr = ParametricPlot[{BX[t],BY[t]},{t,0,1},AspectRatio ->0.5, DisplayFunction->Identity];
Show[gr,Graphics[Line[XY]],dots,DisplayFunction->$DisplayFunction, Axes->False]

Example 2. Script D:

XY = {{-1,4},{-0.8,0},{-1.5,-6},{-1.8,-5}, {-1.5,-3},{-0.5,-5},{0,-4},{0.7,2}, {0,7},{-1,8},{-1.5,2},{0,-8}};  
B[t_] = Bspline[XY,t]; 
BX[t_] = B[t][[1]]; 
BY[t_] = B[t][[2]];

curve=ParametricPlot[{BX[t],BY[t]},{t,0,1}, Axes->False,AspectRatio ->1]

nodes=ListPlot[XY,Frame->False,Axes->False, PlotStyle->PointSize[.02],DisplayFunction->Identity]; 
Show[curve,nodes,Graphics[Line[XY]], 
DisplayFunction->$DisplayFunction]

Example 3.  Car: .

XY = {{0.,0.}, {1.2,1.2}, {8.,1.2},{10.,4.},{15.,4},{20.,4.},{22.,2},{27.5,2.},{28.,0}};  
B[t_] = Bspline[XY,t]; 
BX[t_] = B[t][[1]]; 
BY[t_] = B[t][[2]];

curve=ParametricPlot[{BX[t],BY[t]},{t,0,1}, Axes->False,AspectRatio->0.3]

nodes=ListPlot[XY,Frame->False,Axes->False, AspectRatio->0.3,PlotStyle->
PointSize[.02], DisplayFunction->Identity]; 
Show[curve,nodes, Graphics[Line[XY]], DisplayFunction->$DisplayFunction]

Example 4.  Duck head:

XY = {{4,-0.5},{3.6,0},{3.25,0},{3.2,0.1}, {3.25,0.2},{3.6,0.3},{3.75,1.2},{4.1,1.3}, {4.5,0},{4.,-0.5}};  
B[t_] = Bspline[XY,t]; 
BX[t_] = B[t][[1]]; 
BY[t_] = B[t][[2]];

curve=ParametricPlot[{BX[t],BY[t]},{t,0,1}, Axes->False,AspectRatio ->1]

dots = ListPlot[XY,PlotStyle->{PointSize[0.02]},DisplayFunction->Identity];
gr = ParametricPlot[{BX[t],BY[t]},{t,0,1},DisplayFunction->Identity];
Show[gr,Graphics[Line[XY]],dots,DisplayFunction->$DisplayFunction, Axes->False]

Discussion

In the last example, what we have is a closed curve for which ((Subscript[x, 0]), (Subscript[y, 0])) = ((Subscript[x, n]), (Subscript[y, n])). The curve shows a sharp point at bottom. For closed curve, we should use only B[nt - j], but use them for the data {((Subscript[x, 0]), (Subscript[y, 0])), ((Subscript[x, 1]), (Subscript[y, 1])), ..., ((Subscript[x, n]), (Subscript[y, n])),((Subscript[x, 1]), (Subscript[y, 1])), ((Subscript[x, 2]), (Subscript[y, 2])), ((Subscript[x, 3]), (Subscript[y, 3]))}.

Closed cubic B-spline curves:

Given a sequence of "control points" XY = {((Subscript[x, 0]), (Subscript[y, 0])), ((Subscript[x, 1]), (Subscript[y, 1])) ..., ((Subscript[x, n]), (Subscript[y, n]))} such that ((Subscript[x, 0]), (Subscript[y, 0])) = ((Subscript[x, n]), (Subscript[y, n])), the B-spline curve is given by  
          B(t) = XY[n-2]B3[nt+1]+XY[n-1]B3[nt+2]+XY[n]B3[nt+3]+(∑_(i=1)^n) XY[i+3]B3[nt-i+1]+XY[1]B3[nt-n+1]+XY[2]B3[nt-n]+XY[3]B3[nt-n-1]                       
Again B(t) is a vector. We call its x component BX(t) and its y component BY(t). Then B(t) = (BX(t), BY(t)) gives the parametric form of the curve.

Algorithm

Bspline[XY0_,t_] := Block[{n,XY},
     XY = XY0; n = Length[XY]; 
     B[t]=XY[n-2]B3[n*t+1]XY[n-1]B3[n*t+2]+XY[n]B3[n*t+3]+ Sum[XY[[i]]B3[n*t-i+2],{i,1,n}]+XY[1]B3[n*t-n+1]+XY[2]B3[n*t-n]+XY[3]B3[n*t-n-1];
     Return[B[t]]; ];

To use the algorithm, all it needs is to have a given set of data XY in the form of  XY ={((Subscript[x, 0]), (Subscript[y, 0])), ((Subscript[x, 1]), (Subscript[y, 1])) ..., ((Subscript[x, n]), (Subscript[y, n]))}, then run   
            Bspline[XY, t]  
 The two components of the spline are given by 
            BX[t_] = B[t][[1]];  BY[t_] = B[t][[2]];

Example

Example 1.  A circle: .

n=10;
XY=Table[{Cos[ 2 k Pi/n], Sin[2 k Pi/n]}, {k,0,n + 3}];B[t_] = Bspline[XY,t]; 
BX[t_] = B[t][[1]]; 
BY[t_] = B[t][[2]];

curve=ParametricPlot[{BX[t],BY[t]},{t,-0.1,1.1}, Axes->False,AspectRatio ->1]

dots = ListPlot[XY,PlotStyle->{PointSize[0.02]},DisplayFunction->Identity];
gr = ParametricPlot[{BX[t],BY[t]},{t,0,1},DisplayFunction->Identity];
Show[gr,Graphics[Line[XY]],dots,DisplayFunction->$DisplayFunction, Axes->False]

Example 2.  Duck head: .

XY = {{4,-0.5},{3.6,0},{3.25,0},{3.2,0.1}, {3.25,0.2},{3.6,0.3},{3.75,1.2},{4.1,1.3}, {4.5,0},{4.,-0.5},{3.6,0},{3.25,0}};  
B[t_] = Bspline[XY,t]; 
BX[t_] = B[t][[1]]; 
BY[t_] = B[t][[2]];

curve=ParametricPlot[{BX[t],BY[t]},{t,0,1}, Axes->False,AspectRatio ->1]