Fix issue in getWinding() where rays were counted twice.

Closes .
This commit is contained in:
Jürg Lehni 2015-08-18 22:36:10 +02:00
parent be603c7da8
commit 08bdfe585b

View file

@ -244,7 +244,7 @@ PathItem.inject(new function() {
}
/**
* Private method that returns the winding contribution of the given point
* Private method that returns the winding contribution of the given point
* with respect to a given set of monotone curves.
*/
function getWinding(point, curves, horizontal, testContains) {
@ -284,6 +284,7 @@ PathItem.inject(new function() {
// half of closest top and bottom intercepts.
yTop = (yTop + py) / 2;
yBottom = (yBottom + py) / 2;
// TODO: Don't we need to pass on testContains here?
if (yTop > -Infinity)
windLeft = getWinding(new Point(px, yTop), curves);
if (yBottom < Infinity)
@ -293,12 +294,13 @@ PathItem.inject(new function() {
xAfter = px + tolerance;
// Find the winding number for right side of the curve, inclusive of
// the curve itself, while tracing along its +-x direction.
var startCounted = false,
prevCurve,
prevT;
for (var i = 0, l = curves.length; i < l; i++) {
var curve = curves[i],
values = curve.values,
winding = curve.winding,
prevT,
prevX;
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
@ -307,25 +309,25 @@ PathItem.inject(new function() {
&& py >= values[1] && py <= values[7]
|| py >= values[7] && py <= values[1])
&& Curve.solveCubic(values, 1, py, roots, 0, 1) === 1) {
var t = roots[0],
x = Curve.evaluate(values, t, 0).x,
slope = Curve.evaluate(values, t, 1).y;
var t = roots[0];
// Due to numerical precision issues, two consecutive curves
// may register an intercept twice, at t = 1 and 0, if y is
// almost equal to one of the endpoints of the 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
// be registered as a double, we need to filter these cases:
if (!(t > tMax
// Detect and exclude intercepts at 'end' of loops:
&& (i === l - 1 || curve.next !== curves[i + 1])
&& abs(Curve.evaluate(curve.next.values, 0, 0).x -x)
< tolerance
if (!( // = the following conditions will be excluded:
// Detect and exclude intercepts at 'end' of loops
// if the start of the loop was already counted.
// This also works for the last curve: [i + 1] == null
t > tMax && startCounted && curve.next !== curves[i + 1]
// Detect 2nd case of a consecutive intercept, but make
// sure we're still on the same loop
|| i > 0 && curve.previous === curves[i - 1]
&& abs(prevX - x) < tolerance
&& prevT > tMax && t < tMin)) {
// sure we're still on the same loop.
|| t < tMin && prevT > tMax
&& curve.previous === prevCurve)) {
var x = Curve.evaluate(values, t, 0).x,
slope = Curve.evaluate(values, t, 1).y,
counted = false;
// Take care of cases where the curve and the preceding
// curve merely touches the ray towards +-x direction,
// but proceeds to the same side of the ray.
@ -340,15 +342,23 @@ PathItem.inject(new function() {
if (testContains && x >= xBefore && x <= xAfter) {
++windLeft;
++windRight;
counted = true;
}
} else if (x <= xBefore) {
windLeft += winding;
counted = true;
} else if (x >= xAfter) {
windRight += winding;
counted = true;
}
// Detect the beginning of a new loop by comparing with
// the previous curve, and set startCounted accordingly.
// This also works for the first loop where i - 1 == -1
if (curve.previous !== curves[i - 1])
startCounted = t < tMin && counted;
}
prevCurve = curve;
prevT = t;
prevX = x;
}
}
}