Boolean: Improve handling of branching at crossings.

This commit is contained in:
Jürg Lehni 2017-02-25 18:55:11 +01:00
parent 919c42af27
commit 86478b6873

View file

@ -765,6 +765,7 @@ PathItem.inject(new function() {
*/ */
function tracePaths(segments, operator) { function tracePaths(segments, operator) {
var paths = [], var paths = [],
branches = [],
starts; starts;
function isValid(seg) { function isValid(seg) {
@ -800,10 +801,10 @@ PathItem.inject(new function() {
// If there are multiple possible intersections, find the ones that's // If there are multiple possible intersections, find the ones that's
// either connecting back to start or are not visited yet, and will be // either connecting back to start or are not visited yet, and will be
// part of the boolean result: // part of the boolean result:
function getIntersections(segment, collectStarts) { function getCrossingSegments(segment, collectStarts) {
var inter = segment._intersection, var inter = segment._intersection,
start = inter, start = inter,
inters = []; crossings = [];
if (collectStarts) if (collectStarts)
starts = [segment]; starts = [segment];
@ -821,7 +822,7 @@ PathItem.inject(new function() {
// If the next segment isn't valid, its intersection // If the next segment isn't valid, its intersection
// to which we may switch might be, so check that. // to which we may switch might be, so check that.
|| nextInter && isValid(nextInter._segment))))) { || nextInter && isValid(nextInter._segment))))) {
inters.push(inter); crossings.push(other);
} }
if (collectStarts) if (collectStarts)
starts.push(other); starts.push(other);
@ -837,7 +838,7 @@ PathItem.inject(new function() {
inter = inter._prev; inter = inter._prev;
collect(inter, start); collect(inter, start);
} }
return inters; return crossings;
} }
// Sort segments to give non-ambiguous segments the preference as // Sort segments to give non-ambiguous segments the preference as
@ -873,7 +874,6 @@ PathItem.inject(new function() {
path = null, path = null,
finished = false, finished = false,
closed = true, closed = true,
branches = [],
branch, branch,
visited, visited,
handleIn; handleIn;
@ -897,12 +897,11 @@ PathItem.inject(new function() {
// visited, or that are not going to be part of the result). // visited, or that are not going to be part of the result).
while (valid) { while (valid) {
// For each segment we encounter, see if there are multiple // For each segment we encounter, see if there are multiple
// intersections, and if so, pick the best one: // crossings, and if so, pick the best one:
var first = !path, var first = !path,
intersections = getIntersections(seg, first), crossings = getCrossingSegments(seg, first),
inter = intersections.shift(), // Get the other segment of the first found crossing.
// Get the other segment on the intersection. other = crossings.shift(),
other = inter && inter._segment,
finished = !first && (isStart(seg) || isStart(other)), finished = !first && (isStart(seg) || isStart(other)),
cross = !finished && other; cross = !finished && other;
if (first) { if (first) {
@ -926,10 +925,13 @@ PathItem.inject(new function() {
branch = null; branch = null;
} }
if (!branch) { if (!branch) {
// Add the branch's root segment as the last segment to try,
// to see if we get to a solution without crossing.
if (cross)
crossings.push(seg);
branch = { branch = {
start: path._segments.length, start: path._segments.length,
segment: seg, crossings: crossings,
intersections: intersections,
visited: visited = [], visited: visited = [],
handleIn: handleIn handleIn: handleIn
}; };
@ -946,21 +948,26 @@ PathItem.inject(new function() {
for (var j = 0, k = visited.length; j < k; j++) { for (var j = 0, k = visited.length; j < k; j++) {
visited[j]._visited = false; visited[j]._visited = false;
} }
// Go back to the segment at which the crossing happened, visited.length = 0;
// and try other crossings first. // Go back to the branch's root segment at which the
if (inter = branch.intersections.shift()) { // crossing happened, and try other crossings.
seg = inter._segment; // NOTE: This also tries the root segment without crossing,
visited.length = 0; // because it is added to the list of crossings when the
} else { // branch is created above.
// If there are no crossings left, try not crossing: do {
// Restore the previous branch and keep adding to it, seg = branch && branch.crossings.shift();
// but stop once we run out of branches to try. if (!seg) {
if (!(branch = branches.pop()) || // If there are no segments left, try previous
!isValid(seg = branch.segment)) // branches until we find one that works.
break; branch = branches.pop();
visited = branch.visited; if (branch) {
} visited = branch.visited;
handleIn = branch.handleIn; handleIn = branch.handleIn;
}
}
} while (branch && !isValid(seg));
if (!seg)
break;
} }
// Add the segment to the path, and mark it as visited. // Add the segment to the path, and mark it as visited.
// But first we need to look ahead. If we encounter the end of // But first we need to look ahead. If we encounter the end of
@ -987,6 +994,8 @@ PathItem.inject(new function() {
if (path.getArea() !== 0) { if (path.getArea() !== 0) {
paths.push(path); paths.push(path);
} }
// Clear branches.
branches = [];
} }
} }
return paths; return paths;