将贝塞尔曲线分成两半相等

问题描述 投票:6回答:3

我有2点之间的bezier曲线。我想将所有曲线切成两个相等的一半。我的一个想法是,如果我可以控制't'值,我将通过t = [0,0.5]和t = [0.5,1]绘制2条曲线,但我不知道如何。以下是我的代码。我不介意任何其他想法或建议

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>D3 test</title>
    <script src="http://d3js.org/d3.v3.min.js"></script>

    <script>
    var Over = function(){
        d3.select(this)
        .style("stroke-opacity", 0.25);
    }
    var Out = function(){
        d3.select(this)
        .transition().duration(200)
        .style("stroke-opacity", 0);
    }

    function curve(n,x1,y1,x2,y2){

        var xr = (x1+x2)/2,
            yr = (y1+y2)/2,
            euDist = Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2)),
            x3 = -y1+xr+yr, x4 = -y2+xr+yr,
            y3 =  x1+yr-xr, y4 =  x2+yr-xr,
            ctrl , curveDescription;

        svg.append('path')
            .attr("stroke", 'blue')
            .attr('fill','none')
            .style("stroke-opacity",0.25)
            .attr('d', 'M'+x3+','+y3+'L'+x4+','+y4)
            .attr('stroke-width',strokeWidth);

        for(var j=0;j<=n;j++){
            ctrl = [(x4-x3)*j/n+x3 , (y4-y3)*j/n+y3] ,                  
            curveDescription=   
                    'M' +x1+','     +y1+ 
                    'Q' +ctrl[0]+','+ctrl[1]+','
                        +x2+','     +y2;

            svg.append('path')
                .attr("stroke", 'blue')
                .attr('fill','none')
                .style("stroke-opacity",0.25)
                .attr('d', curveDescription)
                .attr('stroke-width',strokeWidth);  

            svg.append('path')
                .attr("stroke", 'blue')
                .attr('fill','none')
                .style("stroke-opacity",0)
                .on("mouseover", Over)
                .on("mouseout", Out)
                .attr('d', curveDescription)
                .attr('stroke-width',strokeWidth*25);

        }

    }
    </script>

</head>

<body>
    <script>
    var w = 1268 , h = 680 , strokeWidth = 2;

    var svg = d3.select("body")
                .append("svg")
                .attr("width", w)
                .attr("height", h)

    curve(5, 100,100, 400,500);


    </script>
</body>
</html>
javascript svg linear-algebra bezier
3个回答
20
投票

将贝塞尔曲线分成两条曲线非常简单。查看De Casteljau的算法。 https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm

更新

De Casteljau比它看起来更简单。对于非mathermeticians而言,WP文章可能更清楚。所以我会更简单地解释一下。

想象一下,你有一个由A,B,C和D点定义的bezier。其中A和D是端点,B和C是控制点。

所以,假设你想在曲线的't'点找到曲线的值(其中t在0..1范围内。你可以通过几何体这样做:

  1. 找到沿直线AB在't'处的点E.
  2. 找到沿直线BC的't'处的点F.
  3. 找到沿直线CD在't'处的点G.
  4. 找到沿直线EF在't'处的点H.
  5. 找到沿直线FG位于't'的点J.
  6. 最后,找到沿直线HJ位于't'的点K.

K也等于贝塞尔曲线上的't'点。这是De Casteljau的算法。

但有用的是,它还为我们提供了两个贝塞尔曲线的控制点,如果曲线在点K处分开将导致这两个贝塞尔曲线的控制点。两个贝塞尔曲线是:A,E,H,K和K,J,G,D。

在你的情况下t = 0.5,所以找到两条曲线只是一系列的加法和除以2。

  E = (A+B)/2
  F = (B+C)/2
  G = (C+D)/2
  H = (E+F)/2
  J = (F+G)/2
  K = (H+J)/2

显然,这些计算必须针对x和y进行。

希望这可以帮助。


9
投票

The answer to this question given by Paul LeBeau对我很有帮助。我认为如果有一个视觉效果可以与其他人一起使用,其他人会受益更多,我在下面提供。

下图说明了Paul LeBeau的答案中描述的要点。请参阅相关说明的答案。实际图表显示了t = 0.5的特殊情况,但是对于0和1之间的任何t值,一般原理是相同的。粗黑线显示原始曲线的“控制线”,而红线显示原始曲线的“控制线”。第一个“半曲线”。

enter image description here


6
投票

这是一个将贝塞尔曲线分割为两条曲线的公式。

var w = 800, h = 560;

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var pts = [{x:20, y:20},
           {x:20, y:100},
           {x:200, y:100},
           {x:200,  y:20}];
var t = 0.5;

function lerp(a, b, t)
{
    var s = 1 - t;
    return {x:a.x*s + b.x*t,
            y:a.y*s + b.y*t};
}


function splitcurve()
{
    var p0 = pts[0], p1 = pts[1], p2 = pts[2], p3 = pts[3];
    var p4 = lerp(p0, p1, t);
    var p5 = lerp(p1, p2, t);
    var p6 = lerp(p2, p3, t);
    var p7 = lerp(p4, p5, t);
    var p8 = lerp(p5, p6, t);
    var p9 = lerp(p7, p8, t);

    var firsthalf = [p0, p4, p7, p9];
    var secondhalf =  [p9, p8, p6, p3];

    console.log(firsthalf);
    console.log(secondhalf);

    ctx.beginPath();
    ctx.moveTo(20,20);
    ctx.lineWidth=5;
    ctx.bezierCurveTo(20,100,200,100,200,20);
    ctx.strokeStyle="black";
    ctx.stroke(); 

    ctx.beginPath();
    ctx.moveTo(p0.x,p0.y);
    ctx.lineWidth=5;
    ctx.bezierCurveTo(p4.x,p4.y,p7.x,p7.y,p9.x,p9.y);
    ctx.strokeStyle="blue";
    ctx.stroke(); 

    ctx.beginPath();
    ctx.moveTo(p9.x,p9.y);
    ctx.lineWidth=5;
    ctx.bezierCurveTo(p8.x,p8.y,p6.x,p6.y,p3.x,p3.y);
    ctx.strokeStyle="red";
    ctx.stroke(); 
}

splitcurve();
© www.soinside.com 2019 - 2024. All rights reserved.