8

我有两点之间的贝塞尔曲线。我想将所有曲线切成两个相等的一半。我的一个想法是,如果我可以控制“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>
4

3 回答 3

26

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

更新

De Casteljau 比看起来更简单。对于非数学家来说,那篇 WP 文章可能更清楚。所以我会更简单地解释。

假设您有一个由点 A、B、C 和 D 定义的贝塞尔曲线。其中 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 进行。

希望这可以帮助。

于 2013-09-08T07:05:17.920 回答
14

Paul LeBeau 对这个问题的回答对我很有帮助。我认为,如果我在下面提供了与该答案相关的视觉效果,那么其他人会受益更多。

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

在此处输入图像描述

于 2016-04-05T18:54:49.933 回答
9

这是将贝塞尔曲线分成两条曲线的公式。

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();
于 2014-11-09T17:47:12.410 回答