首页 文章

通过3个点绘制2条贝塞尔曲线,以模拟圆弧

提问于
浏览
1

我有三个不在同一条线上的点,最初我想通过这三个点绘制一个圆弧,我做了 . 但实际上镀铬并没有画出真正的圆形,而是使用几条贝塞尔曲线来假装它是圆形,因为贝塞尔曲线很便宜 . 如果铬作为一个中间人这样做,为什么我不是自己绘制圆形贝塞尔(两个bezier,从点1到中点,中点到点3)?这将更清洁,更便宜(2与浏览器决定的未知数量的贝塞尔曲线相比) . 那是我被困的地方,怎么样? “控制点”应该在哪里?

这是我在javascript中的旧绘制弧函数

drawArc = function(startPoint, thirdPoint, endPoint){
var ctx = this.ctx;
ctx.lineWidth = this.strokeWidth;
ctx.strokeStyle = this.strokeColor;

var centerObject = circleCenter( new Point(startPoint.x, startPoint.y), 
                                 new Point(thirdPoint.x, thirdPoint.y), 
                                 new Point(endPoint.x, endPoint.y) );
var centerX = centerObject.x;
var centerY = centerObject.y;
var r = centerObject.r

var angle = Math.atan2(centerX-startPoint.x, centerY-startPoint.y);
// console.log(centerObject);
if (!angle){
    ctx.beginPath();
    ctx.moveTo(startPoint.x, startPoint.y);
    ctx.lineTo(endPoint.x, endPoint.y);
} else {
    if( angle > Math.PI/2) {
        ctx.beginPath();
        ctx.arc(centerX, centerY, r, Math.PI * 1.5-angle, Math.PI * 1.5 + angle, true);
    } else {
        ctx.beginPath();
        ctx.arc(centerX, centerY, r, Math.PI * 1.5-angle, Math.PI * 1.5 + angle, false);
    }
}
ctx.globalCompositeOperation = "source-over";
ctx.stroke();

}
var circleCenter = function(startPoint, thirdPoint, endPoint){
var dy1 = thirdPoint.y - startPoint.y;
var dx1 = thirdPoint.x - startPoint.x;
var dy2 = endPoint.y - thirdPoint.y;
var dx2 = endPoint.x - thirdPoint.x;

var aSlope = dy1/dx1;
var bSlope = dy2/dx2;  


var centerX = (aSlope*bSlope*(startPoint.y - endPoint.y) + bSlope*(startPoint.x + thirdPoint.x)
    - aSlope*(thirdPoint.x+endPoint.x) )/( 2* (bSlope-aSlope) );
var centerY = -1*(centerX - (startPoint.x+thirdPoint.x)/2)/aSlope +  (startPoint.y+thirdPoint.y)/2;
var r = dist(centerX, centerY, startPoint.x, startPoint.y)

return {
    x: centerX,
    y: centerY,
    r: r
};
}

任何人都可以帮我重写drawArc函数使用canvas bezierCurveTo()方法而不是arc()?

我的代码示例在这里:http://codepen.io/wentin/pen/VYegqq

2 回答

  • 1

    您可以使用4个立方贝塞尔曲线近似圆...但它不是一个完美的圆;-)

    enter image description here

    示例代码和演示:

    您可以使用半径和常量 c 与起点,终点和控制点之间的关系来计算您自己想要的控制点 . 此Bezier近似圆围绕原点[0,0]绘制,因此您将转换为特定圆的中心点 .

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");
    var cw=canvas.width;
    var ch=canvas.height;
    
    ctx.lineWidth=3;
    
    // refined from 0.551915024494 thanks to Pomax
    var c=0.5522847498307933984022516322796 ;  
    
    var cx=150; // center x
    var cy=150; // center y
    var r=100;  // radius
    
    drawBezierCircle(cx,cy,r);
    
    function drawBezierCircle(cx,cy,r){
      
        ctx.translate(cx,cy); // translate to centerpoint
    
        ctx.beginPath();
    
        ctx.moveTo(0,-r);
        ctx.bezierCurveTo(
          c*r,-r, 
          r,-c*r, 
          r,0
        );
        ctx.strokeStyle='red';
        ctx.stroke();
    
        ctx.beginPath();
        ctx.moveTo(r,0);
        ctx.bezierCurveTo(
          r,c*r, 
          c*r,r, 
          0,r
        );
        ctx.strokeStyle='green';
        ctx.stroke();
    
        ctx.beginPath();
        ctx.moveTo(0,r);
        ctx.bezierCurveTo(
          -c*r,r, 
          -r,c*r, 
          -r,0
        );
        ctx.strokeStyle='blue';
        ctx.stroke();
    
        ctx.beginPath();
        ctx.moveTo(-r,0);
        ctx.bezierCurveTo(
          -r,-c*r, 
          -c*r,-r, 
          0,-r
        );
        ctx.strokeStyle='gold';
        ctx.stroke();
    }
    
    body{ background-color: ivory; padding:10px; }
    #canvas{border:1px solid red;}
    
    <canvas id="canvas" width=300 height=300></canvas>
    
  • 3

    您可以使用以下方法查找三次贝塞尔曲线的控制点,以近似圆弧, endpoints 为P0,P1,半径为R,角度范围为A:

    将控制点表示为Q0,Q1,Q2和Q3,然后

    Q0 = P0,
    Q3 = P1,
    Q1 = P0 L * T0
    Q2 = P1-L * T1

    其中T0和T1是P0和P1处的圆弧的单位切向量,L =(4/3)* tan(A / 4) .

    请注意,随着角 Span A变大,近似误差将增大 . 因此,如果由3个点定义的圆弧具有相对较小的角度范围,您甚至可以使用单个三次贝塞尔曲线以高精度逼近它 . 同样,如果你总是使用两条贝塞尔曲线(每两个点之间有一条曲线)来近似圆弧,那么你最终可能会得到一个不那么好的近似 . 如果Chrome使用多条贝塞尔曲线绘制圆弧,精度可能是贝塞尔曲线数量不是固定值的原因 .

相关问题