Boolean: Correctly handle open filled paths

Closes #1647
This commit is contained in:
Jürg Lehni 2019-06-23 03:24:13 +02:00
parent 15e00e0b99
commit 14ce1dc011
3 changed files with 38 additions and 9 deletions

View file

@ -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();

View file

@ -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;

View file

@ -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);
});