How to find out Y coordinate of specific point in

2019-03-11 09:58发布

问题:

I need to find out Y coordinate of specific point of bezier curve in canvas. Do you know, how to find it out? Thank you

回答1:

Using de Casteljau's algorithm you can find the coordinates x and y of a bezier curve for any t, the percentage or interpolation step. So a t of .1 would give you the x and y at 10% of the curve from the beginning. A t of .9 would be 90% from the beginning, and so on.

In our cubic bezier we have p0 (point 0), cp0 (control point 0), cp1 (control point 1), and p1 (point 1).

In the first step of the algorithm we draw a line connecting p0 and cp0, another line connecting cp0 and cp1, and another still connecting cp1 and p1. Then for all 3 of these lines we're going to find the point on them that is t % from the start of them.

I'll call the points as follows:

  • p0 -> cp0 = A
  • cp0 -> cp1 = B
  • cp1 -> p1 = C

    Ax = ( (1 - t) * p0x ) + (t * cp0x);
    Ay = ( (1 - t) * p0y ) + (t * cp0y);
    Bx = ( (1 - t) * cp0x ) + (t * cp1x);
    By = ( (1 - t) * cp0y ) + (t * cp1y);
    Cx = ( (1 - t) * cp1x ) + (t * p1x);
    Cy = ( (1 - t) * cp1y ) + (t * p1y);
    

The second step is very much like the first. In the first we connected the four points with lines and then found 3 new points on them. In this step we'll connect those 3 points with lines find 2 new points on them. I'll call these two new points D and E.

    Dx = ( (1 - t) * Ax ) + (t * Bx);
    Dy = ( (1 - t) * Ay ) + (t * By);

    Ex = ( (1 - t) * Bx ) + (t * Cx);
    Ey = ( (1 - t) * By ) + (t * Cy);

Finally, we can connect these last two points with another line, and find the last point on it which will give us the point on the bezier curve for that t. I'll call this point P.

    Px = ( (1 - t) * Dx ) + (t * Ex);
    Py = ( (1 - t) * Dy ) + (t * Ey);

There we go, we now have the x and y coordinate of a point on the bezier that is t% from the start. I'll add some pictures soon.



回答2:

Cut and paste ready answer:

// Points are objects with x and y properties
// p0: start point
// p1: handle of start point
// p2: handle of end point
// p3: end point
// t: progression along curve 0..1
// returns an object containing x and y values for the given t
BezierCubicXY = function(p0, p1, p2, p3, t) {
    var ret = {};
    var coords = ['x', 'y'];
    var i, k;

    for (i in coords) {
        k = coords[i];
        ret[k] = Math.pow(1 - t, 3) * p0[k] + 3 * Math.pow(1 - t, 2) * t * p1[k] + 3 * (1 - t) * Math.pow(t, 2) * p2[k] + Math.pow(t, 3) * p3[k];
    }

    return ret;
}


回答3:

I've been trying to figure same thing and I think I solved it at least for square bezier curves, which are bezier curves with only a single control point.

Mathematical explanation

The mathematical formula for a square bezier curve is:

Where 'X' is the result, 'A' is the the position of the start point, 'B' the control point and 'C' the end point.

't' is a number between 0 and 1 that indicates which point on the line you want to calculate. 0 represents the start point, 1 the end point, and 0.5 would refer to the center of the curve.

This function is used for calculating both the X and the Y coordinates of a point on the line. If you want to calculate X just fill in the X coordinate of the A,B and C points.

Now in order to determine the Y that belongs to the X we need to determine the 't' for said X coordinate.

We can write the same bezier equation in the quadratic form ():

This allows us to use the quadratic formula to derive the values of 't' that solve the equation. The quadratic formula is actually 2 formulas.

and

The resulting formula is:

and

Code solution

We can describe this in code as:

GetYValues(StartPoint, ControlPoint, EndPoint, X)
{
    Ax = StartPoint.X
    Bx = ControlPoint.X
    Cx = EndPoint.X

    q1 = 2*Ax - 2*Bx;
    q2 = Sqrt(5*Ax*Ax - 10*Ax*Bx + Ax*Cx - Ax*X + 2*Bx*X + 4*Bx*Bx)
    q3 = 2*Ax - 4*Bx + 2*Cx

    t1 = (q1 + q2) / q3
    t2 = (q1 - q2) / q3

    Ay = StartPoint.Y
    By = ControlPoint.Y
    Cy = EndPoint.Y

    Y1 = Ay*(1-t1)*(1-t1) + By*2*(1-t1)*t1 + Cy*t1
    Y2 = Ay*(1-t2)*(1-t2) + By*2*(1-t2)*t2 + Cy*t2

    return [Y1,Y2]
}

Now, I haven't tested this, and the function doesn't check if there are actually any valid points, so there are definitely values for which it will throw an exception. Be sure to check for 'divide by 0' and 'square root of numbers below 0'.

Problems with this solution

A big problem with this equation is that it only works for square bezier curves, while most bezier curves are actually cubic. I've tried to find a similar way to solve the cubic version of this problem, unfortunately this is an order of magnitude more difficult. The only formula I've been able to find for solving cubic equations can be found here Cubic function. This formula contains imaginary numbers and I have no idea what to do with those.

Another problem is that for bezier curves with 4 control points or more, there is no way to solve the equation at all.

Conclusion

In the end, your best bet is to simply convert the bezier curve into straight lines, which are infinitely easier to calculate with.



回答4:

The equation for a circle is: (x-h)^2 + (y-k)^2 = r^2 where h,k are the x,y coordinates for the center. So the equation for y would be y = sqrt(r^2 - (x-h)^2) + k . Most languages come with a Math package, so I assume you can do some sort of Math.sqrt(..) and Math.pow(..)

Anyone correct me if my math is off.



回答5:

@DerekR: Although what you've presented here is clear and probably helpful for many, I think the answer and all the following comments by yourself and immo are not addressing the problem.

(My math stinks and I've been trying to solve the same problem so I may not understand the followup comments.)

I think the question is: Knowing the X,Y coordinates for the two end points and the two control points, and assuming the range is 0 to 1, then given Y, what is Y?

I'm new and can't post images yet but you can see the curve at duck.cc/images/beziercurve_findY.png