diff --git a/src/path/CurveLocation.js b/src/path/CurveLocation.js index 6adfda86..7d6dd9df 100644 --- a/src/path/CurveLocation.js +++ b/src/path/CurveLocation.js @@ -424,9 +424,9 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{ // both values point to the same curve, and the curve-time is to be // handled accordingly further down. var c2 = this.getCurve(), - c1 = t1 < tMin ? c2.getPrevious() : c2, + c1 = c2 && t1 < tMin ? c2.getPrevious() : c2, c4 = inter.getCurve(), - c3 = t2 < tMin ? c4.getPrevious() : c4; + c3 = c4 && t2 < tMin ? c4.getPrevious() : c4; // If t1 / t2 are at the end, then step to the next curve. if (t1 > tMax) c2 = c2.getNext(); diff --git a/src/path/PathItem.Boolean.js b/src/path/PathItem.Boolean.js index f31a35b0..30c38d36 100644 --- a/src/path/PathItem.Boolean.js +++ b/src/path/PathItem.Boolean.js @@ -46,6 +46,10 @@ PathItem.inject(new function() { exclude: { '1': true, '-1': true } }; + function getPaths(path) { + return path._children || [path]; + } + /* * Creates a clone of the path that we can modify freely, with its matrix * applied to its geometry. Calls #reduce() to simplify compound paths and @@ -53,12 +57,29 @@ PathItem.inject(new function() { * make sure all paths have correct winding direction. */ function preparePath(path, resolve) { - var res = path.clone(false).reduce({ simplify: true }) - .transform(null, true, true); + var res = path + .clone(false) + .reduce({ simplify: true }) + .transform(null, true, true); + // For correct results, close open filled paths with straight lines: + if (resolve && res.hasFill()) { + var paths = getPaths(res); + for (var i = 0, l = paths.length; i < l; i++) { + var path = paths[i]; + if (!path._closed) { + // Close with epsilon tolerance, to avoid tiny straight + // that would cause issues with intersection detection. + path.closePath(/*#=*/Numerical.EPSILON); + path.getFirstSegment().setHandleIn(0, 0); + path.getLastSegment().setHandleOut(0, 0); + } + } + } return resolve - ? res.resolveCrossings().reorient( - res.getFillRule() === 'nonzero', true) - : res; + ? res + .resolveCrossings() + .reorient(res.getFillRule() === 'nonzero', true) + : res; } function createResult(paths, simplify, path1, path2, options) { @@ -103,8 +124,8 @@ PathItem.inject(new function() { // intersection, path2 is null and getIntersections() handles it. var crossings = divideLocations( CurveLocation.expand(_path1.getCrossings(_path2))), - paths1 = _path1._children || [_path1], - paths2 = _path2 && (_path2._children || [_path2]), + paths1 = getPaths(_path1), + paths2 = _path2 && getPaths(_path2), segments = [], curves = [], paths; diff --git a/test/tests/Path_Boolean.js b/test/tests/Path_Boolean.js index 0d448243..bab08843 100644 --- a/test/tests/Path_Boolean.js +++ b/test/tests/Path_Boolean.js @@ -1222,3 +1222,11 @@ test('#1351', function() { var result = 'M10,10l40,40l50,50'; compareBoolean(path1.unite(path2), result); }); + +test('#1647', function() { + var path1 = PathItem.create("M0,0h60v60h-60z"); + var path2 = PathItem.create("M60,47c-0.89493,-0.00177 -1.72254,0.47497 -2.17,1.25l-12.12,21c-0.44703,0.77427 -0.44656,1.72831 0.00123,2.50214c0.44779,0.77383 1.27472,1.24963 2.16877,1.24786h24.24c0.89405,0.00177 1.72098,-0.47403 2.16877,-1.24786c0.44779,-0.77383 0.44826,-1.72787 0.00123,-2.50214l-12.12,-21c-0.44746,-0.77503 -1.27507,-1.25177 -2.17,-1.25M64.33,47l12.12,21c0.89315,1.54699 0.89316,3.45295 0.00003,4.99995c-0.89313,1.547 -2.54373,2.50001 -4.33003,2.50005h-24.24c-1.78631,-0.00005 -3.4369,-0.95306 -4.33003,-2.50005c-0.89313,-1.547 -0.89312,-3.45296 0.00003,-4.99995l12.12,-21c0.88236,-1.55771 2.53981,-2.51466 4.33,-2.5c1.79019,-0.01466 3.44764,0.94229 4.33,2.5"); + path1.fillColor = path2.fillColor = 'black'; + var result = 'M57.83,48.25l-6.78143,11.75l-2.88143,0l7.50286,-13c0.88236,-1.55771 2.53981,-2.51466 4.33,-2.5v2.5c-0.89493,-0.00177 -1.72254,0.47497 -2.17,1.25z'; + compareBoolean(path1.intersect(path2), result); +});