不得不说,上篇我把牛逼吹大了。因为直线和二次贝塞尔曲线求交,实际上可以在直线上钻钻空子,计算过程就变得很简单了,不会比矩阵法复杂多少。
然后我们重温一下连载二十一中把换元结果代入到直线方程上的演算过程:
对吧,如果直线方程换成复杂点的,比如二次贝塞尔曲线,那这里的化简也不见得简单。
之所以上篇我会吹牛逼,是因为我当时思维短路了,以为一定要消去t才能继续求解,确实贝塞尔曲线消t特别繁琐。然而真相是,只要把带t的方程代入到直线方程即可。
现在我们把两条线的方程搬过来:
把贝塞尔曲线两条参数方程的右侧代入到直线方程就能得到一个关于t的一元二次方程了。
关键这条方程的化简也容易:
贝塞尔曲线中,t的取值范围为0~1,所以这两个解都可以要,我们分别代入一下,得到第一组解为
同理第二组解为
跟矩阵法得到的解完全一致!而且做法也非常简单粗暴,同时也没有复杂的整理过程,哎呦,那我讲矩阵还有何用?!不过不用急,现在还只是直线+二次曲线,而且只是针对贝塞尔曲线的情况才看不出区别来。所谓真金不怕红炉火,谁能通过最终的考验,谁才是笑到最后的人。
好了,我也顺带给出常规法的求解代码,因为没有太多创新的东西,所以这篇还没写长,可以用代码撑一撑。
<!DOCTYPE html>
<html>
<head>
<title>传统法计算二次贝塞尔曲线和直线的交点</title>
<script src="Matrix.js"></script>
<script src="MatrixUtil.js"></script>
<script src="Point.js"></script>
</head>
<body>
<canvas width="800" height="800" id="canvas"></canvas>
</body>
<script>
function toScreen(p)
{
return new Point(coordO.x + p.x * unitSize, coordO.y - p.y * unitSize);
}
var unitSize = 160;
var coordO = new Point(200, 400);
//贝塞尔曲线的3个点
var p0 = new Point(-1, 0);
var p1 = new Point(1, -1);
var p2 = new Point(2, 2);
//直线上的两个点
var lineP0 = new Point(-2, -2);
var lineP1 = new Point(4, 14 / 5);
//根据两点式求出的直线一般式系数
var lineA = (lineP1.y - lineP0.y);
var lineB = (lineP0.x - lineP1.x);
var lineC = (lineP1.x * lineP0.y - lineP1.y * lineP0.x);
//贝塞尔曲线x分量的3个系数(可在连载十七中找到这公式)
var ax = p0.x - 2 * p1.x + p2.x;
var bx = 2 * p1.x - 2 * p0.x;
var cx = p0.x;
//贝塞尔曲线y分量的3个系数(可在连载十七中找到这公式)
var ay = p0.y - 2 * p1.y + p2.y;
var by = 2 * p1.y - 2 * p0.y;
var cy = p0.y;
//At+Bt^2+C=0
//一元二次方程的3个系数
var squareFormulaA = lineA * ax + lineB * ay;
var squareFormulaB = lineA * bx + lineB * by;
var squareFormulaC = lineA * cx + lineB * cy + lineC;
console.log(squareFormulaA, squareFormulaB, squareFormulaC);
var ts = [];
if(squareFormulaA != 0)
{
//接着求根公式
var delta = squareFormulaB * squareFormulaB - 4 * squareFormulaA * squareFormulaC;
console.log(delta);
//delta小于0时无实数解
if(delta >= 0)
{
var t1 = (- squareFormulaB + Math.sqrt(delta)) / 2 / squareFormulaA;
var t2 = (- squareFormulaB - Math.sqrt(delta)) / 2 / squareFormulaA;
if(t1 >= 0 && t1 <= 1)
{
ts.push(t1);
}
if(t2 >= 0 && t2 <= 1)
{
ts.push(t2);
}
}
}else if(squareFormulaB != 0)
{
var t = - squareFormulaC / squareFormulaB;
if(t >= 0 && t <= 1)
{
ts.push(t);
}
}
var screenP0 = toScreen(p0);
var screenP1 = toScreen(p1);
var screenP2 = toScreen(p2);
var screenLineP0 = toScreen(lineP0);
var screenLineP1 = toScreen(lineP1);
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
context.lineWidth = 0.5;
context.strokeStyle = "#0000cc";
//绘制贝塞尔曲线
context.beginPath();
context.moveTo(screenP0.x, screenP0.y);
context.quadraticCurveTo(screenP1.x, screenP1.y, screenP2.x, screenP2.y);
context.stroke();
context.closePath();
//绘制直线
context.beginPath();
context.moveTo(screenLineP0.x, screenLineP0.y);
context.lineTo(screenLineP1.x, screenLineP1.y);
context.stroke();
context.closePath();
context.fillStyle = "#cc0000";
//绘制交点
for(var i = 0, len = ts.length; i < len; i ++)
{
//把t代入到参数方程中求出xy
var t = ts[i];
var x = ax * t * t + bx * t + cx;
var y = ay * t * t + by * t + cy;
console.log(x, y);
var toScreenP = toScreen(new Point(x, y));
context.beginPath();
context.arc(toScreenP.x, toScreenP.y, 5, 0, Math.PI * 2, true);
context.fill();
context.closePath();
}
</script>
</body>
</html>
代码里面已经给出注释,正文不再讲解,相信大家都能理解。然后,再给个效果图。
在直线和贝塞尔曲线的求交中,矩阵法和传统法不相上下,接下来,我们会尝试更多的曲线,敬请期待!