Bezier curves are parametric
x(t)=fx(t)
y(t)=fy(t)
Where (x(t),y(t))
is point on Bezier curve, t
is parameter and fx(),fy()
are polynomial functions.
That means Bezier curves are not necessarily functions (can have multiple y
values per single x
). So you need to find all parameters t
for such the x(t)=x0
where x0
is your x
-coordinate.
As @Mike 'Pomax' Kamermans mentions in his answer you can use LUT or compute the roots of fx(t)-x0=0
(x0
moved to the other side of the equation x0 = fx(t)
) polynomial algebraically.
There is also "simpler" option with use of approximation search (similar to binary search but usable also on non monotonic functions) which does not require too high math knowledge (but is slower of coarse). For example something like this C++ code:
double x0,y0; // your point x0 valid y0 not yet
double ax0,ax1,ax2,ax3; // cubic Bezier fx() polynomial coefficients
double ay0,ay1,ay2,ay3; // cubic Bezier fy() polynomial coefficients
double ee,x,t,tt,ttt;
approx aa;
for (aa.init(0.0,1.0,0.025,6,&ee); !aa.done; aa.step()) // search t
{
t = aa.a;
tt = t*t;
ttt = tt*t;
x = ax0 + ax1*t + ax2*tt + ax3*ttt; // compute the x=fx(t)
ee = fabs(x-x0); // compute error of solution for the approximation search
}
// here aa.a holds found `t0`
t = aa.a;
tt = t*t;
ttt = tt*t;
y0 = ay0 + ay1*t + ay2*tt + ay3*ttt; // compute the final y0=fx(t0)
If you need all the possible points then you need to recursively subdivide the search interval t=<0.0,1.0>
to <0.0,t0)
and (t0,1.0>
until there is no new found solution.
[edit1] some more info (requested by Mike 'Pomax' Kamermans
)
As 2D Bezier curves are not necessarily functions there may be more y
values for single x
coordinate. Like on this example:

The numbered blue points are control points of rendered 2D cubic Bezier curve. The yellow ones are found solutions with the recursion described above. Here some C++ example of this:
double x0; // input x coordinate
List<double> y0; // output y coordinates
double ax0,ax1,ax2,ax3; // cubic coefficients
double ay0,ay1,ay2,ay3;
double px0,px1,px2,px3; // control points
double py0,py1,py2,py3;
//---------------------------------------------------------------------------
bool find_point(double t0,double t1,int layer)
{
approx aa;
double ee,x,y,t,tt,ttt,dt;
const double _zero=1e-4;
dt=0.025*(t1-t0); // approximation search step
if (dt<=_zero) return false; // stop if too small interval to search to avoid stack overflows
if (layer>10) return false; // stop if too high recursion layer to avoid stack overflows (this also limits the max found solutions
for (aa.init(t0,t1,dt,6,&ee); !aa.done; aa.step()) // search t
{
t = aa.a;
tt = t*t;
ttt= tt*t;
x = ax0 + ax1*t + ax2*tt + ax3*ttt; // compute the x=fx(t)
ee = fabs(x-x0); // compute error of solution for the approximation search
}
// check the error of found solution
if (aa.e0>_zero) return false;
// here aa.aa holds found `t0`
t = aa.aa;
// check bounds can cross the border a bit
if (t<t0) return false;
if (t>t1) return false;
// add new solution
tt = t*t;
ttt= tt*t;
y = ay0 + ay1*t + ay2*tt + ay3*ttt; // compute the final y0=fx(t0)
y0.add(y); // add to list of solutions
// recursion to check for other solutions by dividing the interval by found solution
if (t0<t-dt) find_point(t0,t-dt,layer+1);
if (t1>t+dt) find_point(t+dt,t1,layer+1);
}
//---------------------------------------------------------------------------
// here usage
void test()
{
// just compute the Bezier cubic polynomials from control points
ax0= ( px0);
ax1= (3.0*px1)-(3.0*px0);
ax2= (3.0*px2)-(6.0*px1)+(3.0*px0);
ax3=( px3)-(3.0*px2)+(3.0*px1)-( px0);
ay0= ( py0);
ay1= (3.0*py1)-(3.0*py0);
ay2= (3.0*py2)-(6.0*py1)+(3.0*py0);
ay3=( py3)-(3.0*py2)+(3.0*py1)-( py0);
// Find the points from mouse x0 coordinate
y0.num=0; // clear found solutions list
find_point(0.0,1.0,0); // recursively found solutions on interval t=<0,1> ,as highest recursion level 0
}
This have a problem with vertical lines because there is infinite number of solutions there ... It will found just few of them instead. Otherwise is this safe to use. (added many checks so it should run smoothly)