mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-03 19:45:44 -05:00
Boolean: Return the full winding object from _getWinding() and use it to improve PathItem#contains() with even-odd full-rule.
Also store the full winding object on the processed segments, to have more information available in boolean operations.
This commit is contained in:
parent
23f3097f84
commit
cbe41c536e
3 changed files with 27 additions and 19 deletions
|
@ -176,7 +176,7 @@ PathItem.inject(new function() {
|
||||||
path._overlapsOnly = false;
|
path._overlapsOnly = false;
|
||||||
// This is no overlap. If it is valid, take note that this
|
// This is no overlap. If it is valid, take note that this
|
||||||
// path contains valid intersections other than overlaps.
|
// path contains valid intersections other than overlaps.
|
||||||
if (operator[segment._winding])
|
if (operator[segment._winding.winding])
|
||||||
path._validOverlapsOnly = false;
|
path._validOverlapsOnly = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -378,7 +378,8 @@ PathItem.inject(new function() {
|
||||||
pathWindingL = 0,
|
pathWindingL = 0,
|
||||||
pathWindingR = 0,
|
pathWindingR = 0,
|
||||||
onPathWinding = 0,
|
onPathWinding = 0,
|
||||||
isOnPath = false,
|
onPathCount = 0,
|
||||||
|
onPath = false,
|
||||||
vPrev,
|
vPrev,
|
||||||
vClose;
|
vClose;
|
||||||
|
|
||||||
|
@ -402,7 +403,7 @@ PathItem.inject(new function() {
|
||||||
// +----+ |
|
// +----+ |
|
||||||
// +-----+
|
// +-----+
|
||||||
if (a1 < paR && a3 > paL || a3 < paR && a1 > paL) {
|
if (a1 < paR && a3 > paL || a3 < paR && a1 > paL) {
|
||||||
isOnPath = true;
|
onPath = true;
|
||||||
}
|
}
|
||||||
// If curve does not change in ordinate direction, windings will
|
// If curve does not change in ordinate direction, windings will
|
||||||
// be added by adjacent curves.
|
// be added by adjacent curves.
|
||||||
|
@ -426,7 +427,7 @@ PathItem.inject(new function() {
|
||||||
} else if (a > paR) {
|
} else if (a > paR) {
|
||||||
pathWindingR += winding;
|
pathWindingR += winding;
|
||||||
} else {
|
} else {
|
||||||
isOnPath = true;
|
onPath = true;
|
||||||
pathWindingL += winding;
|
pathWindingL += winding;
|
||||||
pathWindingR += winding;
|
pathWindingR += winding;
|
||||||
}
|
}
|
||||||
|
@ -442,7 +443,7 @@ PathItem.inject(new function() {
|
||||||
} else if (a3Prev < paL && a > paL || a3Prev > paR && a < paR) {
|
} else if (a3Prev < paL && a > paL || a3Prev > paR && a < paR) {
|
||||||
// Point is on a horizontal curve between the previous non-
|
// Point is on a horizontal curve between the previous non-
|
||||||
// horizontal and the current curve.
|
// horizontal and the current curve.
|
||||||
isOnPath = true;
|
onPath = true;
|
||||||
if (a3Prev < paL) {
|
if (a3Prev < paL) {
|
||||||
// left winding was added before, now add right winding.
|
// left winding was added before, now add right winding.
|
||||||
pathWindingR += winding;
|
pathWindingR += winding;
|
||||||
|
@ -528,7 +529,7 @@ PathItem.inject(new function() {
|
||||||
handleCurve(vClose);
|
handleCurve(vClose);
|
||||||
vClose = null;
|
vClose = null;
|
||||||
}
|
}
|
||||||
if (!pathWindingL && !pathWindingR && isOnPath) {
|
if (!pathWindingL && !pathWindingR && onPath) {
|
||||||
// If the point is on the path and the windings canceled
|
// If the point is on the path and the windings canceled
|
||||||
// each other, we treat the point as if it was inside the
|
// each other, we treat the point as if it was inside the
|
||||||
// path. A point inside a path has a winding of [+1,-1]
|
// path. A point inside a path has a winding of [+1,-1]
|
||||||
|
@ -544,7 +545,9 @@ PathItem.inject(new function() {
|
||||||
windingR += pathWindingR;
|
windingR += pathWindingR;
|
||||||
pathWindingL = pathWindingR = 0;
|
pathWindingL = pathWindingR = 0;
|
||||||
}
|
}
|
||||||
isOnPath = false;
|
if (onPath)
|
||||||
|
onPathCount++;
|
||||||
|
onPath = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!windingL && !windingR) {
|
if (!windingL && !windingR) {
|
||||||
|
@ -558,7 +561,10 @@ PathItem.inject(new function() {
|
||||||
// contribution of 2 is not part of the result unless it's the contour:
|
// contribution of 2 is not part of the result unless it's the contour:
|
||||||
return {
|
return {
|
||||||
winding: max(windingL, windingR),
|
winding: max(windingL, windingR),
|
||||||
contour: !windingL ^ !windingR
|
windingL: windingL,
|
||||||
|
windingR: windingR,
|
||||||
|
onContour: !windingL ^ !windingR,
|
||||||
|
onPathCount: onPathCount
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,8 +606,8 @@ PathItem.inject(new function() {
|
||||||
// contributing to the second operand and is outside the
|
// contributing to the second operand and is outside the
|
||||||
// first operand.
|
// first operand.
|
||||||
winding = !(operator.subtract && path2 && (
|
winding = !(operator.subtract && path2 && (
|
||||||
path === path1 && path2._getWinding(pt, dir) ||
|
path === path1 && path2._getWinding(pt, dir).winding ||
|
||||||
path === path2 && !path1._getWinding(pt, dir)))
|
path === path2 && !path1._getWinding(pt, dir).winding))
|
||||||
? getWinding(pt, curves, dir)
|
? getWinding(pt, curves, dir)
|
||||||
: { winding: 0 };
|
: { winding: 0 };
|
||||||
break;
|
break;
|
||||||
|
@ -610,9 +616,7 @@ PathItem.inject(new function() {
|
||||||
}
|
}
|
||||||
// Now assign the winding to the entire curve chain.
|
// Now assign the winding to the entire curve chain.
|
||||||
for (var j = chain.length - 1; j >= 0; j--) {
|
for (var j = chain.length - 1; j >= 0; j--) {
|
||||||
var seg = chain[j].segment;
|
chain[j].segment._winding = winding;
|
||||||
seg._winding = winding.winding;
|
|
||||||
seg._contour = winding.contour;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,9 +641,10 @@ PathItem.inject(new function() {
|
||||||
// also part of the contour of the result. Such segments are not
|
// also part of the contour of the result. Such segments are not
|
||||||
// chosen as the start of new paths and are not always counted as a
|
// chosen as the start of new paths and are not always counted as a
|
||||||
// valid next step, as controlled by the excludeContour parameter.
|
// valid next step, as controlled by the excludeContour parameter.
|
||||||
|
var winding;
|
||||||
return !!(seg && !seg._visited && (!operator
|
return !!(seg && !seg._visited && (!operator
|
||||||
|| operator[seg._winding]
|
|| operator[(winding = seg._winding).winding]
|
||||||
|| !excludeContour && operator.unite && seg._contour));
|
|| !excludeContour && operator.unite && winding.onContour));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isStart(seg) {
|
function isStart(seg) {
|
||||||
|
@ -849,7 +854,7 @@ PathItem.inject(new function() {
|
||||||
* @return {Number} the winding number
|
* @return {Number} the winding number
|
||||||
*/
|
*/
|
||||||
_getWinding: function(point, dir) {
|
_getWinding: function(point, dir) {
|
||||||
return getWinding(point, this.getCurves(), dir).winding;
|
return getWinding(point, this.getCurves(), dir);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -265,8 +265,11 @@ var PathItem = Item.extend(/** @lends PathItem# */{
|
||||||
// for a quick check before calculating the actual winding.
|
// for a quick check before calculating the actual winding.
|
||||||
var winding = point.isInside(
|
var winding = point.isInside(
|
||||||
this.getBounds({ internal: true, handle: true }))
|
this.getBounds({ internal: true, handle: true }))
|
||||||
&& this._getWinding(point);
|
? this._getWinding(point)
|
||||||
return !!(this.getFillRule() === 'evenodd' ? winding & 1 : winding);
|
: {};
|
||||||
|
return !!(this.getFillRule() === 'evenodd'
|
||||||
|
? winding.windingL & 1 || winding.windingR & 1
|
||||||
|
: winding.winding);
|
||||||
/*#*/ } // !__options.nativeContains && __options.booleanOperations
|
/*#*/ } // !__options.nativeContains && __options.booleanOperations
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ test('CompoundPath#contains() (donut)', function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
path.fillRule = 'evenodd';
|
path.fillRule = 'evenodd';
|
||||||
// testDonut(path, '\'evenodd\'');
|
testDonut(path, '\'evenodd\'');
|
||||||
path.reorient();
|
path.reorient();
|
||||||
testDonut(path, '\'evenodd\' + reorient()');
|
testDonut(path, '\'evenodd\' + reorient()');
|
||||||
path.fillRule = 'nonzero';
|
path.fillRule = 'nonzero';
|
||||||
|
|
Loading…
Reference in a new issue