Bezier Curves (Lec 08)

yukita@k.hosei.ac.jp

•Bernstein  Polynomials

Definitions:

BE[n_,k_][x_]=Binomial[n,k] x^k (1-x)^(n-k)

(1 - x)^(-k + n) x^k Binomial[n, k]

Cubic Bernstein polynomials are

Table[ BE[3,k][x] //Expand, {k,0,3}]

{1 - 3 x + 3 x^2 - x^3, 3 x - 6 x^2 + 3 x^3, 3 x^2 - 3 x^3, x^3}

Show[ Table[ Plot[ Evaluate[BE[3,k][x]], {x,0,1},
                   DisplayFunction->Identity ],
             {k,0,3} ],
      DisplayFunction->$DisplayFunction ];

[Graphics:HTMLFiles/index_3.gif]

•Example 1

We will approximate the following data.

data={{0,0}, {1,3}, {3,-2}, {5,5}};

P[t_]=Sum[ data[[i+1]] BE[3,i][t], {i,0,3} ] //Expand

{-t^3 + 3 t^2 + 3 t, 20 t^3 - 24 t^2 + 9 t}

g1=ParametricPlot[ Evaluate[P[t]], {t,0,1},
                   PlotRange->{{0,5},{-2,5}},
                   PlotDivision->60,
                   DisplayFunction->Identity];
g2=Graphics[ Line[data] ];

Show[{g1,g2}, DisplayFunction->$DisplayFunction];

[Graphics:HTMLFiles/index_5.gif]

Mathematica allows  direct embedding of PostScript code in the Graphics functions. The limitation to this feature is the output range. It is confined to the unit square.

Show[Graphics[ {
         PostScript["0.2 0.1 scale"],
         PostScript["0 2 translate"],
         PostScript["0.01 setlinewidth"],
         PostScript["0 0 moveto"],
         PostScript["1 3 3 -2 5 5 curveto stroke"] ,
         PostScript["0 0 moveto"],
         PostScript["1 3 lineto"],
         PostScript["3 -2 lineto"],
         PostScript["5 5 lineto stroke"]},
                Frame->True ]];

[Graphics:HTMLFiles/index_6.gif]

We will show how the position of a control point affects the shape of the Bezier curve.

Clear[data, P]

data[n_]={{0,0}, {1,3 n}, {3,-2}, {5,5}};

P[n_][x_]=Sum[ data[n][[i+1]] *
               BE[3,i][t], {i,0,3} ] //Expand

{-t^3 + 3 t^2 + 3 t, 9 n t^3 + 11 t^3 - 18 n t^2 - 6 t^2 + 9 n t}

Show[ GraphicsArray[
Table[ Show[ ParametricPlot[Evaluate[P[n][t]], {t,0,1},
                            PlotRange->{{0,5},{-2,10}},
                            DisplayFunction->Identity],
             Graphics[ Line[data[n]] ] ],
       {n,0,3} ]
], DisplayFunction->$DisplayFunction ];

[Graphics:HTMLFiles/index_9.gif]

•Example --- Bezier curve of degree 7

We will  find a Bezier   approximation of degree  7  for the following  data  of  8  points.

data={{0,0},{1,3},{3,-2},{5,5},{6,0},{7,3},{9,-1},{10,4}};

P[t_]=Sum[ data[[i+1]] *
           BE[7,i][t], {i,0,7} ] //Expand

{3 t^7 - 28 t^6 + 42 t^5 - 35 t^3 + 21 t^2 + 7 t, 312 t^7 - 1169 t^6 + 1848 t^5 - 1540 t^4 + 700 t^3 - 168 t^2 + 21 t}

g1=ParametricPlot[ Evaluate[P[t]], {t,0,1},
                   DisplayFunction->Identity];
g2=Graphics[ Line[data] ];

Show[{g1,g2}, DisplayFunction->$DisplayFunction,
PlotRange->{{0,10},{-2,5}}];

[Graphics:HTMLFiles/index_11.gif]

It seems that the Bezier curve doesn't produce a nice approximation  for  rapidly oscilating data

•Bezier-Spline Approximation

We will  find a Bezier-Spline Approximation of degree 3  for the following  data of  8  points.

data={{0,0},{1,3},{3,-2},{5,5},{6,0},{7,3},{9,-1},{10,4}};

"cp"'s  stand  for control points.  We  approximate  the data by  three  pieces of Bezier curves of  degree 3.

Do[ cp[0,i]=data[[i+1]], {i,0,2} ];
cp[0,3]=(data[[3]]+data[[4]]) / 2;
cp[1,0]=(data[[3]]+data[[4]]) / 2;
Do[ cp[1,i]=data[[i+3]], {i,1,2} ];
cp[1,3]=(data[[5]]+data[[6]]) / 2;
cp[2,0]=(data[[5]]+data[[6]]) / 2;
Do[ cp[2,i]=data[[i+5]], {i,1,3} ];

Clear[P];
P[i_,t_]=Sum[ cp[i,k] BE[3,k][t], {k,0,3} ]

cp(i, 0) (1 - t)^3 + 3 t cp(i, 1) (1 - t)^2 + 3 t^2 cp(i, 2) (1 - t) + t^3 cp(i, 3)

g[i_]:=ParametricPlot[ Evaluate[P[i,t]], {t,0,1},
                       DisplayFunction->Identity];
gLines=Graphics[ {Thickness[0.002],Line[data]} ];
gJoints=Graphics[ {Thickness[0.002],
                   Dashing[{0.01,0.01}],
                   Circle[cp[1,0],0.5],
                   Circle[cp[2,0],0.5]} ];

Show[{g[0],g[1],g[2],gLines,gJoints},
     DisplayFunction->$DisplayFunction];

[Graphics:HTMLFiles/index_13.gif]

Joining points are indicated by the dashed circles.

•Functional Approach

We do the same task as Example 1.

data={{0,0}, {1,3}, {3,-2}, {5,5}};

•Basis

cubicBezierBasis = Table[BE[3, k][t] // Expand, {k, 0, 3}]

{1 - 3 t + 3 t^2 - t^3, 3 t - 6 t^2 + 3 t^3, 3 t^2 - 3 t^3, t^3}

Control points should be multiplied by the basis functions.

MapThread[Times, {cubicBezierBasis, data}]

{{0, 0}, {3 t - 6 t^2 + 3 t^3, 3 (3 t - 6 t^2 + 3 t^3)}, {3 (3 t^2 - 3 t^3), -2 (3 t^2 - 3 t^3)}, {5 t^3, 5 t^3}}

Summing up these vectors, we obtain the parametric representation of the approximated curve.

P[t_] = Fold[Plus, 0, MapThread[Times, {cubicBezierBasis, data}]] // Expand

{3 t + 3 t^2 - t^3, 9 t - 24 t^2 + 20 t^3}

•Adequate Plot Range

To obtain the maximum values of the x and y coordinates we MapThread the function Max over data.

MapThread[Max, data]

{5, 5}

Likewise, we can obtain the minimum values.

MapThread[Min, data]

{0, -2}

The adequate plot range will be obtained as follows.

Transpose[{MapThread[Min, data], MapThread[Max, data]}]

{{0, 5}, {-2, 5}}

•Exercises

•Problem 1

Write a function cubicBezierPlot that accepts one argument such as
{{0,0},{1,3},{3,-2},{5,5}}
and returns and displays a graphics like below. Use all the building blocks in Section "Functional Approach."

[Graphics:HTMLFiles/index_26.gif]

•Solution

•When is the Evaluate function needed, and why is it if it is?

The following plotting needs no special consideration.

ParametricPlot[{Cos[t], Sin[t]}, {t, 0, 2 Pi}]

[Graphics:HTMLFiles/index_30.gif]

-Graphics -

Clear[f]

The  ParametricPlot function can draw multiple plots in a single coordinate plane when given a list of functions or parametric representations. See the example below.

ParametricPlot[{{Cos[t], Sin[t]}, {2 Cos[t], 1/2 Sin[t]}}, {t, 0, 2 Pi}]

[Graphics:HTMLFiles/index_34.gif]

-Graphics -

ParametricPlot accepts its first argument in a nonstandard way where the argument is passed without evaluation. ParametricPlot  examnines the structure of the first argument  to know whether the caller wants a single plot or multiple plot. Let us consider the following indirection.

g[t_] := {{Cos[t], Sin[t]}, {2 Cos[t], 1/2 Sin[t]}}

Try the following and see it produces errors.

ParametricPlot[g[t], {t, 0, 2 Pi}]

ParametricPlot :: ppcom :  関数 g[t] はコンパイルできません.元の関数を使ってプロット処理を行います.

ParametricPlot :: pptr :  t  =  2.617993877991494`*^-7 における g[t] の評価結果として実数対になりません.

ParametricPlot :: pptr :  t  =  0.25488992540742256` における g[t] の評価結果として実数対になりません.

ParametricPlot :: pptr :  t  =  0.5328694051959509` における g[t] の評価結果として実数対になりません.

General :: stop :  計算中, ParametricPlot :: \" pptr \" のこれ以上の出力は表示されません.

[Graphics:HTMLFiles/index_43.gif]

-Graphics -

If we get g[t] evaluated before pass control to ParametricPlot, then we can avoid the errors. The straightforward way to accomplish this  is to wrap  g[t] with Evaluate. See if the following produces the correct result.

ParametricPlot[Evaluate[g[t]], {t, 0, 2 Pi}]

[Graphics:HTMLFiles/index_46.gif]

-Graphics -


Converted by Mathematica  (June 3, 2003)