Boolean: Improve handling of multiple crossings on the same curve.

This commit is contained in:
Jürg Lehni 2017-02-21 22:05:38 +01:00
parent 357ff0dd43
commit ed57b82b19
4 changed files with 35 additions and 33 deletions

View file

@ -89,10 +89,10 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
*/ */
getSegment: function() { getSegment: function() {
// Request curve first, so _segment gets invalidated if it's out of sync // Request curve first, so _segment gets invalidated if it's out of sync
var curve = this.getCurve(), var segment = this._segment;
segment = this._segment;
if (!segment) { if (!segment) {
var time = this.getTime(); var curve = this.getCurve(),
time = this.getTime();
if (time === 0) { if (time === 0) {
segment = curve._segment1; segment = curve._segment1;
} else if (time === 1) { } else if (time === 1) {
@ -119,10 +119,9 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
var path = this._path, var path = this._path,
that = this; that = this;
if (path && path._version !== this._version) { if (path && path._version !== this._version) {
// If the path's segments have changed in the meantime, clear the // If the path's segments have changed, clear the cached time and
// internal _time value and force re-fetching of the correct // offset values and force re-fetching of the correct curve.
// curve again here. this._time = this._offset = this._curve = null;
this._time = this._curve = this._offset = null;
} }
// If path is out of sync, access current curve objects through segment1 // If path is out of sync, access current curve objects through segment1
@ -134,7 +133,6 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
if (curve && (that._time = curve.getTimeOf(that._point)) != null) { if (curve && (that._time = curve.getTimeOf(that._point)) != null) {
// Fetch path again as it could be on a new one through split() // Fetch path again as it could be on a new one through split()
that._setCurve(curve); that._setCurve(curve);
that._segment = segment;
return curve; return curve;
} }
} }
@ -142,7 +140,6 @@ var CurveLocation = Base.extend(/** @lends CurveLocation# */{
return this._curve return this._curve
|| trySegment(this._segment) || trySegment(this._segment)
|| trySegment(this._segment1) || trySegment(this._segment1)
|| trySegment(this._segment1.getNext())
|| trySegment(this._segment2.getPrevious()); || trySegment(this._segment2.getPrevious());
}, },

View file

@ -1199,10 +1199,9 @@ var Path = PathItem.extend(/** @lends Path# */{
this._add([segments[0]]); this._add([segments[0]]);
path.remove(); path.remove();
} }
// Close the resulting path and merge first and last segment if they // If the first and last segment touch, close the resulting path and
// touch, meaning the touched at path ends. Also do this if no path // merge the end segments. Also do this if no path argument was provided
// argument was provided, in which cases the path is joined with itself // in which cases the path is joined with itself only if its ends touch.
// only if its ends touch.
var first = this.getFirstSegment(), var first = this.getFirstSegment(),
last = this.getLastSegment(); last = this.getLastSegment();
if (first !== last && first._point.isClose(last._point, epsilon)) { if (first !== last && first._point.isClose(last._point, epsilon)) {

View file

@ -148,7 +148,10 @@ PathItem.inject(new function() {
} else { } else {
// When there are no crossings, the result can be determined through // When there are no crossings, the result can be determined through
// a much faster call to reorientPaths(): // a much faster call to reorientPaths():
paths = reorientPaths(paths2 ? paths1.concat(paths2) : paths1, paths = reorientPaths(
// Make sure reorientPaths() never works on original
// _children arrays by calling paths1.slice()
paths2 ? paths1.concat(paths2) : paths1.slice(),
function(w) { function(w) {
return !!operator[w]; return !!operator[w];
}); });
@ -357,6 +360,7 @@ PathItem.inject(new function() {
// be changed to the scaled value after splitting previously. // be changed to the scaled value after splitting previously.
// See CurveLocation#getCurve(), #resolveCrossings() // See CurveLocation#getCurve(), #resolveCrossings()
time = loc._time, time = loc._time,
origTime = time,
exclude = include && !include(loc), exclude = include && !include(loc),
// Retrieve curve after calling include(), because it may cause // Retrieve curve after calling include(), because it may cause
// a change in the cached location values, see above. // a change in the cached location values, see above.
@ -371,12 +375,12 @@ PathItem.inject(new function() {
// renormalization within the curve. // renormalization within the curve.
renormalizeLocs = []; renormalizeLocs = [];
prevTime = null; prevTime = null;
prevCurve = curve;
} else if (prevTime >= tMin) { } else if (prevTime >= tMin) {
// Rescale curve-time when we are splitting the same curve // Rescale curve-time when we are splitting the same curve
// multiple times, if splitting was done previously. // multiple times, if splitting was done previously.
loc._time /= prevTime; time /= prevTime;
} }
prevCurve = curve;
} }
if (exclude) { if (exclude) {
// Store excluded locations for later renormalization, in case // Store excluded locations for later renormalization, in case
@ -387,8 +391,7 @@ PathItem.inject(new function() {
} else if (include) { } else if (include) {
results.unshift(loc); results.unshift(loc);
} }
prevTime = time; prevTime = origTime;
time = loc._time;
if (time < tMin) { if (time < tMin) {
segment = curve._segment1; segment = curve._segment1;
} else if (time > tMax) { } else if (time > tMax) {
@ -1121,7 +1124,7 @@ PathItem.inject(new function() {
var hasOverlaps = false, var hasOverlaps = false,
hasCrossings = false, hasCrossings = false,
intersections = this.getIntersections(null, function(inter) { intersections = this.getIntersections(null, function(inter) {
return inter._overlap && (hasOverlaps = true) || return inter.hasOverlap() && (hasOverlaps = true) ||
inter.isCrossing() && (hasCrossings = true); inter.isCrossing() && (hasCrossings = true);
}), }),
// We only need to keep track of curves that need clearing // We only need to keep track of curves that need clearing
@ -1132,7 +1135,7 @@ PathItem.inject(new function() {
// First divide in all overlaps, and then remove the inside of // First divide in all overlaps, and then remove the inside of
// the resulting overlap ranges. // the resulting overlap ranges.
var overlaps = divideLocations(intersections, function(inter) { var overlaps = divideLocations(intersections, function(inter) {
return inter._overlap; return inter.hasOverlap();
}, clearCurves); }, clearCurves);
for (var i = overlaps.length - 1; i >= 0; i--) { for (var i = overlaps.length - 1; i >= 0; i--) {
var seg = overlaps[i]._segment, var seg = overlaps[i]._segment,
@ -1160,20 +1163,23 @@ PathItem.inject(new function() {
// Check both involved curves to see if they're still valid, // Check both involved curves to see if they're still valid,
// meaning they are still part of their paths. // meaning they are still part of their paths.
var curve1 = inter.getCurve(), var curve1 = inter.getCurve(),
// Do not call getCurve() on the other intersection yet, seg1 = inter.getSegment(),
// as it too is in the intersections array and will be // Do not call getCurve() and getSegment() on the other
// divided later. But do check if its current curve is // intersection yet, as it too is in the intersections
// still valid. This is required by some very rare edge // array and will be divided later. But check if its
// current curve is valid, as required by some rare edge
// cases, related to intersections on the same curve. // cases, related to intersections on the same curve.
curve2 = inter._intersection._curve, other = inter._intersection,
seg = inter._segment; curve2 = other._curve,
if (curve1 && curve2 && curve1._path && curve2._path) { seg2 = other._segment;
if (curve1 && curve2 && curve1._path && curve2._path)
return true; return true;
} else if (seg) { // Remove all intersections that were involved in the
// Remove all intersections that were involved in the // handling of overlaps, to not confuse tracePaths().
// handling of overlaps, to not confuse tracePaths(). if (seg1)
seg._intersection = null; seg1._intersection = null;
} if (seg2)
seg2._intersection = null;
}, clearCurves); }, clearCurves);
if (clearCurves) if (clearCurves)
clearCurveHandles(clearCurves); clearCurveHandles(clearCurves);

View file

@ -355,7 +355,7 @@ var PathItem = Item.extend(/** @lends PathItem# */{
// as it should in the known use cases. // as it should in the known use cases.
// The ideal implementation would deal with it in a way outlined in: // The ideal implementation would deal with it in a way outlined in:
// https://github.com/paperjs/paper.js/issues/874#issuecomment-168332391 // https://github.com/paperjs/paper.js/issues/874#issuecomment-168332391
return inter._overlap || inter.isCrossing(); return inter.hasOverlap() || inter.isCrossing();
}); });
}, },