the relation of the bezier Curve and ellipse?

前端 未结 5 2183
南笙
南笙 2020-12-18 05:03

I want to draw a oval in html5 canvas,and i found a good method for it in stackoverflow.but I have another quesition.

function drawEllipse(ctx, x, y, w, h) {         


        
相关标签:
5条回答
  • 2020-12-18 05:41

    My background isn't in mathematics so if I'm wrong I'm sure someone will correct me, but from my understanding we can draw a pretty good approximation of an ellipse with just two cubic bezier curves but the coordinates will be a little tricky.

    To answer your question about the relation between the oval point and the control points I think it best be answered by watching this video from the point I've selected if you're familiar with interpolation or from the beginning if you are not. Don't worry it is short.

    One problem we're probably going to run into is that when we start from the top and do a bezierCurveTo the bottom of the ellipse with the corners of the rectangle (of the same width and height) as the control points, the ellipses width is going to be smaller than the rectangle. .75 times the size we want. So we can just scale the control points accordingly.

    Our control point's x would be adjusted like so (assuming width is the width of the ellipse and we're dividing by two to get its offset from the origin)

    var cpx = (width / .75) / 2;
    

    Put together a visualization where you can play with the width and height and see the drawn ellipse.

    The red ellipse is how we wanted it to be drawn, with the inner one how it would be drawn if we didnt reposition the control points. The lines illustrate De Casteljau's algorithm that was shown in the video.

    Here's a screenshot of the visualization enter image description here

    0 讨论(0)
  • 2020-12-18 05:47

    You only need two cubic bezier curves to draw an ellipse. Here's a simplified version of DerekR's code that uses the original function arguments that you provided--assuming you want x and y to be the center of the ellipse:

    jsFiddle

    function drawEllipse(ctx, x, y, w, h) {
        var width_over_2 = w / 2;
        var width_two_thirds = w * 2 / 3;
        var height_over_2 = h / 2;
    
        ctx.beginPath();
        ctx.moveTo(x, y - height_over_2);
        ctx.bezierCurveTo(x + width_two_thirds, y - height_over_2, x + width_two_thirds, y + height_over_2, x, y + height_over_2);
        ctx.bezierCurveTo(x - width_two_thirds, y + height_over_2, x - width_two_thirds, y - height_over_2, x, y -height_over_2);
        ctx.closePath();
        ctx.stroke();
    }
    
    0 讨论(0)
  • 2020-12-18 05:47

    It can be mathematically proven, that circle can not be made with Bézier curve of any degree. You can make "almost circle" by approximating it.

    Say you want to draw a quarter of circle around [0,0]. Cubic bézier coordinates are:

    [0   , 1   ]
    [0.55, 1   ]
    [1   , 0.55]
    [1   , 0   ]
    

    It is a very good approximation. Transform it linearly to get an ellpise.

    0 讨论(0)
  • 2020-12-18 05:54

    Big thanks to BKH. I used his code with two bezier curves to complete my ellipse drawing with any rotation angle. Also, I created an comparison demo between ellipses drawn by bezier curves and native ellipse() function (for now implemented only in Chrome).

    function drawEllipseByBezierCurves(ctx, x, y, radiusX, radiusY, rotationAngle) {
    var width_two_thirds = radiusX * 4 / 3;
    
    var dx1 = Math.sin(rotationAngle) * radiusY;
    var dy1 = Math.cos(rotationAngle) * radiusY;
    var dx2 = Math.cos(rotationAngle) * width_two_thirds;
    var dy2 = Math.sin(rotationAngle) * width_two_thirds;
    
    var topCenterX = x - dx1;
    var topCenterY = y + dy1;
    var topRightX = topCenterX + dx2;
    var topRightY = topCenterY + dy2;
    var topLeftX = topCenterX - dx2;
    var topLeftY = topCenterY - dy2;
    
    var bottomCenterX = x + dx1;
    var bottomCenterY = y - dy1;
    var bottomRightX = bottomCenterX + dx2;
    var bottomRightY = bottomCenterY + dy2;
    var bottomLeftX = bottomCenterX - dx2;
    var bottomLeftY = bottomCenterY - dy2;
    
    ctx.beginPath();
    ctx.moveTo(bottomCenterX, bottomCenterY);
    ctx.bezierCurveTo(bottomRightX, bottomRightY, topRightX, topRightY, topCenterX, topCenterY);
    ctx.bezierCurveTo(topLeftX, topLeftY, bottomLeftX, bottomLeftY, bottomCenterX, bottomCenterY);
    ctx.closePath();
    ctx.stroke();
    

    }

    0 讨论(0)
  • 2020-12-18 06:02

    You will find this explained slightly more math-based in http://pomax.github.io/bezierinfo/#circles_cubic, but the gist is that using a cubic bezier curve for more than a quarter turn is usually not a good idea. Thankfully, using four curves makes finding the required control points rather easy. Start off with a circle, in which case each quarter circle is (1,0)--(1,0.55228)--(0.55228,1)--(0,1) with scaled coordinates for your ellipse. Draw that four times with +/- signs swapped to effect a full circle, scale the dimensions to get your ellipse, and done.

    If you use two curves, the coordinates become (1,0)--(1,4/3)--(-1,4/3)--(-1,0), scaled for your ellipse. It may still look decent enough in your application, it depends a bit on how big your drawing ends up being.

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