bezier cross test

This commit is contained in:
mkwiser 2014-09-06 02:51:19 +08:00
parent 2152c4bf62
commit 16556d4494
3 changed files with 90 additions and 80 deletions

View File

@ -22,33 +22,14 @@ define(
var width = canvas.offsetWidth; var width = canvas.offsetWidth;
var height = canvas.offsetHeight; var height = canvas.offsetHeight;
var points = [ var points = [{"x":132,"y":409},{"x":314,"y":227},{"x":39,"y":356},{"x":181,"y":276},{"x":202,"y":567},{"x":100,"y":122}];
{
x: 130,
y: 77
},
{
x: 130,
y: 148
},
{
x: 65,
y: 148
},
{
x: 330,
y: 77
},
{
x: 130,
y: 148
},
{
x: 165,
y: 148
}
];
// benchmark
console.time('bezier');
for (var i = 0; i < 1000; i++) {
isBezierCross.apply(null, points);
}
console.timeEnd('bezier');
points.forEach(function(p, index) { points.forEach(function(p, index) {
$('[data-index="'+index+'"]').css({ $('[data-index="'+index+'"]').css({
@ -105,13 +86,14 @@ define(
ctx.lineWidth = 1; ctx.lineWidth = 1;
ctx.stroke(); ctx.stroke();
//console.log(JSON.stringify(points));
//console.time('bezier');
var r = isBezierCross.apply(null, points); var r = isBezierCross.apply(null, points);
//console.timeEnd('bezier');
if(r) { if(r) {
ctx.beginPath(); ctx.beginPath();
r.forEach(function(item) { r.forEach(function(item) {
ctx.moveTo(item.x, item.y);
ctx.arc(item.x, item.y, 4, 0, Math.PI * 2, true); ctx.arc(item.x, item.y, 4, 0, Math.PI * 2, true);
}); });
ctx.fill(); ctx.fill();
@ -121,8 +103,6 @@ define(
draw(); draw();
//var points = [{"x":79,"y":156},{"x":314,"y":227},{"x":74,"y":287},{"x":159,"y":116},{"x":32,"y":256},{"x":303,"y":290}];
//console.log(isBezierCross.apply(null, points));
} }
}; };

View File

@ -124,18 +124,23 @@ define(
tmatrix = minus(multi(xmatrix, ymatrix[4]), multi(ymatrix, xmatrix[4])); tmatrix = minus(multi(xmatrix, ymatrix[4]), multi(ymatrix, xmatrix[4]));
tResult = bezierQ2Equation.apply(this, tmatrix); tResult = bezierQ2Equation.apply(this, tmatrix);
} }
// matrix[4] 系数为0, 曲线2退化成垂直线 // xmatrix[4] 系数为0, 曲线2退化成垂直线
else if(xmatrix[4] == 0) { else if(xmatrix[4] == 0) {
tResult = bezierQ2Equation.apply(this, xmatrix); tResult = bezierQ2Equation.apply(this, xmatrix);
} }
else if(ymatrix[4] == 0) { else if(ymatrix[4] == 0) {
tResult = bezierQ2Equation.apply(this, ymatrix); tResult = bezierQ2Equation.apply(this, ymatrix);
// 保证ymatrix可解
tmatrix = xmatrix;
xmatrix = ymatrix;
ymatrix = tmatrix;
} }
} }
// 二次系数有一个为0 // 二次系数有一个为0
else { else {
// 代入法 // 代入法
// 置换矩阵 // 置换矩阵, 保持xmatrix[3]为 0
if (ymatrix[3] == 0) { if (ymatrix[3] == 0) {
tmatrix = xmatrix; tmatrix = xmatrix;
xmatrix = ymatrix; xmatrix = ymatrix;
@ -148,66 +153,79 @@ define(
} }
// 同高次方程1 // 同高次方程1
else { else {
// 求t2的参数方程
tmatrix = multi(xmatrix, 1 / xmatrix[4]); tmatrix = multi(xmatrix, 1 / xmatrix[4]);
// 代入法求四次方程系数 var t4 = multi([
tmatrix = [ Math.pow(tmatrix[0], 2),
ymatrix[3] * Math.pow(tmatrix[0], 2), // 4 2 * tmatrix[0] * tmatrix[1],
2 * ymatrix[3] * tmatrix[0] * tmatrix[1] + ymatrix[4] * tmatrix[0], // 3 2 * tmatrix[0] * tmatrix[2] + Math.pow(tmatrix[1], 2),
ymatrix[3] * (2 * tmatrix[0] * tmatrix[2] + Math.pow(tmatrix[1], 2)) + ymatrix[4] * tmatrix[2] - ymatrix[0], // 2 2 * tmatrix[1] * tmatrix[2],
ymatrix[3] * 2 * tmatrix[1] * tmatrix[2] + ymatrix[4] * tmatrix[2] - ymatrix[1], // 1 Math.pow(tmatrix[2], 2)
ymatrix[3] * tmatrix[2] * tmatrix[2] - ymatrix[2] // 0 ], ymatrix[3]);
];
tResult = bezierQ4Equation.apply(this, tmatrix); var t3 = multi([0, 0, tmatrix[0], tmatrix[1], tmatrix[2]], ymatrix[4]);
// 四次方程系数
var t5 = plus(t4, t3);
t5[2] = t5[2] - ymatrix[0];
t5[3] = t5[3] - ymatrix[1];
t5[4] = t5[4] - ymatrix[2];
tResult = bezierQ4Equation.apply(this, t5);
} }
} }
// t1 有解 // t1 有解
if(tResult) { if(tResult) {
var pair = [], t1, t2, t2Result = false; var tr1, t2Result, tr2, px, tx;
for (var i = tResult.length - 1; i >= 0; i--) {
tr1 = tResult[i];
for (var i = 0, l = tResult.length; i < l; i++) { // 方程联和求解
t1 = tResult[i];
// 代入求t2
t2Result = bezierQ2Equation( t2Result = bezierQ2Equation(
xmatrix[3], ymatrix[3] - xmatrix[3],
xmatrix[4], ymatrix[4] - xmatrix[4],
-(xmatrix[0] * Math.pow(t1, 2) + xmatrix[1] * t1 + xmatrix[2]) (xmatrix[0] * Math.pow(tr1, 2) + xmatrix[1] * tr1 + xmatrix[2])
-(ymatrix[0] * Math.pow(tr1, 2) + ymatrix[1] * tr1 + ymatrix[2])
); );
if(t2Result) { if(!t2Result) {
tResult.splice(i, 1);
}
else {
for (var j = t2Result.length - 1; j >= 0; j--) {
// 验证根是否成立 tr2 = t2Result[j];
for (var j = 0, ll = t2Result.length; j < ll; j++) {
t2 = t2Result[j]; // 这里有些情况会出现4个解需要舍去
// 控制舍入误差,非必需 px = p0.x * Math.pow(1 - tr1, 2) + 2 * p1.x * tr1 * (1-tr1) + p2.x * Math.pow(tr1, 2);
// if( tx = t0.x * Math.pow(1 - tr2, 2) + 2 * t1.x * tr2 * (1-tr2) + t2.x * Math.pow(tr2, 2);
// true ||
// 0.0001 > Math.abs( if(0.001 < Math.abs(px - tx)){
// ymatrix[0] * Math.pow(t1, 2) + ymatrix[1] * t1 + ymatrix[2] t2Result.splice(j, 1);
// - (ymatrix[3] * Math.pow(t2, 2) + ymatrix[4] * t2) }
// ) }
// ) {
pair.push([t1, t2]); if(!t2Result.length) {
//} tResult.splice(i, 1);
} }
} }
} }
// 求解xy坐标 // 求解xy坐标
return pair.length return tResult.length
? pair.map(function(item) { ? tResult.map(function(t) {
return { return {
x: xmatrix[0] * Math.pow(t1, 2) + xmatrix[1] * t1 + p0.x, x: p0.x * Math.pow(1 - t, 2) + 2 * p1.x * t * (1-t) + p2.x * Math.pow(t, 2),
y: ymatrix[0] * Math.pow(t1, 2) + ymatrix[1] * t1 + p0.y y: p0.y * Math.pow(1 - t, 2) + 2 * p1.y * t * (1-t) + p2.y * Math.pow(t, 2)
}; };
}) })
: false; : false;
} }
} }

View File

@ -35,22 +35,34 @@ define(
var b1_lb = isPointInBounding(b2, {x: b1.x, y: b1.y + b1.height}); // 左下 var b1_lb = isPointInBounding(b2, {x: b1.x, y: b1.y + b1.height}); // 左下
var b1_rb = isPointInBounding(b2, {x: b1.x + b1.width, y: b1.y + b1.height}); //右下 var b1_rb = isPointInBounding(b2, {x: b1.x + b1.width, y: b1.y + b1.height}); //右下
// b2 包含 b1
if(b1_lt && b1_rt && b1_lb && b1_rb) {
return 2;
}
var b2_lt = isPointInBounding(b1, b2); // 左上 var b2_lt = isPointInBounding(b1, b2); // 左上
var b2_rt = isPointInBounding(b1, {x: b2.x + b2.width, y: b2.y}); // 右上 var b2_rt = isPointInBounding(b1, {x: b2.x + b2.width, y: b2.y}); // 右上
var b2_lb = isPointInBounding(b1, {x: b2.x, y: b2.y + b2.height}); // 左下 var b2_lb = isPointInBounding(b1, {x: b2.x, y: b2.y + b2.height}); // 左下
var b2_rb = isPointInBounding(b1, {x: b2.x + b2.width, y: b2.y + b2.height}); //右下 var b2_rb = isPointInBounding(b1, {x: b2.x + b2.width, y: b2.y + b2.height}); //右下
// b1 包含 b2
if(b2_lt && b2_rt && b2_lb && b2_rb) {
return 3;
}
// 无交点 // 无交点
if(false == (b1_lt || b1_rt || b1_lb || b1_rb || b2_lt || b2_rt || b2_lb || b2_rb)) { if(false == (b1_lt || b1_rt || b1_lb || b1_rb || b2_lt || b2_rt || b2_lb || b2_rb)) {
return false; // 判断十字架
} if(
// b2 包含 b1 (b1.x >= b2.x && b1.x <= b2.x + b2.width)
else if (b1_lt && b1_rt && b1_lb && b1_rb) { || (b1.y >= b2.y && b1.y <= b2.y + b2.height)
return 2; ) {
} return 1;
// b1 包含 b2 }
else if (b2_lt && b2_rt && b2_lb && b2_rb) { else {
return 3; return false;
}
} }
// 有交点 // 有交点
else { else {