Design an algorithm to check if point lies on a Bézier curve

2.2k Views Asked by At

I'm trying to create an algorithm that could tell if a point lies on a Bézier curve (or very close to it) analytically.

I know that I can check if that's the case by solving $t$ for some ${x,y}$:

$x = a_x(1-t)^3 + 3b_xt(1-t)^2 + 3c_xt^2(1-t)+d_xt^3$

$y = a_y(1-t)^3 + 3b_yt(1-t)^2 + 3c_yt^2(1-t)+d_yt^3$

and if it has solutions in range $[0,1]$, then the point belongs to a Bézier.

$a_x, a_y, ... d_x, d_y $ are Bézier's start point, controls points and end point.

I know how to do that on a piece of paper if I substitute $a_x, a_y, ... d_x, d_y $ with some values, but I have no idea how to create an algorithm for doing that with my computer (mainly because I'm not able to combine polynomial terms since they are connected to $a_x, a_y, ... d_x, d_y $). Could someone please point me to a technique that can be used in such cases or to some source code that does what I want to achieve?

3

There are 3 best solutions below

8
On BEST ANSWER

You have to eliminate ("Elimination" is an important keyword) variable $t$ in order to turn your parametric representation into a cartesian implicit equation $f(x,y)=0$, because it is easy to test if $f(x,y)=0$ or, more realisticaly, $ -\varepsilon < f(x,y) < + \varepsilon$ for some small $\varepsilon$.

How to do that ? The answer, adapted to computer programs, is : by using Sylvester's resultant. I refer you to the recent answer I made here in a similar case.

In order to simplify a little the computations, let us assume that the initial point ($t=0$) is the origin (it is always possible to do that), expanding formulas for $x$ and $y$ as polynomials in $t$ :

$$\begin{cases}U_xt^3+V_xt^2+W_xt-x=0\\ U_yt^3+V_yt^2+W_yt-y=0\end{cases}$$

$$\begin{cases}U_x&:=&3b_x-3c_x+d_x\\V_x&:=&3c_x-6b_x\\ W_x&:=& 3b_x\end{cases} \ \ \text{and} \ \ \begin{cases}U_y&:=&3b_y-3c_y+d_y\\V_y&:=&3c_y-6b_y\\ W_y&:=& 3b_y\end{cases}$$

one gets the following $6 \times 6$ "determinantal" expression for $f$:

$$f(x,y)=\left|\begin{array}{rrrrrrrr} U_x&V_x&W_x&-x&0&0\\ 0&U_x&V_x&W_x&-x&0\\ 0&0&U_x&V_x&W_x&-x\\ U_y&V_y&W_y&-y&0&0 \\ 0&U_y&V_y&W_y&-y&0\\ 0&0&U_y&V_y&W_y&-y \end{array}\right|=0.$$

You can leave it under this form if you use a software like Matlab. But if you have to do the operation $\approx10^9$ times, you need to expand it once for all (using a CAS).

4
On

See my answer to this question.

Elimination and implicitization are the key ideas, as @Jean Marie points out.

The comments mention these notes by Tom Sederberg. In chapter 17 of those notes, you will find some very simple techniques for "inversion" of polynomial and rational curves, based on the use of resultants. Specifically, on page 204, you will find a formula for doing "inversion" of a cubic curve written in Bezier form, and there's a nice worked example on page 205. This is exactly what you need.

Sederberg's approach is somewhat simpler than the Sylvester resultant, and it does not require converting the curve into algebraic form. The conversion occasionally causes numerical problems, though it should be OK with cubic curves.

If the given point (call it $\mathbf{P}$) is not exactly on the curve, you can apply the inversion formula, anyway, and it will give you some value of $t$. I'm not 100% sure what point this value of $t$ represents. I don't think it's the point on the curve that's closest to $\mathbf{P}$, but it should be good enough if $\mathbf{P}$ is already very close to the curve.

0
On

I google similar problem about this and somebody gives a more straight way to check if any point is on the bezier curve.

The background for me is, in my drawing application, I need to use mouse to move a bezier curve, first I need to know if the mouse is over it.

The core logic is divide the bezier curve into smaller pieces, and check the endpoints of them, get the nearest endpoint, then divide its neighbouring pieces to find a nearer endpoint of its sub pieces.

No extra math knowledge required.

The error parameter is relative to the line width of the curve.

function checkPointOnCubicBezierCurve(controlPoints, p, startT = 0, endT = 1, error =1) {
  var t = startT;
  var minDistance = Infinity,
  minDistanceT = null;
  var count = 100;
  var step = (endT - startT) / count;
  
  // condition to quit recursion
  var startP = pointOnCubicBezierCurve(controlPoints, startT); // get start point of the slice of curve
  var endP = pointOnCubicBezierCurve(controlPoints, endT); // get end point of the slice of curve
    
  if(distanceBetweenPoints(p1, p2)<=error){
    return false;
  }
  
  for (var i = 0; i < count; i++) {
    var point = pointOnCubicBezierCurve(controlPoints, t);
    var dst = distanceBetweenPoints(point, p);
    if (dst < minDistance) {
      minDistance = dst;
      minDistanceT = t;
    }
    if (dst <= error) {
      return true;
    }
    t += step;
  }
  return checkPointOnCubicBezierCurve(controlPoints, p, minDistanceT - step,minDistanceT + step, error);
}