1 | initial version |
Create a list of the control points:
LP=[vector([var("x_{}".format(u), domain="real"),
var("y_{}".format(u),domain="real")])
for u in (0..3)]
LP
[(x_0, y_0), (x_1, y_1), (x_2, y_2), (x_3, y_3)]
Utility function to express the Bernstein polynomials:
var("t")
Bn(p,j,t)=binomial(p,j)*t^j*(1-t)^(p-j)
Bn
(p, j, t) |--> t^j*(-t + 1)^(-j + p)*binomial(p, j)
We use it to get the parametric equation of the Bezier curve:
Bz=sum(map(lambda u,v:u*v, [Bn(3,w,t) for w in (0..3)], LP))
list(Bz)
[-(t - 1)^3*x_0 + 3*(t - 1)^2*t*x_1 - 3*(t - 1)*t^2*x_2 + t^3*x_3,
-(t - 1)^3*y_0 + 3*(t - 1)^2*t*y_1 - 3*(t - 1)*t^2*y_2 + t^3*y_3]
and its derivative:
dBz=Bz.diff(t)
list(dBz)
(-3*(t - 1)^2*x_0 + 3*(t - 1)^2*x_1 + 6*(t - 1)*t*x_1 - 6*(t - 1)*t*x_2 - 3*t^2*x_2 + 3*t^2*x_3,
-3*(t - 1)^2*y_0 + 3*(t - 1)^2*y_1 + 6*(t - 1)*t*y_1 - 6*(t - 1)*t*y_2 - 3*t^2*y_2 + 3*t^2*y_3)
Let d0 and d1 be the slopes of the function at begin and end points, and use them to define the equations constraining the equality of slopes:
var("d0, d1")
dB0=dBz(t=0)
E0=d0==dB0[1]/dB0[0]
dB1=dBz(t=1)
E1=d1==dB1[1]/dB1[0]
[E0,E1]
(d0, d1)
[d0 == (y_0 - y_1)/(x_0 - x_1), d1 == (y_2 - y_3)/(x_2 - x_3)]
These equations, along with the (implicit) equality of points (satisfied by keeping $(x_0, y_0)$ and $(x_3, y_3)$ as such), define the set of Bezier curves satisfying our constraints:
Sol=solve([E0, E1], [x_1, x_2, y_1, y_2])
print("len(Sol)={}".format(len(Sol)))
Sol[0]
len(Sol)=1
[x_1 == r8,
x_2 == r7,
y_1 == d0*r8 - d0*x_0 + y_0,
y_2 == d1*r7 - d1*x_3 + y_3]
This means that any Bezier curve:
starting at $P_0=(x_0, y_0)$,
ending at $P_3=(x_3, y_3)$,
having $P_1$ somewhere on the line passing at $P_0$ with slope $d_0$, and
having $P_2$ somewhere on the line passing at $P_3$ with slope $d_1$
is a curve approximating (in some undefined sense) our original function and satisfying our constraints.
In fact, we have a double infinity of solutions. Choosing $x_1$ and $x_2$ can (should) therefore be done to satisfy further constraints, for example minimizing the mean square distance between the original function and the Bezier curve, and possibly other constraints (e. g. keeping the relation between $x$ and $t$ monotonic on $[x_0~x_3]\mapsto[0~1]$, i. e. no loop or cusp in the curve...).
One can also note that these Bezier curves cannot have a linear mapping of $t$ to $x$ (one can check that this implies $P_0=P_1=P_2$).
HTH,