在两点之间绘制平滑曲线 arc()

Posted

技术标签:

【中文标题】在两点之间绘制平滑曲线 arc()【英文标题】:Drawing a smooth curved arc() between two points 【发布时间】:2013-12-19 15:49:21 【问题描述】:

我正在尝试在画布中的两点之间绘制一条平滑的弧线,我已将这些点设置为 sutch note,这些点是动态的并且可以更改。

var p1 = 
    x=100, y=100 


var p2 = 
    x=255, y=255

曲线看起来像这样

这是我开始的代码,我无法理解这个函数的数学/逻辑:

function curveA2B(a,b)

    var mindpoint = 
        x: (a.x+b.x)/2,
        y: (a.y+b.y)/2,
        d: Math.sqrt(Math.pow(b.x-a.x,2) + Math.pow(b.y-a.y,2))
    ;

    context.beginPath();
    context.arc(
        a.x,
        a.y,
        mindpoint.d/2,
        1.5*Math.PI,
        0,
        false
    );

    context.arc(
        b.x,
        b.y,
        mindpoint.d/2,
        1*Math.PI,
        0.5*Math.PI,
        true
    );

    context.context.stroke();


动态例子在这里:http://jsfiddle.net/CezarisLT/JDdjp/6/

【问题讨论】:

how to draw smooth curve through N points using javascript html5 canvas?的可能重复 提示:思考三角形。 提示:想想样条曲线! @DevlshOne:您引用的上一个 SO 答案有一个很好的例子,用样条连接点。样条曲线只是一组单独的二次曲线,它们被设计成看起来像一条穿过给定点集的曲线。 【参考方案1】:

您可以使用两个点的中点作为 x 和 y 轴的两个半径设置。

以下示例已简化,但它显示了一种在框内创建平滑曲线的方法,如您的示例所示。

框将始终缩放,使曲线穿过两点之间的中点(例如更改终点)。

DEMO

/// set up some values
var ctx = demo.getContext('2d'),
    p1 = x:100, y:100,           /// point 1
    p2 = x:355, y:255,           /// point 2
    mx = (p2.x - p1.x) * 0.5,      /// mid-point between point 1 and 2
    my = (p2.y - p1.y) * 0.5,
    c1 = x: p1.x, y: p1.y + my,  /// create center point objects
    c2 = x: p2.x, y: p2.y - my,
    steps = 0.05;                  /// curve resolution

/// mark the points and the boxes which represent the center of those
ctx.fillStyle = '#ff6e6e';
ctx.fillRect(p1.x, p1.y, mx, my);

ctx.fillStyle = '#6e93ff';
ctx.fillRect(p1.x + mx, p1.y + my, mx, my);

然后我们为每个“盒子”渲染四分之一椭圆:

/// render the smooth curves using 1/4 ellipses    
ctx.beginPath();

for(var isFirst = true,            /// first point is moveTo, rest lineTo
        angle = 1.5 * Math.PI,     /// start angle in radians
        goal = 2 * Math.PI,        /// goal angle
        x, y; angle < goal; angle += steps) 

    /// calculate x and y using cos/sin
    x = c1.x + mx * Math.cos(angle);
    y = c1.y + my * Math.sin(angle);

    /// move or draw line
    (isFirst) ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
    isFirst = false;


/// second box    
for(var isFirst = true,
        angle = Math.PI,
        goal = 0.5 * Math.PI,
        x, y;angle > goal; angle -= steps) 

    x = c2.x + mx * Math.cos(angle);
    y = c2.y + my * Math.sin(angle);

    (isFirst) ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
    isFirst = false;

ctx.stroke();

我会留给你把它放到可重用的函数中。希望这会有所帮助!

如果还是不行,我建议你看看我的cardinal spline implementation。

【讨论】:

【参考方案2】:

我创建了一个可以很容易地根据许多需求修改的函数,称为 plot_curve,它可以让您了解问题的分解。

快速演示:http://jsfiddle.net/LVFat/

function plot_curve(x,y,xx,yy, target,color)

    var startX=x;  
    var startY=y;  

    var endX=xx;  
    var endY=yy;  

    var diff_x = xx - x;
    var diff_y = yy - y;


    var bezierX=x;  // x1
    var bezierY=yy; // y2

    console.log("bx:"+bezierX);
    console.log("by:"+bezierY); 

    var cx,cy, t;   

    for(t=0.0; t<=1; t+=0.01)  
      
        cx =  Math.round(  (1-t)*(1-t)*startX + 2*(1-t) * t * bezierX + t*t*endX);  
        cy =  Math.round(  (1-t)*(1-t)*startY + 2*(1-t) * t * bezierY + t*t*endY);  

        // change this part to whatever you are trying to manipulate to the curve
        plot_pixel( Math.round(cx), Math.round(cy), target, color);
      

示例...(与我制作的 divCanvas 函数一起使用...查看 jsfiddle 链接...)

plot_curve(25,25,5,5, ".divCanvas","blue");

如果你只想要两点之间曲线的坐标,试试这个:

function plot_curve(x,y,xx,yy)

    // returns an array of x,y coordinates to graph a perfect curve between 2 points.
    var startX=x;  
    var startY=y;  

    var endX=xx;  
    var endY=yy;  

    var diff_x = xx - x;
    var diff_y = yy - y;

    var xy = [];
    var xy_count = -1;

    var bezierX=x;  // x1
    var bezierY=yy; // y2

    var t;   

    for(t=0.0; t<=1; t+=0.01)  
    
      xy_count++;
      xy[xy_count] = ;
      xy[xy_count].x = Math.round(  (1-t)*(1-t)*startX + 2*(1-t) * t * bezierX + t*t*endX);
      xy[xy_count].y = Math.round(  (1-t)*(1-t)*startY + 2*(1-t) * t * bezierY + t*t*endY);
    

    return xy; // returns array of coordinates

【讨论】:

你能帮我减小曲线的大小吗?我只想要一条轻微的曲线,而不是演示中指定的曲线。 prnt.sc/pxh6w6

以上是关于在两点之间绘制平滑曲线 arc()的主要内容,如果未能解决你的问题,请参考以下文章

Canvas HTML - lineTo 和 Bezier 曲线之间的平滑度

如何使用javascript HTML5画布通过N个点绘制平滑曲线?

在 matplotlib 图中绘制平滑曲线

如何通过 Python 3 中的真实数据点绘制平滑曲线?

python 绘制训练曲线--Savitzky-Golay 滤波平滑处理

python 绘制训练曲线--插值法 曲线平滑处理