【原创】《矩阵的史诗级玩法》连载二十九:二次贝塞尔曲线和椭圆的矩阵求交计算代码

本篇的代码跟连载二十七中的大同小异,不同的是把其中一条贝塞尔曲线换成椭圆,然后矩阵的生成也变得直截了当,都基于已知条件。总的来说,比二次贝塞尔曲线的还要简单。

PS:Canvas未提供现成的绘制椭圆方法(chrome有一个但其他浏览器还木有),我百度了一个基于正圆+缩放的方法并加入其中。

<!DOCTYPE html>
<html>
<head>
<title>矩阵法计算二次贝塞尔曲线和椭圆的交点</title>
<script src="Matrix.js"></script>
<script src="MatrixUtil.js"></script>
<script src="Point.js"></script>
<script src="FormulaUtil.js"></script>
<script src="ComplexNum.js"></script>
<script src="AngleUtil.js"></script>
<script src="ComplexConsts.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 p00 = new Point(-1, -1);
	var p01 = new Point(2, 2);
	var p02 = new Point(1, -0.5);
	
	//椭圆中心
	var ellipseCenter = new Point(0.5, -0.5);
	//长轴半径
	var aLength = 1.5;
	//短轴半径
	var bLength = 1;
	//旋转角度
	var rotation = Math.PI / 12;
	
	var matrix = new Matrix();
	
	//把椭圆变换逐个加入
	//缩放
	MatrixUtil.scale(matrix, aLength, bLength);
	//旋转
	MatrixUtil.rotate(matrix, rotation);
	//平移
	MatrixUtil.translate(matrix, ellipseCenter.x, ellipseCenter.y);
	
	var matrixInvert = matrix.clone();
	matrixInvert.invert();
	
	console.log(matrixInvert.a, matrixInvert.b, matrixInvert.c, matrixInvert.d, matrixInvert.tx, matrixInvert.ty)
	
	//用椭圆逆矩阵变换贝塞尔曲线的3个点
	var transformedP00 = matrixInvert.transformPoint(p00);
	var transformedP01 = matrixInvert.transformPoint(p01);
	var transformedP02 = matrixInvert.transformPoint(p02);
	
	//贝塞尔曲线x分量的3个系数(可在连载十七中找到这公式)
	var ax = transformedP00.x - 2 * transformedP01.x + transformedP02.x;
	var bx = 2 * transformedP01.x - 2 * transformedP00.x;
	var cx = transformedP00.x;
	
	//贝塞尔曲线y分量的3个系数(可在连载十七中找到这公式)
	var ay = transformedP00.y - 2 * transformedP01.y + transformedP02.y;
	var by = 2 * transformedP01.y - 2 * transformedP00.y;
	var cy = transformedP00.y;
	
	//由X^2+Y^2=1和X=ax*t^2+bx*t+cx和Y=ay*t^2+by*t+cy,得到
	//(ax*t^2+bx*t+cx)^2+(ay*t^2+by*t+cy)^2=1
	//展开化简将获得一个四次方程,系数如下
	var fourFormulaA = ax * ax + ay * ay;
	var fourFormulaB = 2 * ax * bx + 2 * ay * by;
	var fourFormulaC = 2 * ax * cx + bx * bx + 2 * ay * cy + by * by;
	var fourFormulaD = 2 * bx * cx + 2 * by * cy;
	var fourFormulaE = cx * cx + cy * cy - 1;
	
	//用四次求根的类进行求解
	var resolutions = FormulaUtil.calcFourFormulaZero(fourFormulaA, fourFormulaB, fourFormulaC, fourFormulaD, fourFormulaE);
	var realResolutions = FormulaUtil.getRealSolutions(resolutions);
	
	var intersections = [];
	
	for(var i = 0, len = realResolutions.length; i < len; i ++)
	{		
		var t = realResolutions[i];
		if(t <= 1)
		{
			//算出在基向量矩阵逆变换后的交点
			var p = new Point();
			p.x = ax * t * t + bx * t + cx;
			p.y = ay * t * t + by * t + cy;
			//第二条曲线在基向量逆矩阵变换后为Y=X^2,x的取值范围为-1到1
			//因为这个交点是方程在基向量逆变换后求得,所以要执行基向量矩阵的原始变换才能让结果恢复到变换前
			intersections.push(matrix.transformPoint(p));
		}		
	}
	
	var screenP00 = toScreen(p00);
	var screenP01 = toScreen(p01);
	var screenP02 = toScreen(p02);	
	
	var canvas = document.getElementById("canvas");
	var context = canvas.getContext("2d");
	context.lineWidth = 0.5;
	context.strokeStyle = "#0000cc";
	
	//绘制第一条贝塞尔曲线
	context.beginPath();
	context.moveTo(screenP00.x, screenP00.y);
	context.quadraticCurveTo(screenP01.x, screenP01.y, screenP02.x, screenP02.y);
	context.stroke();
	context.closePath();	
	
	//绘制椭圆(百度回来的,用绘制正圆+缩放实现)
	context.save();
	var screenCenter = toScreen(ellipseCenter);
    context.translate(screenCenter.x, screenCenter.y);
    context.rotate(-rotation);
    context.scale(aLength * unitSize, bLength * unitSize);
	context.lineWidth = 0.5 / unitSize;
	context.strokeStyle = "#0000cc";
	context.beginPath();
    context.arc(0,0,1,0,Math.PI*2,true);
	context.stroke();
	context.closePath();
    context.restore();
	
	context.fillStyle = "#cc0000";
	
	//绘制交点
	for(var i = 0, len = intersections.length; i < len; i ++)
	{	
		var toScreenP = toScreen(intersections[i]);
		context.beginPath();
		context.arc(toScreenP.x, toScreenP.y, 5, 0, Math.PI * 2, true);
		context.fill();
		context.closePath();
	}	
	
</script>
</body>
</html>

运行结果如下图所示。

我们又用了贝塞尔曲线的转换点方法来绕开复杂的方程变形,不难看出,贝塞尔曲线真的很好用。要不是我现在所处行业的数控机床仅支持圆弧的话,我还真的不想拿圆弧作为数据源。但我不会有任何意见,毕竟圆弧求交只需解一元二次方程,性能还是比贝塞尔曲线高很多了,只是一些场景下的需求没法做而已。

上篇我们提到,以贝塞尔曲线作为基准,让椭圆的形状适应它会比较困难,所以,如果两条曲线都是椭圆,那这时候椭圆的矩阵变换就真的逃不掉了。

嗯,下一篇我们就迎难而上,把椭圆的变形问题给解决掉吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值