fix path cross
This commit is contained in:
parent
e5d45a4572
commit
49bc739f8c
45
demo/boundSegmentCross.html
Normal file
45
demo/boundSegmentCross.html
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>线段相交</title>
|
||||||
|
<script src="./lib/esl.js"></script>
|
||||||
|
<script src="./lib/jquery.min.js"></script>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.point {
|
||||||
|
position: absolute;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
margin-left: -5px;
|
||||||
|
margin-top: -5px;
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
|
[data-index="0"], [data-index="1"] {
|
||||||
|
border-color: green
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="point" data-index="0"></div>
|
||||||
|
<div class="point" data-index="1"></div>
|
||||||
|
<div class="point" data-index="2"></div>
|
||||||
|
<div class="point" data-index="3"></div>
|
||||||
|
<canvas id="canvas" width="500" height="500"></canvas>
|
||||||
|
<script>
|
||||||
|
require.config({
|
||||||
|
baseUrl: '../src',
|
||||||
|
paths: {
|
||||||
|
demo: '../demo/js',
|
||||||
|
}
|
||||||
|
});
|
||||||
|
define('jquery', $);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script>require(['demo/boundSegmentCross'])</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
100
demo/js/boundSegmentCross.js
Normal file
100
demo/js/boundSegmentCross.js
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
* @file quadraticBezier.js
|
||||||
|
* @author mengke01
|
||||||
|
* @date
|
||||||
|
* @description
|
||||||
|
* 线段和bound相交
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
function(require) {
|
||||||
|
|
||||||
|
var isBoundingBoxSegmentCross = require('graphics/isBoundingBoxSegmentCross');
|
||||||
|
|
||||||
|
var entry = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化
|
||||||
|
*/
|
||||||
|
init: function() {
|
||||||
|
var canvas = $('#canvas').get(0);
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
var width = canvas.offsetWidth;
|
||||||
|
var height = canvas.offsetHeight;
|
||||||
|
|
||||||
|
var points = [{"x":50,"y":200},{"x":97,"y":98},{"x":58,"y":94},{"x":105,"y":201}];
|
||||||
|
|
||||||
|
$('[data-index]').each(function(index, item) {
|
||||||
|
$(item).css({
|
||||||
|
left: points[index].x,
|
||||||
|
top: points[index].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() {
|
||||||
|
|
||||||
|
var bound = {
|
||||||
|
x: Math.min(points[0].x, points[1].x),
|
||||||
|
y: Math.min(points[0].y, points[1].y),
|
||||||
|
width: Math.abs(points[0].x - points[1].x),
|
||||||
|
height: Math.abs(points[0].y - points[1].y)
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
//绘制2次贝塞尔曲线
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.strokeStyle='black';
|
||||||
|
ctx.moveTo(bound.x, bound.y);
|
||||||
|
ctx.lineTo(bound.x, bound.y + bound.height);
|
||||||
|
ctx.lineTo(bound.x + bound.width, bound.y + bound.height);
|
||||||
|
ctx.lineTo(bound.x + bound.width, bound.y);
|
||||||
|
ctx.lineTo(bound.x, bound.y);
|
||||||
|
|
||||||
|
ctx.moveTo(points[2].x, points[2].y);
|
||||||
|
ctx.lineTo(points[3].x, points[3].y);
|
||||||
|
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.stroke();
|
||||||
|
//console.time('bezier');
|
||||||
|
var r = isBoundingBoxSegmentCross(bound, points[2], points[3]);
|
||||||
|
//console.timeEnd('bezier');
|
||||||
|
console.log(r);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
entry.init();
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
);
|
@ -82,7 +82,7 @@ define(
|
|||||||
var computeBoundingBox = require('graphics/computeBoundingBox');
|
var computeBoundingBox = require('graphics/computeBoundingBox');
|
||||||
var pathAdjust = require('render/util/pathAdjust');
|
var pathAdjust = require('render/util/pathAdjust');
|
||||||
|
|
||||||
pathArray.reverse().forEach(function(path) {
|
pathArray.slice(4, 5).forEach(function(path) {
|
||||||
var shape = {};
|
var shape = {};
|
||||||
shape.points = path;
|
shape.points = path;
|
||||||
var bound = computeBoundingBox.computePath(path);
|
var bound = computeBoundingBox.computePath(path);
|
||||||
|
@ -14,7 +14,8 @@ define(
|
|||||||
var bezierQ2Equation = require('../math/bezierQ2Equation');
|
var bezierQ2Equation = require('../math/bezierQ2Equation');
|
||||||
var bezierQ4Equation = require('../math/bezierQ4Equation');
|
var bezierQ4Equation = require('../math/bezierQ4Equation');
|
||||||
var bezierCubeEquation = require('../math/bezierCubeEquation');
|
var bezierCubeEquation = require('../math/bezierCubeEquation');
|
||||||
|
var ceil = require('./util').ceil;
|
||||||
|
|
||||||
var matrix = require('./matrix');
|
var matrix = require('./matrix');
|
||||||
var multi = matrix.multi;
|
var multi = matrix.multi;
|
||||||
var minus = matrix.minus;
|
var minus = matrix.minus;
|
||||||
@ -186,10 +187,10 @@ define(
|
|||||||
// 求解x,y坐标
|
// 求解x,y坐标
|
||||||
return tResult.length
|
return tResult.length
|
||||||
? tResult.map(function(t) {
|
? tResult.map(function(t) {
|
||||||
return {
|
return ceil({
|
||||||
x: p0.x * Math.pow(1 - t, 2) + 2 * p1.x * t * (1-t) + p2.x * Math.pow(t, 2),
|
x: p0.x * Math.pow(1 - t, 2) + 2 * p1.x * t * (1-t) + p2.x * Math.pow(t, 2),
|
||||||
y: p0.y * Math.pow(1 - t, 2) + 2 * p1.y * t * (1-t) + p2.y * Math.pow(t, 2)
|
y: p0.y * Math.pow(1 - t, 2) + 2 * p1.y * t * (1-t) + p2.y * Math.pow(t, 2)
|
||||||
};
|
});
|
||||||
})
|
})
|
||||||
: false;
|
: false;
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ define(
|
|||||||
function(require) {
|
function(require) {
|
||||||
|
|
||||||
var bezierQ2Equation = require('../math/bezierQ2Equation');
|
var bezierQ2Equation = require('../math/bezierQ2Equation');
|
||||||
|
var ceil = require('./util').ceil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断贝塞尔曲线与直线相交
|
* 判断贝塞尔曲线与直线相交
|
||||||
@ -66,10 +67,10 @@ define(
|
|||||||
|
|
||||||
if(result) {
|
if(result) {
|
||||||
return result.map(function(t) {
|
return result.map(function(t) {
|
||||||
return {
|
return ceil({
|
||||||
x: p0.x * Math.pow(1 - t, 2) + 2 * p1.x * t * (1-t) + p2.x * Math.pow(t, 2),
|
x: p0.x * Math.pow(1 - t, 2) + 2 * p1.x * t * (1-t) + p2.x * Math.pow(t, 2),
|
||||||
y: p0.y * Math.pow(1 - t, 2) + 2 * p1.y * t * (1-t) + p2.y * Math.pow(t, 2)
|
y: p0.y * Math.pow(1 - t, 2) + 2 * p1.y * t * (1-t) + p2.y * Math.pow(t, 2)
|
||||||
};
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@
|
|||||||
|
|
||||||
define(
|
define(
|
||||||
function(require) {
|
function(require) {
|
||||||
|
|
||||||
var computeBoundingBox = require('./computeBoundingBox');
|
var computeBoundingBox = require('./computeBoundingBox');
|
||||||
var bezierQ2Equation = require('../math/bezierQ2Equation');
|
var isBezierLineCross = require('./isBezierLineCross');
|
||||||
|
var isPointInBound = require('./util').isPointInBound;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断x轴射线是否与贝塞尔曲线相交
|
* 判断x轴射线是否与贝塞尔曲线相交
|
||||||
@ -25,80 +25,12 @@ define(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bound = computeBoundingBox.quadraticBezier(p0, p1, p2);
|
var result = isBezierLineCross(p0, p1, p2, p, {x: 10000, y: p.y});
|
||||||
|
if (result) {
|
||||||
// 退化成线段
|
var filter = result.filter(function(item) {
|
||||||
if(bound.width == 0) {
|
return item.x >= p.x;
|
||||||
return ! (p.y > p0.y) ^ (p.y < p2.y)
|
});
|
||||||
? {
|
return filter.length ? filter : false;
|
||||||
x: p0.x,
|
|
||||||
y: p.y
|
|
||||||
}
|
|
||||||
: false;
|
|
||||||
}
|
|
||||||
// 判断是否在曲线上
|
|
||||||
else if (bound.height == 0) {
|
|
||||||
return p.y == p0.y && (p.x < p0.x || p.x < p2.x)
|
|
||||||
? p
|
|
||||||
: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 在包围盒左侧
|
|
||||||
if(
|
|
||||||
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 tResult = bezierQ2Equation(a, b, c);
|
|
||||||
|
|
||||||
// 无解
|
|
||||||
if(!tResult) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// 一个解
|
|
||||||
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(p.x < x) {
|
|
||||||
return {
|
|
||||||
x: x,
|
|
||||||
y: p.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
return false;
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
define(
|
define(
|
||||||
function(require) {
|
function(require) {
|
||||||
var isPointInBound = require('./util').isPointInBound;
|
var isPointInBound = require('./util').isPointInBound;
|
||||||
|
var isSegmentCross = require('./isSegmentCross');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* boundingbox和线段的关系
|
* boundingbox和线段的关系
|
||||||
@ -19,8 +20,27 @@ define(
|
|||||||
* @param {Object} s1 线段点2
|
* @param {Object} s1 线段点2
|
||||||
* @return {number} 包含关系
|
* @return {number} 包含关系
|
||||||
*/
|
*/
|
||||||
function isBoundingBoxSegmentCross(bound, s0 s1) {
|
function isBoundingBoxSegmentCross(bound, s0, s1) {
|
||||||
|
if (isPointInBound(bound, s0) || isPointInBound(bound, s1)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
isSegmentCross(bound, {x: bound.x, y: bound.y + bound.height}, s0, s1)
|
||||||
|
|| isSegmentCross(bound, {x: bound.x + bound.width, y: bound.y}, s0, s1)
|
||||||
|
|| isSegmentCross(
|
||||||
|
{x: bound.x + bound.width, y: bound.y},
|
||||||
|
{x: bound.x + bound.width, y: bound.y + bound.height},
|
||||||
|
s0, s1)
|
||||||
|
|| isSegmentCross(
|
||||||
|
{x: bound.x, y: bound.y + bound.height},
|
||||||
|
{x: bound.x + bound.width, y: bound.y + bound.height},
|
||||||
|
s0, s1)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return isBoundingBoxSegmentCross;
|
return isBoundingBoxSegmentCross;
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
define(
|
define(
|
||||||
function(require) {
|
function(require) {
|
||||||
var isBezierRayCross = require('./isBezierRayCross');
|
var isBezierRayCross = require('./isBezierRayCross');
|
||||||
var isLineRayCross = require('./isLineRayCross');
|
var isSegmentRayCross = require('./isSegmentRayCross');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断点是否在path内部
|
* 判断点是否在path内部
|
||||||
@ -36,9 +36,15 @@ define(
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
|
var joint = null;
|
||||||
|
if(joint = isSegmentRayCross(prev, point.p, p)) {
|
||||||
|
|
||||||
if(isLineRayCross(prev, point.p, p)) {
|
// 在直线上
|
||||||
if(point.p.y < prev.y) {
|
if(joint[0].x == p.x) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(point.p.y > prev.y) {
|
||||||
zCount++;
|
zCount++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -51,9 +57,21 @@ define(
|
|||||||
|
|
||||||
case 'Q':
|
case 'Q':
|
||||||
var joint = p1 = p2 = null;
|
var joint = p1 = p2 = null;
|
||||||
|
|
||||||
if(joint = isBezierRayCross(prev, point.p1, point.p, p)) {
|
if(joint = isBezierRayCross(prev, point.p1, point.p, p)) {
|
||||||
|
|
||||||
|
// 在曲线上
|
||||||
|
if(joint[0].x == p.x || joint[1] && joint[1].x == p.x) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if(!(joint.x > prev.x)^(joint.x < point.p1.x)) {
|
if (joint.length == 2) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
joint = joint[0];
|
||||||
|
|
||||||
|
if(joint.y > prev.y && joint.y < point.p1.y) {
|
||||||
p1 = prev;
|
p1 = prev;
|
||||||
p2 = point.p1;
|
p2 = point.p1;
|
||||||
}
|
}
|
||||||
@ -62,7 +80,7 @@ define(
|
|||||||
p2 = point.p;
|
p2 = point.p;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(p1.y < p2.y) {
|
if(p2.y > p1.y) {
|
||||||
zCount++;
|
zCount++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -72,21 +90,6 @@ define(
|
|||||||
}
|
}
|
||||||
prev = point.p;
|
prev = point.p;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// case 'Z':
|
|
||||||
// prev = path[i - 1];
|
|
||||||
// // 需要判断最后为直线的情况
|
|
||||||
// if(prev && prev.c == 'L') {
|
|
||||||
// if(isLineRayCross(prev.p, point.p, p)) {
|
|
||||||
|
|
||||||
// if(point.p.y < prev.y) {
|
|
||||||
// zCount++;
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// zCount--;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
/**
|
|
||||||
* @file isLineCross.js
|
|
||||||
* @author mengke01
|
|
||||||
* @date
|
|
||||||
* @description
|
|
||||||
* 判断x轴射线是否穿过线段
|
|
||||||
*/
|
|
||||||
|
|
||||||
define(
|
|
||||||
function(require) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断x轴射线是否穿过线段
|
|
||||||
*
|
|
||||||
* @return {boolean} 是否
|
|
||||||
*/
|
|
||||||
function isLineCross(p0, p1, p) {
|
|
||||||
// 射线平行于线段
|
|
||||||
if (p0.y == p1.y && p0.y == p.y) {
|
|
||||||
return p.x < p1.x;
|
|
||||||
}
|
|
||||||
else if(p0.y == p1.y && p0.y != p.y) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 直线在射线两边,求交点
|
|
||||||
if (!(p0.y < p.y) ^ (p1.y > p.y)) {
|
|
||||||
|
|
||||||
// 垂直, 并且大于p
|
|
||||||
if (p0.x == p1.x) {
|
|
||||||
if(p.x < p0.x) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var xcross = (p.y - p0.y) / (p1.y - p0.y) * (p1.x - p0.x) + p0.x;
|
|
||||||
if (xcross > p.x) {
|
|
||||||
return {
|
|
||||||
x: xcross,
|
|
||||||
y: p.y
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isLineCross;
|
|
||||||
}
|
|
||||||
);
|
|
@ -10,6 +10,7 @@ define(
|
|||||||
function(require) {
|
function(require) {
|
||||||
|
|
||||||
var isBoundingBoxCross = require('./isBoundingBoxCross');
|
var isBoundingBoxCross = require('./isBoundingBoxCross');
|
||||||
|
var ceil = require('./util').ceil;
|
||||||
var matrix = require('./matrix');
|
var matrix = require('./matrix');
|
||||||
var multi = matrix.multi;
|
var multi = matrix.multi;
|
||||||
var minus = matrix.minus;
|
var minus = matrix.minus;
|
||||||
@ -141,7 +142,7 @@ define(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if(isPointInBound(b1, p) && isPointInBound(b2, p)) {
|
if(isPointInBound(b1, p) && isPointInBound(b2, p)) {
|
||||||
return [p];
|
return [ceil(p)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
src/graphics/isSegmentRayCross.js
Normal file
25
src/graphics/isSegmentRayCross.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* @file isSegmentRayCross.js
|
||||||
|
* @author mengke01
|
||||||
|
* @date
|
||||||
|
* @description
|
||||||
|
* 判断x轴射线是否穿过线段
|
||||||
|
*/
|
||||||
|
|
||||||
|
define(
|
||||||
|
function(require) {
|
||||||
|
|
||||||
|
var isSegmentCross = require('./isSegmentCross');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断x轴射线是否穿过线段
|
||||||
|
*
|
||||||
|
* @return {boolean} 是否
|
||||||
|
*/
|
||||||
|
function isSegmentRayCross(p0, p1, p) {
|
||||||
|
return isSegmentCross(p0, p1, p, {x: 100000, y: p.y});
|
||||||
|
}
|
||||||
|
|
||||||
|
return isSegmentRayCross;
|
||||||
|
}
|
||||||
|
);
|
Loading…
x
Reference in New Issue
Block a user