我有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>
将贝塞尔曲线分成两条曲线非常简单。查看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范围内。你可以通过几何体这样做:
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进行。
希望这可以帮助。
The answer to this question given by Paul LeBeau对我很有帮助。我认为如果有一个视觉效果可以与其他人一起使用,其他人会受益更多,我在下面提供。
下图说明了Paul LeBeau的答案中描述的要点。请参阅相关说明的答案。实际图表显示了t = 0.5的特殊情况,但是对于0和1之间的任何t值,一般原理是相同的。粗黑线显示原始曲线的“控制线”,而红线显示原始曲线的“控制线”。第一个“半曲线”。
这是一个将贝塞尔曲线分割为两条曲线的公式。
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();