Message Boards Message Boards

GROUPS:

How to get a BezierFunction to give the same curve as a BezierCurve?

Posted 4 years ago
2158 Views
|
10 Replies
|
12 Total Likes
|

BezierCurve is a graphics primitive, BezierFunction creates a function. How can I use the later to get the same curve as with the former? Motivation: I need to discretize such a curve, as imported from a PDF document. Please see here for (much) more information about my actual problem:

Here's what I tried. How should I use BezierFunction to get the same thing as BezierCurve? Unfortunately I don't know much about Bezier curves.

pt = {{85.6699`, 270.639`}, {81.4849`, 
    265.53000000000003`}, {72.1939`, 247.082`}, {69.5059`, 
    244.27`}, {66.8189`, 241.46`}, {65.39789999999999`, 
    237.92700000000002`}, {64.1759`, 236.649`}, {62.9539`, 
    235.372`}, {75.0969`, 229.142`}, {76.6069`, 
    228.67600000000002`}, {78.11789999999999`, 228.21`}, {75.1319`, 
    234.644`}, {75.2469`, 237.147`}, {75.3609`, 239.65`}, {80.5859`, 
    252.02`}, {82.9949`, 256.076`}, {85.4049`, 
    260.13100000000003`}, {92.1679`, 270.779`}, {93.5919`, 
    274.19`}, {95.0159`, 277.6`}, {92.9719`, 279.555`}, {85.6699`, 
    270.639`}};

ParametricPlot[BezierFunction[pt][x], {x, 0, 1}, 
 Epilog -> {BezierCurve[pt]}]

10 Replies

Thanks to help from Shutao Tang, I have a solution. BezierFunction[pt] is the equivalent of BezierCurve[pt, SplineDegree -> Length[pt]-1]. The default SplineDegree is 3. To achieve the same result as produced by a BezierCurve, we need to stitch together several BezierFunctions created from 4 (or less) points, like so:

funs = BezierFunction /@ Partition[pt, 4, 3, {1,1}, {}];
Show[Table[ParametricPlot[f[x], {x, 0, 1}], {f, funs}], PlotRange -> All]
Posted 4 years ago

I think Bezier Curve by default creates a curve by combining several curves with SplineDegree 3. So, you can get your two plots to match by setting the SplineDegree of the BezierCurve to the number of control points.

But that would be getting your BezierCurve to match your BezierFunction, and you were asking for the other way. In that case, you'd have to replicate BezierCurve's default behavior. You'd need to create sever BezierFunctions by taking your control points 3 at a time. If you wanted to have one single function with parameter running from 0 to 1, you'd have to scale each individual BezierFunction to the appropriate part of the parameter's domain and compose them all together.

I think you need to use the BernstainBasis, something similar to:

pts = {{1, 0}, {1, 2}, {2, -1}, {3, 2}};
f[t_] := Sum[pts[[i + 1]] BernsteinBasis[3, i, t], {i, 0, 3}]
ParametricPlot[f[t], {t, 0, 1}, Frame -> True, PlotRange -> All, Prolog -> {Thickness[0.01], Dashed, CapForm[None], Red, BezierCurve[pts, SplineDegree -> 3]}, PlotStyle -> Black]

Depending on your knots expanding to more points might be tricky...

enter image description here

Doesn't this do exactly the same thing as BezierFunction? I mean, it is still necessary to stitch together multiple degree-3 parts with this method too.

Correct. But then you have the real formula, not a 'bezier-object'.

Or do it yourself, the construction is quite easy:

enter image description here

Clear[Smoothed, CreateGraphics]
Smoothed[pts_List, \[Alpha]_?
   NumericQ] := {1 - \[Alpha], \[Alpha]}.# & /@ Partition[pts, 2, 1]
CreateGraphics[pts_List, \[Alpha]_] := 
 Module[{sublines, smooth, \[Theta], gr},
  sublines = NestList[Smoothed[#, \[Alpha]] &, pts, Length[pts] - 2];
  smooth = Nest[Smoothed[#, \[Theta]] &, pts, Length[pts] - 1];
  sublines = 
   Riffle[Line /@ sublines, Hue /@ Range[0, 1, 1/(Length[pts] - 1)]];
  gr = ParametricPlot[smooth, {\[Theta], 0, 1}];
  Show[{Graphics[{sublines, PointSize[Large], 
      Point[smooth /. \[Theta] -> \[Alpha]]}], gr}, 
   PlotRange -> {{-2, 3}, {-1, 2}}]
  ]

Manipulate[
 CreateGraphics[
  pt, \[Alpha]], {{pt, {{-1, 0}, {-0.8, 1.6}, {0.8, 1.8}, {1.8, 
     1.2}, {2, 0}, {1.4, -0.8}}}, Locator, 
  LocatorAutoCreate -> {3, 8}}, {{\[Alpha], 0.3}, 0, 1}]

giving:

enter image description here

What do you think about the discrepancy between BezierCurve and its DiscretizeGraphics version (this is where the question came from)?

Consider

pt = {{93.2759`, 277.0452`}, {90.6249`, 273.3252`}, {79.7499`, 
    255.70020000000002`}, {76.9999`, 250.70020000000002`}, {74.2499`, 
    245.70020000000002`}, {70.2499`, 237.70020000000002`}, {69.9999`, 
    235.32520000000002`}};

g = Graphics[{BezierCurve[pt]}];
Show[DiscretizeGraphics[g], g]

The discretized version doesn't quite match up. There is a small but consistent difference between the curves. Also, the bottom endpoint doesn't match. Bug?

enter image description here

Maybe a bug, maybe some accuracy thing (though I tried AccuracyGoal, PrecisionGoal, MaxCellMeasure et cetera to improve but to no avail).

pt = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {2, 0}};
g = Graphics[{BezierCurve[pt]}];
Show[DiscretizeGraphics[g], g]

enter image description here

Maybe also something to do with the default SplineDegree....

Based on your example: This is a bug, no doubt. DiscretizeGraphics makes the exact same mistake I made. It simply uses BezierFunction with all the points form the BezierCurve, ending up with a single high degree function instead of a combination of several degree-3 ones.

pt = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {2, 0}};
g = Graphics[{BezierCurve[pt]}];
Show[g, ParametricPlot[BezierFunction[pt][x], {x, 0, 1}, 
  PlotStyle -> Orange], DiscretizeGraphics[g]]

enter image description here

Indeed! Good hunting! Now, it would be nice is there was a @mathematicabugreport account. So you could just submit this by mentioning it....

Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract