Get a point inside bezier curve

后端 未结 1 429
深忆病人
深忆病人 2021-01-29 05:05

I\' using KineticJS to drawing a logo, both top an bottom lines are bezierCurveTo.

I need to draw lines between them and so I need to locate the points inside both curv

相关标签:
1条回答
  • 2021-01-29 05:20

    Given an X coordinate: How to get the Y coordinate of 2 vertically stacked bezier curves.

    I can think of 2 ways, both use “brute force”.

    First method: examine pixels:

    • Draw both your beziers on a separate canvas.
    • Use context.getImageData to get an array of all the vertical pixels on the canvas at coordinate X.
    • Iterate through each vertical Y pixel at your desired X coordinate
    • If you find a non-transparent pixel, you've hit the Bezier (and it's Y)
    • Iterate from top to bottom until you find the top bezier Y.
    • Iterate from bottom to top until you find the bottom Bezier Y.

    enter image description here

    Here is code and Fiddle for the first method: http://jsfiddle.net/m1erickson/uRDYf/

    <!doctype html>
    <html>
    <head>
    <link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
    <script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
    
    <style>
        body{ background-color: ivory; }
        #canvas{border:1px solid red;}
    </style>
    
    <script>
    $(function(){
    
        var canvas=document.getElementById("canvas");
        var ctx=canvas.getContext("2d");
    
        // draw a top bezier
        ctx.beginPath();
        ctx.moveTo(50,50);
        ctx.bezierCurveTo(125,0,150,100,250,75);
        ctx.lineWidth=3;
        ctx.strokeStyle="black";
        ctx.stroke();
    
        // draw a bottom bezier
        ctx.beginPath();
        ctx.moveTo(50,150);
        ctx.bezierCurveTo(125,0,150,100,250,175);
        ctx.lineWidth=3;
        ctx.strokeStyle="blue";
        ctx.stroke();
    
        // get an array of all the pixels in the canvas
        var x=100;  // put your X coordinate value here
        var iData = ctx.getImageData(x,0,1,canvas.height);
        var data = iData.data;
        var w=canvas.width;
        var h=canvas.height;
        var theY1=-999;  // your top result
        var theY2=-999;  // your bottom result
    
    
        // iterate through each Y at your vertical X coordinate
        // Examine the opacity value at the XY
        // if the pixel is not transparent, you have found your Y
        for(var y=0; y<h; y++) {
            if(data[y*4+3]>10){
                theY1=y;
                break;
          }
        }
    
        // now iterate backwards to get the Y of the bottom curve
        for(var y=0; y<h; y++) {
            if(data[(h-y)*4+3]>10){
                theY2=(h-y);
                break;
          }
        }
    
    
        // testing -- display the results
    
        ctx.beginPath();
        ctx.moveTo(x,0);
        ctx.lineTo(x,h);
        ctx.strokeStyle="lightgray";
        ctx.stroke();
    
        ctx.beginPath();
        ctx.arc(x,theY1,4,Math.PI*2,false);
        ctx.closePath();
        ctx.arc(x,theY2,4,Math.PI*2,false);
        ctx.closePath();
        ctx.fillStyle="red";
        ctx.fill();
    
    }); // end $(function(){});
    </script>
    
    </head>
    
    <body>
        <canvas id="canvas" width=300 height=300></canvas>
    </body>
    </html>
    

    Second method: use the Bezier curve formula to repeatedly "guess" the Y coordinate.

    FYI, the cubic Bezier actually does have a formula

    // where ABCD are the control points and T is an interval along that curve
    
    function CubicN(T, a,b,c,d) {
        var t2 = T*T;
        var t3 = t2*T;
        return a + (-a * 3 + T * (3 * a - a * T)) * T
        + (3 * b + T * (-6 * b + b * 3 * T)) * T
        + (c * 3 - c * 3 * T) * t2
        + d * t3;
    }
    

    And you can calculate XY points along that formula like this:

    // cubic bezier T is 0-1
    // When T==0.00, you are at the beginning of the Curve
    // When T==1.00, you are at the ending of the Curve
    function getCubicBezierXYatT(startPt,controlPt1,controlPt2,endPt,T){
        var x=CubicN(T,startPt.x,controlPt1.x,controlPt2.x,endPt.x);
        var y=CubicN(T,startPt.y,controlPt1.y,controlPt2.y,endPt.y);
        return({x:x,y:y});
    }
    

    So the second method is to repeatedly "guess" T values along your curve using getCubicBezierXYatT.

    When the returned X is your desired X, you also have your desired Y.

    I haven't tried it, but this SO post uses something called the Newton-Raphson refinement to make better than random guesses:

    Getting y from x co-ord for cubic bezier curve, fast Newton-Raphson method

    0 讨论(0)
提交回复
热议问题