求二次贝塞尔曲线交点

This commit is contained in:
kekee000 2014-09-04 13:35:06 +08:00
parent 55f6edaf49
commit 9c3bb600b9
7 changed files with 434 additions and 49 deletions

View File

@ -0,0 +1,134 @@
/**
* @file quadraticBezier.js
* @author mengke01
* @date
* @description
* 二次贝塞尔求交演示
*/
define(
function(require) {
var isBezierCross = require('render/util/isBezierCross');
var entry = {
/**
* 初始化
*/
init: function() {
var canvas = $('#canvas').get(0);
var ctx = canvas.getContext('2d');
var width = canvas.offsetWidth;
var height = canvas.offsetHeight;
// var points = [];
// [0, 1, 2, 3].forEach(function(i) {
// var p = {
// x: Math.floor(Math.random() * (width - 100) + 50),
// y: Math.floor(Math.random() * (height - 100) + 50)
// }
// points[i] = p;
// $($('.point').get(i)).css({
// left: p.x,
// top: p.y
// });
// });
var points = [
{
x: 130,
y: 77
},
{
x: 130,
y: 148
},
{
x: 65,
y: 148
},
{
x: 330,
y: 77
},
{
x: 130,
y: 148
},
{
x: 165,
y: 148
}
];
points.forEach(function(p, index) {
$('[data-index="'+index+'"]').css({
left: p.x,
top: p.y
})
});
var point;
$('.point').on('mousedown', function(e) {
point = $(this);
});
$(document.body).on('mousemove', onMove);
$(document.body).on('mouseup', function(e) {
onMove.call(this, e);
point = null;
});
function onMove(e) {
var px = e.pageX;
var py = e.pageY;
if(point) {
point.css({
left: px,
top: py
});
var p = points[+point.attr('data-index')];
p.x = px;
p.y = py;
draw();
}
}
function draw() {
ctx.clearRect(0, 0, width, height);
//绘制2次贝塞尔曲线
ctx.beginPath();
ctx.strokeStyle='black';
ctx.moveTo(points[0].x, points[0].y);
ctx.quadraticCurveTo(points[1].x, points[1].y, points[2].x, points[2].y);
ctx.lineWidth = 1;
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle='green';
ctx.moveTo(points[3].x, points[3].y);
ctx.quadraticCurveTo(points[4].x, points[4].y, points[5].x, points[5].y);
ctx.lineWidth = 1;
ctx.stroke();
//console.time('bezier');
var r = isBezierCross.apply(null, points);
//console.timeEnd('bezier');
//console.log(r);
}
draw();
}
};
entry.init();
return entry;
}
);

View File

@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>二次贝塞尔求交</title>
<script src="http://s1.bdstatic.com/r/www/cache/ecom/esl/1-8-2/esl.js"></script>
<script src="http://libs.baidu.com/jquery/1.9.0/jquery.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
.point {
position: absolute;
width: 9px;
height: 9px;
margin-left: -5px;
margin-top: -5px;
background: #CCC;
border: 1px solid red;
}
#start-point {
border-color: green;
}
#end-point {
border-color: black;
}
#control-point {
border-color: blue;
}
</style>
</head>
<body>
<div id="start-point" class="point" data-index="0"></div>
<div id="end-point" class="point" data-index="1"></div>
<div id="control-point" class="point" data-index="2"></div>
<div id="start-point1" class="point" data-index="3"></div>
<div id="end-point1" class="point" data-index="4"></div>
<div id="control-point1" class="point" data-index="5"></div>
<canvas id="canvas" width="800" height="600">
</canvas>
<script>
require.config({
baseUrl: '../src',
paths: {
demo: '../demo/js',
}
});
define('jquery', $);
</script>
<script>require(['demo/quadraticBezierCross'])</script>
</body>
</html>

View File

