add path hover check
This commit is contained in:
parent
52888e340b
commit
d08f45f175
@ -10,6 +10,8 @@ define(
|
||||
function(require) {
|
||||
|
||||
var computeBoundingBox = require('render/util/computeBoundingBox');
|
||||
var isLineCross = require('render/util/isLineCross');
|
||||
var isBezierCross = require('render/util/isBezierCross');
|
||||
|
||||
var entry = {
|
||||
|
||||
@ -23,7 +25,7 @@ define(
|
||||
var height = canvas.offsetHeight;
|
||||
|
||||
var points = [];
|
||||
[0, 1, 2].forEach(function(i) {
|
||||
[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)
|
||||
@ -63,6 +65,8 @@ define(
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
||||
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
//绘制2次贝塞尔曲线
|
||||
ctx.beginPath();
|
||||
@ -93,6 +97,8 @@ define(
|
||||
ctx.lineTo(max[0], min[1]);
|
||||
ctx.lineTo(min[0], min[1]);
|
||||
ctx.stroke();
|
||||
|
||||
console.log(isBezierCross(points[0], points[1], points[2], points[3]));
|
||||
}
|
||||
|
||||
draw();
|
||||
|
@ -28,6 +28,9 @@
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
#control-point {
|
||||
border-color: blue;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
@ -35,6 +38,7 @@
|
||||
<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 class="point" data-index="3"></div>
|
||||
<canvas id="canvas" width="500" height="500">
|
||||
</canvas>
|
||||
<script>
|
||||
|
@ -10,7 +10,7 @@
|
||||
define(
|
||||
function(require) {
|
||||
var ShapeConstructor = require('./Shape');
|
||||
var isInsidePolygon = require('../util/isInsidePolygon');
|
||||
var isInsidePath = require('../util/isInsidePath');
|
||||
var pathAdjust = require('../util/pathAdjust');
|
||||
var proto = {
|
||||
|
||||
@ -61,15 +61,10 @@ define(
|
||||
&& y >= shape.y
|
||||
) {
|
||||
|
||||
return true;
|
||||
// var points = [];
|
||||
// shape.points.forEach(function(point) {
|
||||
// if(point.c == 'Q') {
|
||||
// points.push(point.p1);
|
||||
// }
|
||||
// points.push(point.p);
|
||||
// });
|
||||
// return isInsidePolygon(points, x, y);
|
||||
return isInsidePath(shape.points, {
|
||||
x: x - shape.x,
|
||||
y: y - shape.y
|
||||
});
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
@ -122,19 +122,7 @@ define(
|
||||
* @return {Object} shape对象
|
||||
*/
|
||||
function cloneShape(shape) {
|
||||
var _shape = null;
|
||||
// 移出克隆的元素
|
||||
if(shape['_shape']) {
|
||||
_shape = shape['_shape'];
|
||||
delete shape['_shape'];
|
||||
}
|
||||
|
||||
var cloned = lang.clone(shape);
|
||||
|
||||
if(_shape) {
|
||||
shape['_shape'] = _shape;
|
||||
}
|
||||
return cloned;
|
||||
return lang.clone(shape);
|
||||
}
|
||||
|
||||
Shape.clone = cloneShape;
|
||||
|
96
src/render/util/isBezierCross.js
Normal file
96
src/render/util/isBezierCross.js
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @file isBezierCross.js
|
||||
* @author mengke01
|
||||
* @date
|
||||
* @description
|
||||
* 判断x轴射线是否与贝塞尔曲线相交
|
||||
*/
|
||||
|
||||
|
||||
define(
|
||||
function(require) {
|
||||
|
||||
var computeBoundingBox = require('./computeBoundingBox');
|
||||
|
||||
/**
|
||||
* 判断x轴射线是否与贝塞尔曲线相交
|
||||
*
|
||||
* @return {boolean} 是否
|
||||
*/
|
||||
function isBezierCross(p0, p1, p2, p) {
|
||||
|
||||
// 3点都在同一侧
|
||||
if(!((p0.y > p.y) + (p1.y > p.y) + (p2.y > p.y)) % 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var bound = computeBoundingBox.quadraticBezier(p0, p1, p2);
|
||||
|
||||
// 在包围盒内部
|
||||
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 b4ac = Math.pow(b, 2) - 4 * a *c;
|
||||
|
||||
// 有解
|
||||
if(b4ac >= 0) {
|
||||
|
||||
var t1 = (-b + Math.sqrt(b4ac, 2)) / 2 / a;
|
||||
var 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
|
||||
+ 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 true;
|
||||
}
|
||||
}
|
||||
else if(x1 != undefined) {
|
||||
if(p.x < x1) {
|
||||
return {
|
||||
x: x1,
|
||||
y: p.y
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(x2 != undefined) {
|
||||
if(p.x < x2) {
|
||||
return {
|
||||
x: x2,
|
||||
y: p.y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return isBezierCross;
|
||||
}
|
||||
);
|
@ -3,22 +3,94 @@
|
||||
* @author mengke01
|
||||
* @date
|
||||
* @description
|
||||
* 判断点是否在path内部
|
||||
* 判断点是否在path内部,射线法
|
||||
* TrueType uses the non-zero winding number rule.
|
||||
* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
|
||||
*/
|
||||
|
||||
|
||||
define(
|
||||
function(require) {
|
||||
var isBezierCross = require('./isBezierCross');
|
||||
var isLineCross = require('./isLineCross');
|
||||
|
||||
/**
|
||||
* 判断点是否在path内部
|
||||
* 以p点的x轴向右射线为起点
|
||||
*
|
||||
* @param {Object} path path对象
|
||||
* @param {Object} p 点对象
|
||||
* @return {boolean} 是否
|
||||
*/
|
||||
function isInsidePath(path, p) {
|
||||
var i = -1;
|
||||
var l = path.length;
|
||||
var prev, cur, point;
|
||||
var zCount = 0;
|
||||
|
||||
while (++i < l) {
|
||||
point = path[i];
|
||||
switch (point.c) {
|
||||
case 'M':
|
||||
prev = point.p;
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
|
||||
if(isLineCross(prev, point.p, p)) {
|
||||
if(point.p.y < prev.y) {
|
||||
zCount++;
|
||||
}
|
||||
else {
|
||||
zCount--;
|
||||
}
|
||||
}
|
||||
prev = point.p;
|
||||
|
||||
break;
|
||||
|
||||
case 'Q':
|
||||
var joint = p1 = p2 = null;
|
||||
if(joint = isBezierCross(prev, point.p1, point.p, p)) {
|
||||
|
||||
if(!(joint.x > prev.x)^(joint.x < point.p1.x)) {
|
||||
p1 = prev;
|
||||
p2 = point.p1;
|
||||
}
|
||||
else {
|
||||
p1 = point.p1;
|
||||
p2 = point.p;
|
||||
}
|
||||
|
||||
if(p1.y < p2.y) {
|
||||
zCount++;
|
||||
}
|
||||
else {
|
||||
zCount--;
|
||||
}
|
||||
|
||||
}
|
||||
prev = point.p;
|
||||
break;
|
||||
|
||||
// case 'Z':
|
||||
// prev = path[i - 1];
|
||||
// // 需要判断最后为直线的情况
|
||||
// if(prev && prev.c == 'L') {
|
||||
// if(isLineCross(prev.p, point.p, p)) {
|
||||
|
||||
// if(point.p.y < prev.y) {
|
||||
// zCount++;
|
||||
// }
|
||||
// else {
|
||||
// zCount--;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
return !!zCount;
|
||||
}
|
||||
|
||||
return isInsidePath;
|
||||
|
50
src/render/util/isLineCross.js
Normal file
50
src/render/util/isLineCross.js
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* @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 true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return isLineCross;
|
||||
}
|
||||
);
|
Loading…
x
Reference in New Issue
Block a user