Detect and handle fully overlapping paths in boolean operations.

Closes #885
This commit is contained in:
Jürg Lehni 2016-01-06 11:50:32 +01:00
parent feec904288
commit 156dd661ed

View file

@ -51,19 +51,22 @@ PathItem.inject(new function() {
return resolve ? res.resolveCrossings() : res; return resolve ? res.resolveCrossings() : res;
} }
function finishBoolean(ctor, paths, path1, path2, reduce) { function finishResult(result, path1, path2) {
// Insert the resulting path above whichever of the two paths appear
// further up in the stack.
result.insertAbove(path2 && path1.isSibling(path2)
&& path1.getIndex() < path2.getIndex() ? path2 : path1);
// Copy over the input path attributes, excluding matrix and we're done.
result.copyAttributes(path1, true);
return result;
}
function createResult(ctor, paths, reduce) {
var result = new ctor(Item.NO_INSERT); var result = new ctor(Item.NO_INSERT);
result.addChildren(paths, true); result.addChildren(paths, true);
// See if the item can be reduced to just a simple Path. // See if the item can be reduced to just a simple Path.
if (reduce) if (reduce)
result = result.reduce({ simplify: true }); result = result.reduce({ simplify: true });
// Insert the resulting path above whichever of the two paths appear
// further up in the stack.
result.insertAbove(path2 && path1.isSibling(path2)
&& path1.getIndex() < path2.getIndex()
? path2 : path1);
// Copy over the input path attributes, excluding matrix and we're done.
result.copyAttributes(path1, true);
return result; return result;
} }
@ -96,7 +99,8 @@ PathItem.inject(new function() {
// Aggregate of all curves in both operands, monotonic in y. // Aggregate of all curves in both operands, monotonic in y.
monoCurves = [], monoCurves = [],
// Keep track if all encountered segments are overlaps. // Keep track if all encountered segments are overlaps.
overlapsOnly = true; overlapsOnly = true,
validOverlapsOnly = true;
function collect(paths) { function collect(paths) {
for (var i = 0, l = paths.length; i < l; i++) { for (var i = 0, l = paths.length; i < l; i++) {
@ -128,12 +132,19 @@ PathItem.inject(new function() {
// See if there are any valid segments that aren't part of overlaps. // See if there are any valid segments that aren't part of overlaps.
// This information is used further down to determine where to start // This information is used further down to determine where to start
// tracing the path, and how to treat encountered invalid segments. // tracing the path, and how to treat encountered invalid segments.
if (!(inter && inter._overlap) && operator[segment._winding]) if (!(inter && inter._overlap)) {
overlapsOnly = false; overlapsOnly = false;
if (operator[segment._winding])
validOverlapsOnly = false;
}
} }
return finishBoolean(CompoundPath, // If all encountered are overlaps (regardless if valid or not), we have
tracePaths(segments, operator, overlapsOnly), // two fully overlapping paths and can just return a clone of the first.
path1, path2, true); return finishResult(overlapsOnly
? path1.getArea() ? path1.clone() : new Path(Item.NO_INSERT)
: createResult(CompoundPath,
tracePaths(segments, operator, validOverlapsOnly),
!overlapsOnly), path1, path2);
} }
function computeOpenBoolean(path1, path2, operator) { function computeOpenBoolean(path1, path2, operator) {
@ -174,7 +185,7 @@ PathItem.inject(new function() {
} }
// At the end, check what's left from our path after all the splitting. // At the end, check what's left from our path after all the splitting.
addPath(_path1); addPath(_path1);
return finishBoolean(Group, paths, path1, path2); return finishResult(createResult(Group, paths), path1, path2);
} }
/* /*
@ -535,7 +546,7 @@ PathItem.inject(new function() {
* not * not
* @return {Path[]} the contours traced * @return {Path[]} the contours traced
*/ */
function tracePaths(segments, operator, overlapsOnly) { function tracePaths(segments, operator, validOverlapsOnly) {
var paths = [], var paths = [],
start, start,
otherStart; otherStart;
@ -601,7 +612,7 @@ PathItem.inject(new function() {
// already visited, or that are not going to be part of the result). // already visited, or that are not going to be part of the result).
// Also don't start in overlaps, unless all segments are part of // Also don't start in overlaps, unless all segments are part of
// overlaps, in which case we have no other choice. // overlaps, in which case we have no other choice.
if (!isValid(seg) || !overlapsOnly if (!isValid(seg) || !validOverlapsOnly
&& inter && seg._winding && inter._overlap) && inter && seg._winding && inter._overlap)
continue; continue;
start = otherStart = null; start = otherStart = null;
@ -640,7 +651,7 @@ PathItem.inject(new function() {
// If there are only valid overlap segments and we encounter // If there are only valid overlap segments and we encounter
// and invalid segment, bail out immediately. Otherwise we need // and invalid segment, bail out immediately. Otherwise we need
// to be more tolerant due to complex situations of crossing. // to be more tolerant due to complex situations of crossing.
if (overlapsOnly && !isValid(seg)) if (validOverlapsOnly && !isValid(seg))
break; break;
if (!path) { if (!path) {
path = new Path(Item.NO_INSERT); path = new Path(Item.NO_INSERT);
@ -754,9 +765,8 @@ PathItem.inject(new function() {
* @return {Group} the resulting group item * @return {Group} the resulting group item
*/ */
divide: function(path) { divide: function(path) {
return finishBoolean(Group, return finishResult(createResult(Group,
[this.subtract(path), this.intersect(path)], [this.subtract(path), this.intersect(path)], true), this, path);
this, path, true);
}, },
/* /*