求二次贝塞尔曲线交点
This commit is contained in:
parent
55f6edaf49
commit
9c3bb600b9
134
demo/js/quadraticBezierCross.js
Normal file
134
demo/js/quadraticBezierCross.js
Normal 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;
|
||||
}
|
||||
);
|
59
demo/quadraticBezierCross.html
Normal file
59
demo/quadraticBezierCross.html
Normal 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>
|
34
src/render/util/bezierQuadeEquation.js
Normal file
34
src/render/util/bezierQuadeEquation.js
Normal 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;
|
||||
}
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
63
src/render/util/isBoundingBoxCross.js
Normal file
63
src/render/util/isBoundingBoxCross.js
Normal 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;
|
||||
}
|
||||
);
|
61
src/render/util/quadeEquation.js
Normal file
61
src/render/util/quadeEquation.js
Normal 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;
|
||||
}
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user