@ -0,0 +1,34 @@
/**
* @file bezierQuadeEquation.js
* @author mengke01
* @date
* @description
* 求解二次贝塞尔方程根
*/
define(
function(require) {
var quadeEquation = require('./quadeEquation');
/**
* 求解二次方程
*
* @param {number} a a系数
* @param {[type]} b b系数
* @param {[type]} c c系数
* @return {Array} 系数解
*/
function bezierQuadeEquation(a, b, c) {
var result = quadeEquation(a, b, c);
return result
? result.filter(function(item) {
return item >= 0 && item <= 1;
})
: false;
}
return bezierQuadeEquation;
}
);

View File

@ -9,14 +9,59 @@
define(
function(require) {
var computeBoundingBox = require('./computeBoundingBox');
var isBoundingBoxCross = require('./isBoundingBoxCross');
var bezierQuadeEquation = require('./bezierQuadeEquation');
/**
* 求两个bezier曲线的交点
*/
function(p0, p1, p2, t0, t1. t2) {
function isBezierCross(p0, p1, p2, t0, t1, t2) {
// t二项式系数
// a = p0 + p2 - 2p1
// b = 2(p1 - p0)
// c = p0
var bounding1 = computeBoundingBox.quadraticBezier(p0, p1, p2);
var bounding2 = computeBoundingBox.quadraticBezier(t0, t1, t2);
// 包围盒是否有交点
if(isBoundingBoxCross(bounding1, bounding2)) {
// 求解x二项式系数
var p0x = p0.x - t0.x;
var p1x = p1.x - t1.x;
var p2x = p2.x - t2.x;
var ax = p0x + p2x - 2 * p1x;
var bx = 2 * (p1x - p0x);
var cx = p0x;
var txResult = bezierQuadeEquation(ax, bx, cx);
console.log(txResult);
// x轴无交点
// if(!txResult) {
// return false;
// }
var p0y = p0.y - t0.y;
var p1y = p1.y - t1.y;
var p2y = p2.y - t2.y;
var ay = p0y + p2y - 2 * p1y;
var by = 2 * (p1y - p0y);
var cy = p0y;
var tyResult = bezierQuadeEquation(ay, by, cy);
console.log(tyResult);
}
return false;
}

View File

@ -11,6 +11,7 @@ define(
function(require) {
var computeBoundingBox = require('./computeBoundingBox');
var bezierQuadeEquation = require('./bezierQuadeEquation');
/**
* 判断x轴射线是否与贝塞尔曲线相交
@ -44,72 +45,60 @@ define(
// 在包围盒内部
if(
p.x < bound.x + bound.width
& p.y > bound.y && p.y < bound.y + bound.height
p.x >= bound.x && p.x <= bound.x + bound.width
&& p.y >= bound.y && p.y <= bound.y + bound.height
) {
var a = p0.y + p2.y - 2 * p1.y;
var b = 2 * (p1.y - p0.y);
var c = p0.y - p.y;
var t1;
var t2;
var tResult = bezierQuadeEquation(a, b, c);
// 只有一个解
if(a == 0) {
t1 = -c / b;
t2 = -1;
// 无解
if(!tResult) {
return false;
}
else {
var b4ac = Math.pow(b, 2) - 4 * a *c;
// 求解二次方程
if(b4ac >= 0) {
t1 = (-b + Math.sqrt(b4ac, 2)) / 2 / a;
t2 = (-b - Math.sqrt(b4ac, 2)) / 2 / a;
}
}
var t = 0;
var x1, x2;
// 两个交点
if(t1 >= 0 && t1 <= 1) {
t = t1;
x1 = Math.pow(1 - t, 2) * p0.x
// 一个解
else if(tResult.length == 1) {
var t = tResult[0];
var x = Math.pow(1 - t, 2) * p0.x
+ 2 * t * (1-t) * p1.x
+ Math.pow(t, 2) * p2.x;
}
if(t2 >= 0 && t2 <= 1) {
t = t2;
x2 = Math.pow(1 - t, 2) * p0.x
+ 2 * t * (1-t) * p1.x
+ Math.pow(t, 2) * p2.x;
}
if (x1 != undefined && x2 != undefined) {
// 点在两个交点中间
if(! (p.x > x1) ^ (p.x < x2) ) {
return p.x > x1
? {x: x2, y: p.y}
: {x: x1, y: p.y};
}
}
else if(x1 != undefined) {
if(p.x < x1) {
if(p.x < x) {
return {
x: x1,
x: x,
y: p.y
}
}
}
else if(x2 != undefined) {
if(p.x < x2) {
else {
var t = tResult[0];
var x1 = Math.pow(1 - t, 2) * p0.x
+ 2 * t * (1-t) * p1.x
+ Math.pow(t, 2) * p2.x;
t = tResult[1];
var x2 = Math.pow(1 - t, 2) * p0.x
+ 2 * t * (1-t) * p1.x
+ Math.pow(t, 2) * p2.x;
// 交换
if(x1 > x2) {
var t = x1;
x1 = x2;
x2 = t;
}
// 点在两个交点中间
if(p.x > x1 && p.x < x2) {
return {
x: x2,
y: p.y
}
};
}
}
}
return false;

View File

@ -0,0 +1,63 @@
/**
* @file isBoundingBoxCross.js
* @author mengke01
* @date
* @description
* 两个boundingbox的关系
*/
define(
function(require) {
/**
* 判断点是否在bounding box内部
*
* @return {boolean} 是否
*/
function isPointInBounding(bound, p) {
return p.x <= bound.x + bound.width
&& p.x >= bound.x
&& p.y <= bound.y + bound.height
&& p.y >= bound.y
}
/**
* 两个boundingbox的关系
*
* @param {Object} b1 bounding 1
* @param {Object} b2 bounding 2
* @return {number} 包含关系
*/
function isBoundingBoxCross(b1, b2) {
var b1_lt = isPointInBounding(b2, b1); // 左上
var b1_rt = isPointInBounding(b2, {x: b1.x + b1.width, y: b1.y}); // 右上
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 b2_lt = isPointInBounding(b1, b2); // 左上
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_rb = isPointInBounding(b1, {x: b2.x + b2.width, y: b2.y + b2.height}); //右下
// 无交点
if(false == (b1_lt || b1_rt || b1_lb || b1_rb || b2_lt || b2_rt || b2_lb || b2_rb)) {
return false;
}
// b2 包含 b1
else if (b1_lt && b1_rt && b1_lb && b1_rb) {
return 2;
}
// b1 包含 b2
else if (b2_lt && b2_rt && b2_lb && b2_rb) {
return 3;
}
// 有交点
else {
return 1;
}
}
return isBoundingBoxCross;
}
);

View File

@ -0,0 +1,61 @@
/**
* @file quadeEquation.js
* @author mengke01
* @date
* @description
* 求解二次方程
*/
define(
function(require) {
/**
* 求解二次方程
*
* @param {number} a a系数
* @param {[type]} b b系数
* @param {[type]} c c系数
* @return {Array} 系数解
*/
function quadraticEquation(a, b, c) {
if(a == 0) {
return [-c / b];
}
if(b == 0) {
if(a | c <= 0) {
var x2 = Math.sqrt(-c / a);
return [x2, -x2];
}
else {
return false;
}
}
if(c == 0) {
return [0, -b / a];
}
var b4ac = Math.pow(b, 2) - 4 * a *c;
if(b4ac >= 0) {
var x1 = (-b + Math.sqrt(b4ac, 2)) / a / 2;
var x2 = (-b - Math.sqrt(b4ac, 2)) / a / 2;
if(x1 == x2) {
return [x1];
}
else {
return [x1, x2];
}
}
else {
return false;
}
}
return quadraticEquation;
}
);