Find coordinates of equidistant points in Bezier curve
I have to find points (say 10 points) in Bezier curve with 2 control points such that they are at equidistant positions in the curve. Currently I am using the following formula which gives me points but they are not equidistant.
t = ………..from (1/10 to 10/10);
q1 = t * t * t * -1 + t * t * 3 + t * -3 + 1;
q2 = t * t * t * 3 + t * t * -6 + t * 3;
q3 = t * t * t * -3 + t * t * 3;
q4 = t * t * t;
coordinates of a point:
x = q1 * startPointxValue + q2 * controlPoint1xValue + q3 * controlPoint2xValue + q4 * endPointxValue;
y = q1 * startPointyValue + q2 * controlPoint1yValue + q3 * controlPoint2yValue + q4 * endPointyValue;
I tried to use this but due my bad understanding, still unable to correct the values :(. Please help me in finding the values.
attaching sample image of required points (here 11):
Solution 1:
The brute force approach is to just use a polyline approximation. If this is just for graphics, it will certainly be accurate enough. Even for more rigorous applications, it will probably be OK.
Compute a large number of points $n$ along the curve. I'd recommend somewhere in the range $n=50$ to $n=1000$, depending on your accuracy requirements and processor speed. Specifically, let the step-length be $s = 1/n$, and let $t_i = i*s$, and let $P_i = C(t_i)$ be a point on the curve, for $i = 0,1, \ldots , n$. Also, let $c_i = \Vert P_i - P_{i-1} \Vert$ be the $i$-th chord length, and let
$$d_i = \frac{\sum_{j=0}^i c_j}{\sum_{j=0}^n c_j}$$
So, in words, $d_i$ is the fractional distance along the polyline $P_0P_1 \ldots P_n$ at the $i$-th point $P_i$.
Now suppose you want to distribute $m+1$ points $Q_0, \ldots , Q_m$ at equal arclengths along the curve. Here's how you locate the $r$-th point $Q_r$.
Let $d = r/m$. Find $i$ such that $d_{i} \le d \le d_{i+1}$. Then let
$$ u = \frac{d - d_i}{d_{i+1} - d_i} $$
$$ t = t_i + u*s = (i+u)*s$$
$$ Q_r = C(t)$$
Getting all the indexing correct is a chore -- there are lots of opportunities for off-by-one errors. But, other than that, it's straightforward. The basic idea is that distances along the curve can be approximated by distances measured along the polyline $P_0P_1 \ldots P_n$.
You can use your existing code to calculate any point $(x,y) = C(t)$ on the curve.
Here's an implementation in C#:
class Program
{
static void Main()
{
Position endpt0, endpt1, control0, control1;
endpt0 = new Position(100,100); control0 = new Position(200,200);
endpt1 = new Position(100,200); control1 = new Position(400,200);
BezierCurve curve = new BezierCurve(endpt0, control0, control1, endpt1);
// Construct polyline with large number of points
int n = 1000;
double s = 1.0/(n-1);
double[] tt = new double[n];
Position[] PP = new Position[n];
double[] cc = new double[n];
for (int i = 0 ; i < n ; i++)
{
tt[i] = i*s;
PP[i] = curve.Position(tt[i]);
if (i > 0) cc[i] = Distance(PP[i], PP[i-1]);
}
// Get fractional arclengths along polyline
double[] dd = new double[n];
dd[0] = 0;
for (int i = 1 ; i < n ; i++) dd[i] = dd[i-1] + cc[i];
for (int i = 1 ; i < n ; i++) dd[i] = dd[i]/dd[n-1];
// Number of points to place on curve
int m = 10;
double step = 1.0/(m-1);
Position[] QQ = new Position[m];
for (int r = 0 ; r < m ; r++)
{
double d = r*step;
int i = FindIndex(dd, d);
double u = (d - dd[i]) / (dd[i+1] - dd[i]);
double t = (i + u)*s;
QQ[r] = curve.Position(t);
}
}
// Find index i such that dd[i] < d < dd[i+1]
public static int FindIndex(double[] dd, double d)
{
int i = 0;
for (int j = 0 ; j < dd.Length ; j++)
{
if (d > dd[j]) i = j;
}
return i;
}
}