Passing BezierFunction to ArcLength?

I have a cubic Bezier curve I’ve generated:

pts = RandomReal[1,{4,2}];
f = BezierFunction[pts];

For a particular value of t, f will return a 2D vector:

In[21]:= f[0.5]
Out[21]= {0.584105,0.651064}

However, if I pass f by itself to ArcLength, it fails to evaluate:

ArcLength[f[t], {t,0,1}] //N
NIntegrate: Integrand ... is not numerical at {t}={0.00795732}

The same general procedure works fine with other vector-valued functions I’ve defined myself:

In[30]:= g[t_]:={t,t}
In[31]:= ArcLength[g[t], {t,0,1}]
Out[31]= ???SqrtBox["2"]?

What’s the correct way to do this?

I don’t know of “an easy way”. Mathematica has made a ton of things like this just too easy (like finding volume of a shape). But this one yet, right?

You can easily type in the Bezier polynomial (find on Mathworld or Wiki) and pass that function to arclength - which will certainly work. Only problem is there are many Bezier curve function “styles”. which one?

In fact this appears to be strange: The value t = 0.007957319952578756 in the error message does not even depend on the choice and number of the Bezier points.

If you are basically interested in the value of length, and less in the syntax, you can get a result e.g. like so:

bezf = DiscretizeGraphics[ParametricPlot[f[t], {t, 0, 1}]];
ArcLength[bezf]

Thank you, Henrik! While it’s disheartening to have what I hoped would be a sophisticated numerical integration method reduced to simple linearization, this at least works without resorting to manual specification of polynomials. What good are high-level primitives like BezierFunction if they must be discarded so easily when used with other parts of the language?

Hello Nicholas,

it is fun to search on the internet for the number ‘‘0.007957312’’ -
one will get interesting hits (is this the mysterious “Wolfram constant”?). It mostly has to do with NIntegrate.

Well, I think, the problem comes like so :

Here we see, what the system is trying to calculate.
The BezierFunction under the Sqrt[] is the derivative of the above f (its “velocity”) -
still a vector! For a “regular function” the expression of an arc element is in fact
Sqrt[1 + func'[t]^2], but of course, this cannot work here!

A clean solution - probably more the way you like it - could therefore simply be:

NIntegrate[Norm[f'[t]], {t, 0, 1}]

Regards – Henrik

I also find that BezierFunction does not play well with other functions, such as FunctionExpand and PiecewiseExpand. I have made my own replacement:

BezierSymbolicFunction[pts_?MatrixQ] := 
  Apply[Function, 
   List@Sum[
     pts[[i + 1]] BernsteinBasis[Length[pts] - 1, i, #], {i, 0, 
      Length[pts] - 1}]];

With this I get ArcLength without trouble:

pts = RandomReal[1, {4, 2}];
f = BezierSymbolicFunction[pts];
ArcLength[f[t], {t, 0, 1}]

Thank you, Gianluca,

now I understand the concept of BernsteinBasis and the like!

pts = CirclePoints[5];

(* according to Gianluca Gorni: *)
bezierSymbolicFunction[pts_List] := 
  Sum[pts[[i]] BernsteinBasis[Length[pts] - 1, i - 1, #], {i, 1, Length[pts]}] &;
(* now the same, but with 'Table' instead of 'Sum' *)
bernBas = Table[pts[[i]] BernsteinBasis[Length[pts] - 1, i - 1, #], {i, 1, Length[pts]}] &;

bezsf = bezierSymbolicFunction[pts];

Manipulate[
 Show[ParametricPlot[bezsf[t], {t, 0, end}, PlotRange -> {-1, 1.1}], 
  Graphics[{MapThread[Arrow[{#1, #2}] &, {ConstantArray[{0, 0}, Length[pts]], 
      bernBas[end]}], {Red, PointSize[.03], Point[pts]}}]], {end, 0.01, 1}]

Bernstein1.gif

This is so much better! Clearly replacing ArcLength is the easier route to take.