First attempt of implementing new getWinding()

As described in #1285
This commit is contained in:
Jürg Lehni 2017-03-19 13:08:24 +01:00
parent a6933e5b2b
commit f6233a5028
3 changed files with 54 additions and 30 deletions

View file

@ -537,11 +537,12 @@ PathItem.inject(new function() {
qualityEpsilon = 1e-6, qualityEpsilon = 1e-6,
paL = pa - windingEpsilon, paL = pa - windingEpsilon,
paR = pa + windingEpsilon, paR = pa + windingEpsilon,
windingL = 0, winding,
windingR = 0, windings = [],
onPath = false, onPath = false,
quality = 1, quality = 1,
roots = [], roots = [],
prevOwner,
vPrev, vPrev,
vClose; vClose;
@ -586,15 +587,15 @@ PathItem.inject(new function() {
a = t === 0 ? a0 a = t === 0 ? a0
: t === 1 ? a3 : t === 1 ? a3
: Curve.getPoint(v, t)[dir ? 'y' : 'x'], : Curve.getPoint(v, t)[dir ? 'y' : 'x'],
winding = o0 > o3 ? 1 : -1, wind = o0 > o3 ? 1 : -1,
windingPrev = vPrev[io] > vPrev[io + 6] ? 1 : -1, windPrev = vPrev[io] > vPrev[io + 6] ? 1 : -1,
a3Prev = vPrev[ia + 6]; a3Prev = vPrev[ia + 6];
if (po !== o0) { if (po !== o0) {
// Standard case, curve is not crossed at its starting point. // Standard case, curve is not crossed at its starting point.
if (a < paL) { if (a < paL) {
windingL += winding; winding.l += wind;
} else if (a > paR) { } else if (a > paR) {
windingR += winding; winding.r += wind;
} else { } else {
onPath = true; onPath = true;
} }
@ -605,13 +606,13 @@ PathItem.inject(new function() {
if (a > pa - qualityEpsilon && a < pa + qualityEpsilon) if (a > pa - qualityEpsilon && a < pa + qualityEpsilon)
quality /= 2; quality /= 2;
} else { } else {
if (winding !== windingPrev) { if (wind !== windPrev) {
// Curve is crossed at starting point and winding changes // Curve is crossed at starting point and winding changes
// from previous curve. Cancel winding from previous curve. // from previous curve. Cancel winding from previous curve.
if (a0 < paL) { if (a0 < paL) {
windingL += winding; winding.l += wind;
} else if (a0 > paR) { } else if (a0 > paR) {
windingR += winding; winding.r += wind;
} }
} else if (a0 != a3Prev) { } else if (a0 != a3Prev) {
// Handle a horizontal curve between the current and // Handle a horizontal curve between the current and
@ -619,11 +620,11 @@ PathItem.inject(new function() {
// #1261#issuecomment-282726147 for a detailed explanation: // #1261#issuecomment-282726147 for a detailed explanation:
if (a3Prev < paR && a > paR) { if (a3Prev < paR && a > paR) {
// Right winding was not added before, so add it now. // Right winding was not added before, so add it now.
windingR += winding; winding.r += wind;
onPath = true; onPath = true;
} else if (a3Prev > paL && a < paL) { } else if (a3Prev > paL && a < paL) {
// Left winding was not added before, so add it now. // Left winding was not added before, so add it now.
windingL += winding; winding.l += wind;
onPath = true; onPath = true;
} }
} }
@ -675,6 +676,14 @@ PathItem.inject(new function() {
v = curve.getValues(), v = curve.getValues(),
res; res;
if (!i || curves[i - 1]._path !== path) { if (!i || curves[i - 1]._path !== path) {
var parent = path._parent,
owner = parent instanceof CompoundPath ? parent : path;
if (owner !== prevOwner) {
windings.push(winding = {
l: 0, r: 0, rule: owner.getFillRule()
});
prevOwner = owner;
}
// 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.
vPrev = null; vPrev = null;
@ -729,8 +738,27 @@ PathItem.inject(new function() {
} }
// Use the unsigned winding contributions when determining which areas // Use the unsigned winding contributions when determining which areas
// are part of the boolean result. // are part of the boolean result.
var windingL = 0,
windingR = 0;
for (var i = 0; i < windings.length; i++) {
var winding = windings[i],
l = winding.l,
r = winding.r;
if (winding.rule === 'evenodd') {
l = l & 1;
r = r & 1;
} else {
l = l ? 1 : 0;
r = r ? 1 : 0;
// l = l < 0 ? -1 : l > 0 ? 1 : 0;
// r = r < 0 ? -1 : r > 0 ? 1 : 0;
}
windingL += l;
windingR += r;
}
windingL = abs(windingL); windingL = abs(windingL);
windingR = abs(windingR); windingR = abs(windingR);
console.log(JSON.stringify(windings), windingL, windingR);
// Return the calculated winding contributions along with a quality // Return the calculated winding contributions along with a quality
// value indicating how reliable the value really is. // value indicating how reliable the value really is.
return { return {
@ -775,6 +803,7 @@ PathItem.inject(new function() {
var curve = entry.curve, var curve = entry.curve,
path = curve._path, path = curve._path,
parent = path._parent, parent = path._parent,
operand = parent instanceof CompoundPath ? parent : path,
t = Numerical.clamp(curve.getTimeAt(length), tMin, tMax), t = Numerical.clamp(curve.getTimeAt(length), tMin, tMax),
pt = curve.getPointAtTime(t), pt = curve.getPointAtTime(t),
// Determine the direction in which to check the winding // Determine the direction in which to check the winding
@ -783,15 +812,13 @@ PathItem.inject(new function() {
// than 45°, cast the ray vertically, else horizontally. // than 45°, cast the ray vertically, else horizontally.
dir = abs(curve.getTangentAtTime(t).normalize().y) dir = abs(curve.getTangentAtTime(t).normalize().y)
< Math.SQRT1_2 ? 1 : 0; < Math.SQRT1_2 ? 1 : 0;
if (parent instanceof CompoundPath)
path = parent;
// While subtracting, we need to omit this curve if it is // While subtracting, we need to omit this curve if it is
// contributing to the second operand and is outside the // contributing to the second operand and is outside the
// first operand. // first operand.
var wind = !(operator.subtract && path2 && ( var wind = !(operator.subtract && path2 && (
path === path1 && operand === path1 &&
path2._getWinding(pt, dir, true).winding || path2._getWinding(pt, dir, true).winding ||
path === path2 && operand === path2 &&
!path1._getWinding(pt, dir, true).winding)) !path1._getWinding(pt, dir, true).winding))
? getWinding(pt, curves, dir, true) ? getWinding(pt, curves, dir, true)
: windingZero; : windingZero;

View file

@ -275,9 +275,7 @@ var PathItem = Item.extend(/** @lends PathItem# */{
: {}; : {};
// See #1116#issuecomment-243794824 for an explanation of the // See #1116#issuecomment-243794824 for an explanation of the
// winding.onPath check here. // winding.onPath check here.
return winding.onPath || !!(this.getFillRule() === 'evenodd' return !!(winding.winding || winding.onPath);
? winding.windingL & 1 || winding.windingR & 1
: winding.winding);
/*#*/ } // !__options.nativeContains && __options.booleanOperations /*#*/ } // !__options.nativeContains && __options.booleanOperations
}, },

View file

@ -405,20 +405,19 @@ test('#870', function() {
}); });
test('#875', function() { test('#875', function() {
var p1 = new Path({ var path1 = new Path.Circle({
segments: [ center: [165, 400],
[158.7, 389.3, 0, 0, -4.95, 4.95], radius: 15,
[158.7, 407.2, -4.95, -4.95, 4.95, 4.95], clockwise: false
[176.6, 407.2, -4.95, 4.95, 4.95, -4.95], });
[176.6, 389.3, 4.95, 4.95, -4.95, -4.95], var path2 = new Path.Circle({
[158.7, 389.3, 4.95, -4.95, 0, 0] center: [260, 320],
], radius: 100,
closed: true clockwise: true
}); });
var p2 = new Path.Circle(260, 320, 100);
compareBoolean(function() { return p1.subtract(p2); }, compareBoolean(function() { return path1.subtract(path2); },
'M158.7,407.2c4.95,4.95 12.95,4.95 17.9,0c4.95,-4.95 4.95,-12.95 0,-17.9c-4.95,-4.95 -12.95,-4.95 -17.9,0c-4.95,4.95 -4.95,12.95 0,17.9z'); 'M165,415c8.28427,0 15,-6.71573 15,-15c0,-8.28427 -6.71573,-15 -15,-15c-8.28427,0 -15,6.71573 -15,15c0,8.28427 6.71573,15 15,15z');
}); });
test('#877', function() { test('#877', function() {