mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-03 19:45:44 -05:00
Improve new getWinding() direction flipping code.
This commit is contained in:
parent
eab0b9db5e
commit
27af304414
1 changed files with 58 additions and 51 deletions
|
@ -358,6 +358,8 @@ PathItem.inject(new function() {
|
||||||
* {@link CompoundPath#getCurves()}
|
* {@link CompoundPath#getCurves()}
|
||||||
* @param {Number} [dir=0] the direction in which to determine the
|
* @param {Number} [dir=0] the direction in which to determine the
|
||||||
* winding contribution, `0`: in x-direction, `1`: in y-direction
|
* winding contribution, `0`: in x-direction, `1`: in y-direction
|
||||||
|
* @param {Boolean} [dontFlip=false] controls whether the algorithm is
|
||||||
|
* allowed to flip direction if it is deemed to produce better results
|
||||||
* @return {Object} an object containing the calculated winding number, as
|
* @return {Object} an object containing the calculated winding number, as
|
||||||
* well as an indication whether the point was situated on the contour
|
* well as an indication whether the point was situated on the contour
|
||||||
* @private
|
* @private
|
||||||
|
@ -382,16 +384,15 @@ PathItem.inject(new function() {
|
||||||
onPath = false,
|
onPath = false,
|
||||||
roots = [],
|
roots = [],
|
||||||
vPrev,
|
vPrev,
|
||||||
vClose,
|
vClose;
|
||||||
result;
|
|
||||||
|
|
||||||
function addWinding(v) {
|
function addWinding(v) {
|
||||||
var o0 = v[io],
|
var o0 = v[io],
|
||||||
o3 = v[io + 6];
|
o3 = v[io + 6];
|
||||||
if (o0 > po && o3 > po || o0 < po && o3 < po) {
|
if (po < min(o0, o3) || po > max(o0, o3)) {
|
||||||
// If curve is outside the ordinates' range, no intersection
|
// If the curve is outside the ordinates' range, no intersection
|
||||||
// with the ray is possible.
|
// with the ray is possible.
|
||||||
return v;
|
return;
|
||||||
}
|
}
|
||||||
var a0 = v[ia],
|
var a0 = v[ia],
|
||||||
a1 = v[ia + 2],
|
a1 = v[ia + 2],
|
||||||
|
@ -409,16 +410,19 @@ PathItem.inject(new function() {
|
||||||
}
|
}
|
||||||
// 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.
|
||||||
return vPrev;
|
// Bail out without updating vPrev at the end of the call.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
var t = null,
|
var t = po === o0 ? 0
|
||||||
a = po === o0 ? a0
|
: po === o3 ? 1
|
||||||
: po === o3 ? a3
|
|
||||||
: paL > max(a0, a1, a2, a3) || paR < min(a0, a1, a2, a3)
|
: paL > max(a0, a1, a2, a3) || paR < min(a0, a1, a2, a3)
|
||||||
? (a0 + a3) / 2
|
? 0.5
|
||||||
: Curve.solveCubic(v, io, po, roots, 0, 1) === 1
|
: Curve.solveCubic(v, io, po, roots, 0, 1) === 1
|
||||||
? Curve.getPoint(v, t = roots[0])[dir ? 'y' : 'x']
|
? roots[0]
|
||||||
: (a0 + a3) / 2,
|
: 0.5,
|
||||||
|
a = t === 0 ? a0
|
||||||
|
: t === 1 ? a3
|
||||||
|
: Curve.getPoint(v, t)[dir ? 'y' : 'x'],
|
||||||
winding = o0 > o3 ? 1 : -1,
|
winding = o0 > o3 ? 1 : -1,
|
||||||
windingPrev = vPrev[io] > vPrev[io + 6] ? 1 : -1,
|
windingPrev = vPrev[io] > vPrev[io + 6] ? 1 : -1,
|
||||||
a3Prev = vPrev[ia + 6];
|
a3Prev = vPrev[ia + 6];
|
||||||
|
@ -435,7 +439,7 @@ PathItem.inject(new function() {
|
||||||
}
|
}
|
||||||
} else if (winding !== windingPrev) {
|
} else if (winding !== windingPrev) {
|
||||||
// Curve is crossed at starting point and winding changes from
|
// Curve is crossed at starting point and winding changes from
|
||||||
// previous. Cancel winding contribution from previous curve.
|
// previous curve. Cancel the winding from previous curve.
|
||||||
if (a3Prev < paR) {
|
if (a3Prev < paR) {
|
||||||
pathWindingL += winding;
|
pathWindingL += winding;
|
||||||
}
|
}
|
||||||
|
@ -454,15 +458,14 @@ PathItem.inject(new function() {
|
||||||
pathWindingL += winding;
|
pathWindingL += winding;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (onPath && !dontFlip) {
|
vPrev = v;
|
||||||
// If we're on the path, look at the tangent to determine if we
|
// If we're on the path, look at the tangent to decide whether to
|
||||||
// should flip direction to determine a reliable winding number.
|
// flip direction to determine a reliable winding number:
|
||||||
t = po === o0 ? 0 : po === o3 ? 1 : t;
|
// If the tangent is parallel to the direction, call getWinding()
|
||||||
result = t !== null
|
// again with flipped direction and return the result.
|
||||||
&& Curve.getTangent(v, t)[dir ? 'x' : 'y'] === 0
|
return onPath && !dontFlip
|
||||||
&& getWinding(point, curves, dir ? 0 : 1, true);
|
&& Curve.getTangent(v, t)[dir ? 'x' : 'y'] === 0
|
||||||
}
|
&& getWinding(point, curves, dir ? 0 : 1, true);
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCurve(v) {
|
function handleCurve(v) {
|
||||||
|
@ -482,17 +485,22 @@ PathItem.inject(new function() {
|
||||||
// abscissa, it can be treated as a monotone curve:
|
// abscissa, it can be treated as a monotone curve:
|
||||||
monoCurves = paL > max(a0, a1, a2, a3) ||
|
monoCurves = paL > max(a0, a1, a2, a3) ||
|
||||||
paR < min(a0, a1, a2, a3)
|
paR < min(a0, a1, a2, a3)
|
||||||
? [v] : Curve.getMonoCurves(v, dir);
|
? [v] : Curve.getMonoCurves(v, dir),
|
||||||
for (var i = 0, l = monoCurves.length; !result && i < l; i++) {
|
res;
|
||||||
vPrev = addWinding(monoCurves[i]);
|
for (var i = 0, l = monoCurves.length; i < l; i++) {
|
||||||
|
// Calling addWinding() my lead to direction flipping, in
|
||||||
|
// which case we already have the result and can return it.
|
||||||
|
if (res = addWinding(monoCurves[i]))
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0, l = curves.length; !result && i < l; i++) {
|
for (var i = 0, l = curves.length; i < l; i++) {
|
||||||
var curve = curves[i],
|
var curve = curves[i],
|
||||||
path = curve._path,
|
path = curve._path,
|
||||||
v = curve.getValues();
|
v = curve.getValues(),
|
||||||
|
res;
|
||||||
if (!i || curves[i - 1]._path !== path) {
|
if (!i || curves[i - 1]._path !== path) {
|
||||||
// We're on a new (sub-)path, so we need to determine values of
|
// We're on a new (sub-)path, so we need to determine values of
|
||||||
// the last non-horizontal curve on this path.
|
// the last non-horizontal curve on this path.
|
||||||
|
@ -529,16 +537,17 @@ PathItem.inject(new function() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCurve(v);
|
// Calling handleCurve() my lead to direction flipping, in which
|
||||||
|
// case we already have the result and can return it.
|
||||||
|
if (res = handleCurve(v))
|
||||||
|
return res;
|
||||||
|
|
||||||
if (!result && (i + 1 === l || curves[i + 1]._path !== path)) {
|
if (i + 1 === l || curves[i + 1]._path !== path) {
|
||||||
// We're at the last curve of the current (sub-)path. If a
|
// We're at the last curve of the current (sub-)path. If a
|
||||||
// closing curve was calculated at the beginning of it, handle
|
// closing curve was calculated at the beginning of it, handle
|
||||||
// it now to treat the path as closed:
|
// it now to treat the path as closed:
|
||||||
if (vClose) {
|
if (vClose && (res = handleCurve(vClose)))
|
||||||
handleCurve(vClose);
|
return res;
|
||||||
vClose = null;
|
|
||||||
}
|
|
||||||
if (!pathWindingL && !pathWindingR && onPath) {
|
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
|
||||||
|
@ -558,27 +567,25 @@ PathItem.inject(new function() {
|
||||||
if (onPath)
|
if (onPath)
|
||||||
onPathCount++;
|
onPathCount++;
|
||||||
onPath = false;
|
onPath = false;
|
||||||
|
vClose = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!result) {
|
if (!windingL && !windingR) {
|
||||||
if (!windingL && !windingR) {
|
windingL = windingR = onPathWinding;
|
||||||
windingL = windingR = onPathWinding;
|
|
||||||
}
|
|
||||||
windingL = windingL && (2 - abs(windingL) % 2);
|
|
||||||
windingR = windingR && (2 - abs(windingR) % 2);
|
|
||||||
// Return the calculated winding contribution and detect if we are
|
|
||||||
// on the contour of the area by comparing windingL and windingR.
|
|
||||||
// This is required when handling unite operations, where a winding
|
|
||||||
// number of 2 is not part of the result unless it's the contour:
|
|
||||||
result = {
|
|
||||||
winding: max(windingL, windingR),
|
|
||||||
windingL: windingL,
|
|
||||||
windingR: windingR,
|
|
||||||
onContour: !windingL ^ !windingR,
|
|
||||||
onPathCount: onPathCount
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return result;
|
windingL = windingL && (2 - abs(windingL) % 2);
|
||||||
|
windingR = windingR && (2 - abs(windingR) % 2);
|
||||||
|
// Return the calculated winding contribution and detect if we are
|
||||||
|
// on the contour of the area by comparing windingL and windingR.
|
||||||
|
// This is required when handling unite operations, where a winding
|
||||||
|
// number of 2 is not part of the result unless it's the contour:
|
||||||
|
return {
|
||||||
|
winding: max(windingL, windingR),
|
||||||
|
windingL: windingL,
|
||||||
|
windingR: windingR,
|
||||||
|
onContour: !windingL ^ !windingR,
|
||||||
|
onPathCount: onPathCount
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function propagateWinding(segment, path1, path2, curves, operator) {
|
function propagateWinding(segment, path1, path2, curves, operator) {
|
||||||
|
|
Loading…
Reference in a new issue