Improve handling of overlap ambiguity in getIntersection()

Two passes are needed, first strict, then non-strict, to prevent 'better' next candiates over 'worse' ones.
This commit is contained in:
Jürg Lehni 2015-09-21 09:44:17 -04:00
parent 19a17b2918
commit f6f42fe09b

View file

@ -538,46 +538,63 @@ PathItem.inject(new function() {
// If there are multiple possible intersections, find the one // If there are multiple possible intersections, find the one
// that's either connecting back to start or is not visited yet, // that's either connecting back to start or is not visited yet,
// and will be part of the boolean result: // and will be part of the boolean result:
function getIntersection(inter, prev, ignoreOther) { function getIntersection(strict, inter, prev, ignoreOther) {
if (!inter) if (!inter)
return null; return null;
var seg = inter._segment, var seg = inter._segment,
next = seg.getNext(); next = seg.getNext();
if (window.reportSegments) { if (window.reportSegments) {
console.log('getIntersection()' console.log('getIntersection(' + strict + ')'
+ ', seg: ' + seg._path._id + '.' +seg._index + ', seg: ' + seg._path._id + '.' +seg._index
+ ', next: ' + next._path._id + '.' + next._index + ', next: ' + next._path._id + '.' + next._index
+ ', seg vis:' + !!seg._visited + ', seg vis:' + !!seg._visited
+ ', next vis:' + !!next._visited + ', next vis:' + !!next._visited
+ ', next start:' + (next === start + ', next start:' + (next === start
|| next === otherStart) || next === otherStart)
+ ', seg wi:' + seg._winding
+ ', next wi:' + next._winding
+ ', seg op:' + isValid(seg, true) + ', seg op:' + isValid(seg, true)
+ ', next op:' + isValid(next) + ', next op:' + isValid(next)
+ ', next: ' + (!!inter._next)); + ', seg ov: ' + (seg._intersection
&& seg._intersection._overlap)
+ ', next ov: ' + (next._intersection
&& next._intersection._overlap)
+ ', more: ' + (!!inter._next));
} }
// See if this segment and next are both not visited yet, or are // See if this segment and next are both not visited yet, or are
// bringing us back to the beginning, and are both part of the // bringing us back to the beginning, and are both part of the
// boolean result. // boolean result.
// Handling overlaps correctly here is a bit tricky business, and
// requires two passes, first with `strict = true`, then `false`:
// In strict mode, the current segment and the next segment are both
// checked for validity, and only the current one is allowed to be
// an overlap (passing true for `unadjusted` in isValid()). If this
// pass does not yield a result, the non-strict mode is used, in
// which invalid current segments are tolerated, and overlaps for
// the next segment are allowed as long as they are valid when not
// adjusted.
return !seg._visited && (!next._visited return !seg._visited && (!next._visited
|| next === start || next === otherStart) || next === start || next === otherStart)
&& (!operator // Self-intersection doesn't need isValid() calls && (!operator // Self-intersection doesn't need isValid() calls
// NOTE: We need to use the unadjusted winding here since an // NOTE: We need to use the unadjusted winding here since an
// overlap crossing might have brought us here, in which // overlap crossing might have brought us here, in which
// case isValid(seg, false) might be false. // case isValid(seg, false) might be false.
|| isValid(seg, true) && isValid(next)) || (!strict || isValid(seg, true))
&& isValid(next, !strict && inter._overlap))
? inter ? inter
// If it's no match, check the other intersection first, then // If it's no match, check the other intersection first, then
// carry on with the next linked intersection. // carry on with the next linked intersection.
: !ignoreOther : !ignoreOther
// We need to get the intersection on the segment, not // We need to get the intersection on the segment, not
// on inter, since they're only linked up through _next // on inter, since multiple solutions are only linked up
// there. But do not check that intersection in the // as a chain through _next there. But do not check that
// first call to getIntersection() (prev == null), since // intersection in the first call to getIntersection()
// we'd go back to the originating segment. // (prev == null), since we'd go straight back to the
// originating segment.
&& (prev || seg._intersection !== inter._intersection) && (prev || seg._intersection !== inter._intersection)
&& getIntersection(seg._intersection, inter, true) && getIntersection(strict, seg._intersection, inter, true)
|| inter._next !== prev // Prevent circular loops || inter._next !== prev // Prevent circular loops
&& getIntersection(inter._next, inter, false); && getIntersection(strict, inter._next, inter, false);
} }
for (var i = 0, l = segments.length; i < l; i++) { for (var i = 0, l = segments.length; i < l; i++) {
var seg = segments[i], var seg = segments[i],
@ -593,18 +610,25 @@ PathItem.inject(new function() {
// Once we started a chain, see if there are multiple // Once we started a chain, see if there are multiple
// intersections, and if so, pick the best one: // intersections, and if so, pick the best one:
if (inter && added && window.reportSegments) { if (inter && added && window.reportSegments) {
console.log('Before getIntersection(), seg: ' console.log('-----\n'
+ inter._segment._path._id + '.' +'#' + pathCount + '.'
+ inter._segment._index); + (path ? path._segments.length + 1 : 1)
+ ', Before getIntersection()'
+ ', seg: ' + seg._path._id + '.' + seg._index
+ ', other: ' + inter._segment._path._id + '.'
+ inter._segment._index);
} }
inter = added && getIntersection(inter) || inter; inter = added && (getIntersection(true, inter)
|| getIntersection(false, inter)) || inter;
var other = inter && inter._segment; var other = inter && inter._segment;
// A switched intersection means we may have changed the segment // A switched intersection means we may have changed the segment
// Point to the other segment in the selected intersection. // Point to the other segment in the selected intersection.
if (inter && added && window.reportSegments) { if (inter && added && window.reportSegments) {
console.log('After getIntersection(), seg: ' console.log('After getIntersection()'
+ inter._segment._path._id + '.' + ', seg: '
+ inter._segment._index); + seg._path._id + '.' + seg._index
+ ', other: ' + inter._segment._path._id + '.'
+ inter._segment._index);
} }
if (added && (seg === start || seg === otherStart)) { if (added && (seg === start || seg === otherStart)) {
// We've come back to the start, bail out as we're done. // We've come back to the start, bail out as we're done.