Move addWinding() into getWinding()

Allowing the reuse of a whole lot of shared variables and leading to some nice simplifications.
This commit is contained in:
Jürg Lehni 2016-07-14 18:53:23 +02:00
parent cad2858070
commit 7da70181cc

View file

@ -295,134 +295,117 @@ PathItem.inject(new function() {
return results || locations; return results || locations;
} }
/** function getWinding(point, curves, horizontal) {
* Adds the winding contribution of a curve to the already found windings.
* The curve does not have to be a monotone curve.
*
* @param v the values of the curve
* @param prevV the values of the previous curve
* @param px x coordinate of the point to be examined
* @param py y coordinate of the point to be examined
* @param windings an array of length 2, windings[0] contains the winding
* number to the left, windings[1] to the right
* @param isOnCurve
* @param coord the coordinate direction of the cast ray (0 = x, 1 = y)
*/
function addWinding(v, prevV, px, py, windings, isOnCurve, coord) {
var epsilon = /*#=*/Numerical.WINDING_EPSILON, var epsilon = /*#=*/Numerical.WINDING_EPSILON,
pa = coord ? py : px, // point's abscissa abs = Math.abs,
po = coord ? px : py, // point's ordinate windingL = 0,
vo0 = v[1 - coord], windingR = 0,
vo3 = v[7 - coord]; pathWindingL = 0,
if (vo0 > po && vo3 > po || pathWindingR = 0,
vo0 < po && vo3 < po) { onPathWinding = 0,
// If curve is outside the ordinates' range, no intersection with isOnPath = false,
// the ray is possible. prevV,
return v; coord = horizontal ? 1 : 0,
} po = horizontal ? point.x : point.y, // point's abscissa
var aBefore = pa - epsilon, pa = horizontal ? point.y : point.x, // point's ordinate
aAfter = pa + epsilon, aBefore = pa - epsilon,
va0 = v[coord], aAfter = pa + epsilon;
va1 = v[2 + coord],
va2 = v[4 + coord], function addWinding(v) {
va3 = v[6 + coord]; var vo0 = v[1 - coord],
if (vo0 === vo3) { vo3 = v[7 - coord];
// A horizontal curve is not necessarily between two non-horizontal if (vo0 > po && vo3 > po ||
// curves. We have to take cases like these into account: vo0 < po && vo3 < po) {
// +-----+ // If curve is outside the ordinates' range, no intersection
// ----+ | // with the ray is possible.
// +-----+ return v;
if (va1 <= aAfter && va3 >= aBefore ||
va3 <= aAfter && va1 >= aBefore) {
isOnCurve[0] = true;
} }
// If curve does not change in ordinate direction, windings will be var va0 = v[coord],
// added by adjacent curves. va1 = v[2 + coord],
return prevV; va2 = v[4 + coord],
} va3 = v[6 + coord];
var roots = [], if (vo0 === vo3) {
a = po === vo0 ? va0 // A horizontal curve is not necessarily between two non-
: po === vo3 ? va3 // horizontal curves. We have to take cases like these into
: ( va0 < aBefore && va1 < aBefore && // account:
va2 < aBefore && va3 < aBefore) || // +-----+
( va0 > aAfter && va1 > aAfter && // ----+ |
va2 > aAfter && va3 > aAfter) // +-----+
if (va1 <= aAfter && va3 >= aBefore ||
va3 <= aAfter && va1 >= aBefore) {
isOnPath = true;
}
// If curve does not change in ordinate direction, windings will
// be added by adjacent curves.
return prevV;
}
var roots = [],
a = po === vo0 ? va0
: po === vo3 ? va3
: ( va0 < aBefore && va1 < aBefore &&
va2 < aBefore && va3 < aBefore) ||
( va0 > aAfter && va1 > aAfter &&
va2 > aAfter && va3 > aAfter)
? (va0 + va3) / 2 ? (va0 + va3) / 2
: Curve.solveCubic(v, coord ? 0 : 1, po, roots, 0, 1) === 1 : Curve.solveCubic(v, coord ? 0 : 1, po, roots, 0, 1) === 1
? Curve.getPoint(v, roots[0])[coord ? 'y' : 'x'] ? Curve.getPoint(v, roots[0])[coord ? 'y' : 'x']
: (va0 + va3) / 2; : (va0 + va3) / 2;
var winding = vo0 > vo3 ? 1 : -1, var winding = vo0 > vo3 ? 1 : -1,
prevWinding = prevV[1 - coord] > prevV[7 - coord] ? 1 : -1, prevWinding = prevV[1 - coord] > prevV[7 - coord] ? 1 : -1,
prevAEnd = prevV[6 + coord]; prevAEnd = prevV[6 + coord];
if (po !== vo0) { if (po !== vo0) {
// Standard case, curve is crossed by not at it's start point // Standard case, curve is crossed by not at its start point.
if (a < aBefore) { if (a < aBefore) {
windings[0] += winding; pathWindingL += winding;
} else if (a > aAfter) { } else if (a > aAfter) {
windings[1] += winding; pathWindingR += winding;
} else { } else {
isOnCurve[0] = true; isOnPath = true;
windings[0] += winding; pathWindingL += winding;
windings[1] += winding; pathWindingR += winding;
} }
} else if (winding !== prevWinding) { } else if (winding !== prevWinding) {
// Curve is crossed at start point and winding changes from // Curve is crossed at start point and winding changes from
// previous. Cancel winding contribution from previous curve // previous. Cancel winding contribution from previous curve.
if (prevAEnd <= aAfter) { if (prevAEnd <= aAfter) {
windings[0] += winding; pathWindingL += winding;
} }
if (prevAEnd >= aBefore) { if (prevAEnd >= aBefore) {
windings[1] += winding; pathWindingR += winding;
} }
} else if (prevAEnd < aBefore && a >= aBefore } else if (prevAEnd < aBefore && a >= aBefore
|| prevAEnd > aAfter && a <= aAfter) { || prevAEnd > aAfter && a <= aAfter) {
// Point is on a horizontal curve between previous non-horizontal // Point is on a horizontal curve between the previous non-
// and current curve // horizontal and the current curve.
isOnCurve[0] = true; isOnPath = true;
if (prevAEnd < aBefore) { if (prevAEnd < aBefore) {
// left winding was added before, now add right winding // left winding was added before, now add right winding
windings[1] += winding; pathWindingR += winding;
} else if (prevAEnd > aAfter) { } else if (prevAEnd > aAfter) {
// right winding was added before, not add left winding // right winding was added before, not add left winding.
windings[0] += winding; pathWindingL += winding;
}
} }
return v;
} }
return v;
}
function getWinding(point, curves, horizontal) {
var epsilon = /*#=*/Numerical.WINDING_EPSILON,
abs = Math.abs,
windings = [0, 0], // left, right winding
pathWindings = [0, 0],
onPathWinding = 0,
isOnPath = [false],
prevV,
coord = horizontal ? 1 : 0,
po = horizontal ? point.x : point.y,
pa = horizontal ? point.y : point.x,
aBefore = pa - epsilon,
aAfter = pa + epsilon;
for (var i = 0, l = curves.length; i < l; i++) { for (var i = 0, l = curves.length; i < l; i++) {
var curve = curves[i]; var curve = curves[i],
var path = curve.getPath(); path = curve.getPath();
if (i === 0 || curves[i - 1].getPath() !== path) { if (i === 0 || curves[i - 1].getPath() !== path) {
// On new path, determine values of last non-horizontal curve. // On new path, determine values of last non-horizontal curve.
prevV = null; prevV = null;
var curvePrev = curve.getPrevious(); var curvePrev = curve.getPrevious();
while (!prevV && curvePrev && curvePrev != curve) { while (!prevV && curvePrev && curvePrev != curve) {
var v2 = curvePrev.getValues(); var v2 = curvePrev.getValues();
if (v2[1 - coord] != v2[7 - coord]) { if (v2[1 - coord] != v2[7 - coord])
prevV = v2; prevV = v2;
}
curvePrev = curvePrev.getPrevious(); curvePrev = curvePrev.getPrevious();
} }
if (!prevV) { prevV = prevV || curve.getValues();
prevV = curve.getValues();
}
} }
var v = curve.getValues(), var v = curve.getValues(),
// Get the other coordinate values (x -> y, y -> x): // Get the ordinates:
vo0 = v[1 - coord], vo0 = v[1 - coord],
vo1 = v[3 - coord], vo1 = v[3 - coord],
vo2 = v[5 - coord], vo2 = v[5 - coord],
@ -442,40 +425,38 @@ PathItem.inject(new function() {
va2 > aAfter && va3 > aAfter) va2 > aAfter && va3 > aAfter)
? [v] : Curve.getMonoCurves(v, coord); ? [v] : Curve.getMonoCurves(v, coord);
for (var j = 0; j < monoCurves.length; j++) { for (var j = 0; j < monoCurves.length; j++) {
prevV = addWinding(monoCurves[j], prevV, point.x, point.y, prevV = addWinding(monoCurves[j]);
pathWindings, isOnPath, coord);
} }
} }
var nextCurve = curves[i + 1]; var nextCurve = curves[i + 1];
if (!nextCurve || nextCurve.getPath() != path) { if (!nextCurve || nextCurve.getPath() != path) {
if (!pathWindings[0] && !pathWindings[1] && isOnPath[0]) { if (!pathWindingL && !pathWindingR && isOnPath) {
// Use the on-path windings if no other intersections // Use the on-path windings if no other intersections
// were found or if they canceled each other. // were found or if they canceled each other.
var incr = path.isClockwise() ? 1 : -1; var add = path.isClockwise() ? 1 : -1;
windings[0] += incr; windingL += add;
windings[1] += -incr; windingR -= add;
onPathWinding += incr; onPathWinding += add;
} else { } else {
windings[0] += pathWindings[0]; windingL += pathWindingL;
windings[1] += pathWindings[1]; windingR += pathWindingR;
pathWindings[0] = pathWindings[1] = 0; pathWindingL = pathWindingR = 0;
} }
isOnPath[0] = false; isOnPath = false;
} }
} }
if (windings[0] === 0 && windings[1] === 0) { if (windingL === 0 && windingR === 0) {
windings[0] = onPathWinding; windingL = windingR = onPathWinding;
windings[1] = onPathWinding;
} }
var windLeft = windings[0] && (2 - abs(windings[0]) % 2), windingL = windingL && (2 - abs(windingL) % 2);
windRight = windings[1] && (2 - abs(windings[1]) % 2); windingR = windingR && (2 - abs(windingR) % 2);
// Return both the calculated winding contribution, and also detect if // Return both the calculated winding contribution, and also detect if
// we are on the contour of the area by comparing windLeft & windRight. // we are on the contour of the area by comparing windingL and windingR.
// This is required when handling unite operations, where a winding // This is required when handling unite operations, where a winding
// 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: Math.max(windLeft, windRight), winding: Math.max(windingL, windingR),
contour: !windLeft ^ !windRight contour: !windingL ^ !windingR
}; };
} }