From 48cf995937b261de3f3dc0fdcfc738474ed434a1 Mon Sep 17 00:00:00 2001 From: mkwiser Date: Thu, 4 Sep 2014 00:24:42 +0800 Subject: [PATCH] fix bezier cross bugs --- demo/js/quadraticBezier.js | 76 ++++++++--- src/render/Controller.js | 3 +- src/render/shape/Rect.js | 26 ++-- src/render/util/isBezierCross.js | 81 +----------- src/render/util/isBezierRayCross.js | 121 ++++++++++++++++++ src/render/util/isInsidePath.js | 4 +- .../{isLineCross.js => isLineRayCross.js} | 5 +- 7 files changed, 208 insertions(+), 108 deletions(-) create mode 100644 src/render/util/isBezierRayCross.js rename src/render/util/{isLineCross.js => isLineRayCross.js} (90%) diff --git a/demo/js/quadraticBezier.js b/demo/js/quadraticBezier.js index 2d90564..e652c2f 100644 --- a/demo/js/quadraticBezier.js +++ b/demo/js/quadraticBezier.js @@ -10,8 +10,8 @@ define( function(require) { var computeBoundingBox = require('render/util/computeBoundingBox'); - var isLineCross = require('render/util/isLineCross'); - var isBezierCross = require('render/util/isBezierCross'); + var isLineCross = require('render/util/isLineRayCross'); + var isBezierRayCross = require('render/util/isBezierRayCross'); var entry = { @@ -24,18 +24,62 @@ define( 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) + // 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: 9, + y: 133 } - points[i] = p; - $($('.point').get(i)).css({ - left: p.x, - top: p.y - }); - }); + ]; + + // var points = [ + // { + // x: 65, + // y: 148 + // }, + // { + // x: 20, + // y: 148 + // }, + // { + // x: 5, + // y: 115 + // }, + // { + // x: 9, + // y: 133 + // } + // ]; + + + $('[data-index="3"]').css({ + left: points[3].x, + top: points[3].y + }) var point; @@ -97,8 +141,10 @@ 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])); + //console.time('bezier'); + var r = isBezierRayCross(points[0], points[1], points[2], points[3]); + //console.timeEnd('bezier'); + console.log(r); } draw(); diff --git a/src/render/Controller.js b/src/render/Controller.js index 76fb1da..60a4b1f 100644 --- a/src/render/Controller.js +++ b/src/render/Controller.js @@ -30,8 +30,9 @@ define( render.camera.ratio = 1; }); - render.capture.on('dragstart', function(e) { + render.capture.on('down', function(e) { var shape = render.painter.getShapeIn(e); + if(shape) { render.selectedShape = shape; } diff --git a/src/render/shape/Rect.js b/src/render/shape/Rect.js index 7b8ac3a..ebd2637 100644 --- a/src/render/shape/Rect.js +++ b/src/render/shape/Rect.js @@ -22,8 +22,8 @@ define( */ getRect: function(shape) { return { - x: shape.x, - y: shape.y, + x: shape.x - shape.width / 2 , + y: shape.y - shape.height / 2, width: shape.width, height: shape.height }; @@ -38,10 +38,12 @@ define( * @param {boolean} 是否 */ isIn: function(shape, x, y) { - return x <= shape.x + shape.width - && x >= shape.x - && y <= shape.y + shape.height - && y >= shape.y; + var w = shape.width / 2; + var h = shape.height / 2; + return x <= shape.x + w + && x >= shape.x - w + && y <= shape.y + h + && y >= shape.y - h; }, /** @@ -51,13 +53,13 @@ define( * @param {Object} shape shape数据 */ draw: function(ctx, shape) { - var w = shape.width; - var h = shape.height; - ctx.moveTo(shape.x, shape.y); - ctx.lineTo(shape.x + w, shape.y); + var w = shape.width / 2; + var h = shape.height / 2; + ctx.moveTo(shape.x - w, shape.y - h); + ctx.lineTo(shape.x + w, shape.y - h); ctx.lineTo(shape.x + w, shape.y + h); - ctx.lineTo(shape.x, shape.y + h); - ctx.lineTo(shape.x, shape.y); + ctx.lineTo(shape.x - w, shape.y + h); + ctx.lineTo(shape.x - w, shape.y - h); } }; diff --git a/src/render/util/isBezierCross.js b/src/render/util/isBezierCross.js index 63db993..9979711 100644 --- a/src/render/util/isBezierCross.js +++ b/src/render/util/isBezierCross.js @@ -3,91 +3,18 @@ * @author mengke01 * @date * @description - * 判断x轴射线是否与贝塞尔曲线相交 + * 求两个bezier曲线的交点 */ define( function(require) { - - var computeBoundingBox = require('./computeBoundingBox'); - + /** - * 判断x轴射线是否与贝塞尔曲线相交 - * - * @return {boolean} 是否 + * 求两个bezier曲线的交点 */ - function isBezierCross(p0, p1, p2, p) { + function(p0, p1, p2, t0, t1. t2) { - // 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; } diff --git a/src/render/util/isBezierRayCross.js b/src/render/util/isBezierRayCross.js new file mode 100644 index 0000000..54c1b2c --- /dev/null +++ b/src/render/util/isBezierRayCross.js @@ -0,0 +1,121 @@ +/** + * @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(bound.width == 0) { + return ! (p.y > p0.y) ^ (p.y < p2.y) + ? { + 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 t1; + var t2; + + // 只有一个解 + if(a == 0) { + t1 = -c / b; + t2 = -1; + } + 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 + + 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) { + return { + x: x1, + y: p.y + } + } + } + else if(x2 != undefined) { + if(p.x < x2) { + return { + x: x2, + y: p.y + } + } + } + + } + + return false; + } + + + return isBezierCross; + } +); diff --git a/src/render/util/isInsidePath.js b/src/render/util/isInsidePath.js index 4b37307..d641d9f 100644 --- a/src/render/util/isInsidePath.js +++ b/src/render/util/isInsidePath.js @@ -11,8 +11,8 @@ define( function(require) { - var isBezierCross = require('./isBezierCross'); - var isLineCross = require('./isLineCross'); + var isBezierCross = require('./isBezierRayCross'); + var isLineCross = require('./isLineRayCross'); /** * 判断点是否在path内部 diff --git a/src/render/util/isLineCross.js b/src/render/util/isLineRayCross.js similarity index 90% rename from src/render/util/isLineCross.js rename to src/render/util/isLineRayCross.js index 09ab8a0..1c05e54 100644 --- a/src/render/util/isLineCross.js +++ b/src/render/util/isLineRayCross.js @@ -38,7 +38,10 @@ define( var xcross = (p.y - p0.y) / (p1.y - p0.y) * (p1.x - p0.x) + p0.x; if (xcross > p.x) { - return true; + return { + x: xcross, + y: p.y + }; } }