Handle cases when point y is equal to y of horizontal curve in path

This commit is contained in:
iconexperience 2015-12-14 10:48:04 +01:00 committed by Jürg Lehni
parent 9c1c00c11e
commit 0e3ac9d7f4

View file

@ -319,80 +319,126 @@ PathItem.inject(new function() {
yBottom = (yBottom + py) / 2; yBottom = (yBottom + py) / 2;
if (yTop > -Infinity) if (yTop > -Infinity)
windLeft = getWinding(new Point(px, yTop), curves, false, windLeft = getWinding(new Point(px, yTop), curves, false,
testContains); testContains);
if (yBottom < Infinity) if (yBottom < Infinity)
windRight = getWinding(new Point(px, yBottom), curves, false, windRight = getWinding(new Point(px, yBottom), curves, false,
testContains); testContains);
} else { } else {
var xBefore = px - epsilon, var xBefore = px - epsilon,
xAfter = px + epsilon; xAfter = px + epsilon;
// Find the winding number for right side of the curve, inclusive of var loopStartIndex,
// the curve itself, while tracing along its +-x direction. loopEndIndex = 0;
var startCounted = false, while (loopEndIndex < curves.length) {
prevCurve, // Determine beginning and end of loop and the first and last curve with
prevT; // non-zero winding within the loop
for (var i = 0, l = curves.length; i < l; i++) { loopStartIndex = loopEndIndex; // index of first curve in loop
var curve = curves[i], loopEndIndex = loopStartIndex + 1; // index after last curve in loop
values = curve.values, var curve,
winding = curve.winding; firstWindCrv = null, // first curve in loop with winding != 0
// Since the curves are monotone in y direction, we can just lastWindCrv; // last curve in loop with winding != 0
// compare the endpoints of the curve to determine if the for (var i = loopStartIndex, l = curves.length; i < l; i++) {
// ray from query point along +-x direction will intersect curve = curves[i];
// the monotone curve. Results in quite significant speedup. if (curve.winding) {
if (winding && (winding === 1 if (!firstWindCrv)
firstWindCrv = curve;
lastWindCrv = curve;
}
if (curve.next !== curves[i + 1]) {
loopEndIndex = i + 1;
break;
}
}
// walk through single loop of curves
var startCounted = false,
prevWindCurve, // non-horizontal curve before current curve
nextWindCurve, // non-horizontal curve after current curve
prevT = null;
curve = null;
for (var curveIndex = loopStartIndex; curveIndex < loopEndIndex; curveIndex++) {
if (!curve) {
prevWindCurve = lastWindCrv;
nextWindCurve = firstWindCrv;
} else if (curve.winding) {
prevWindCurve = curve;
}
curve = curves[curveIndex];
if (curve === nextWindCurve) {
nextWindCurve = curve.next;
while (nextWindCurve && !nextWindCurve.winding) {
nextWindCurve = nextWindCurve.next;
}
}
var values = curve.values,
winding = curve.winding;
// Since the curves are monotone in y direction, we can just
// compare the endpoints of the curve to determine if the
// ray from query point along +-x direction will intersect
// the monotone curve. Results in quite significant speedup.
if (winding && (winding == 1
&& py >= values[1] && py <= values[7] && py >= values[1] && py <= values[7]
|| py >= values[7] && py <= values[1]) || py >= values[7] && py <= values[1])
&& Curve.solveCubic(values, 1, py, roots, 0, 1) === 1) { && Curve.solveCubic(values, 1, py, roots, 0, 1) == 1) {
var t = roots[0]; var t = roots[0];
// Due to numerical precision issues, two consecutive curves // Due to numerical precision issues, two consecutive curves
// may register an intercept twice, at t = 1 and 0, if y is // may register an intercept twice, at t = 1 and 0, if y is
// almost equal to one of the endpoints of the curves. // almost equal to one of the endpoints of the curves.
// But since curves may contain more than one loop of curves // But since curves may contain more than one loop of curves
// and the end point on the last curve of a loop would not // and the end point on the last curve of a loop would not
// be registered as a double, we need to filter these cases: // be registered as a double, we need to filter these cases:
if (!( // = the following conditions will be excluded: if (!( // = the following conditions will be excluded:
// Detect and exclude intercepts at 'end' of loops // Detect and exclude intercepts at 'end' of loops
// if the start of the loop was already counted. // if the start of the loop was already counted.
// This also works for the last curve: [i + 1] == null t > tMax && startCounted && curve === lastWindCrv
t > tMax && startCounted && curve.next !== curves[i + 1] // Detect 2nd case of a consecutive intercept
// Detect 2nd case of a consecutive intercept, but make || t < tMin && prevT > tMax)) {
// sure we're still on the same loop. var x = Curve.getPoint(values, t).x,
|| t < tMin && prevT > tMax slope = Curve.getTangent(values, t).y,
&& curve.previous === prevCurve)) { counted = false;
var x = Curve.getPoint(values, t).x, // Take care of cases where the curve and the preceding
slope = Curve.getTangent(values, t).y, // curve merely touches the ray towards +-x direction,
counted = false; // but proceeds to the same side of the ray.
// Take care of cases where the curve and the preceding // This essentially is not a crossing.
// curve merely touches the ray towards +-x direction, if (Numerical.isZero(slope) && !Curve.isStraight(values)
// but proceeds to the same side of the ray. // Does the slope over curve beginning change?
// This essentially is not a crossing. || t < tMin && prevWindCurve && slope * Curve.getTangent(
if (Numerical.isZero(slope) && !Curve.isStraight(values) prevWindCurve.values, 1).y < 0
// Does the slope over curve beginning change? // Does the slope over curve end change?
|| t < tMin && slope * Curve.getTangent( || t > tMax && nextWindCurve && slope * Curve.getTangent(
curve.previous.values, 1).y < 0 nextWindCurve.values, 0).y < 0) {
// Does the slope over curve end change? if (testContains && x >= xBefore && x <= xAfter) {
|| t > tMax && slope * Curve.getTangent( ++windLeft;
curve.next.values, 0).y < 0) { ++windRight;
if (testContains && x >= xBefore && x <= xAfter) { counted = true;
++windLeft; }
++windRight; } else if (x <= xBefore) {
windLeft += winding;
counted = true;
} else if (x >= xAfter) {
windRight += winding;
counted = true; counted = true;
} }
} else if (x <= xBefore) { // mark the start of the path as counted
windLeft += winding; if (curve === firstWindCrv)
counted = true; startCounted = t < tMin && counted;
} else if (x >= xAfter) {
windRight += winding;
counted = true;
} }
// Detect the beginning of a new loop by comparing with prevT = t;
// the previous curve, and set startCounted accordingly. } else if (!winding) {
// This also works for the first loop where i - 1 == -1 // if the point is on a horizontal curve and winding changes between
if (curve.previous !== curves[i - 1]) // before and after the curve, we treat this as a 'touch point'
startCounted = t < tMin && counted; if (testContains && py == values[1] &&
(xAfter >= values[0] && xBefore <= values[6] ||
xAfter >= values[6] && xBefore <= values[0]) &&
prevWindCurve && nextWindCurve &&
prevWindCurve.winding * nextWindCurve.winding < 0) {
++windLeft;
++windRight;
}
// we keep the value for prevT to avoid double counting of intersections
// at the end of a curve and the start of the next curve, even if
// any number of horizontal curves is between both curves
} else {
prevT = null;
} }
prevCurve = curve;
prevT = t;
} }
} }
} }