Make Curve#_getCrossings() a static method and have it work on curve values directly.

This commit is contained in:
Jürg Lehni 2013-06-13 23:16:00 -07:00
parent d6db007765
commit 610e396d94
2 changed files with 79 additions and 84 deletions

View file

@ -286,87 +286,11 @@ var Curve = Base.extend(/** @lends Curve# */{
&& this._segment2._handleIn.isZero();
},
isZero: function() {
return this._segment1._point.equals(this._segment2._point)
&& this.isLinear();
},
getIntersections: function(curve) {
return Curve.getIntersections(this.getValues(), curve.getValues(),
this, curve, []);
},
_getCrossings: function(point, previous, roots) {
// Implementation of the crossing number algorithm:
// http://en.wikipedia.org/wiki/Point_in_polygon
// Solve the y-axis cubic polynomial for point.y and count all solutions
// to the right of point.x as crossings.
var vals = this.getValues(),
count = Curve.solveCubic(vals, 1, point.y, roots),
crossings = 0,
tolerance = /*#=*/ Numerical.TOLERANCE,
abs = Math.abs;
// Checks the y-slope between the current curve and the previous for a
// change of orientation, when a solution is found at t == 0
function changesOrientation(tangent) {
return Curve.evaluate(previous.getValues(), 1, true, 1).y
* tangent.y > 0;
}
// TODO: See if this speeds up code, or slows it down:
// var bounds = this.getBounds();
// if (point.y < bounds.getTop() || point.y > bounds.getBottom()
// || point.x > bounds.getRight())
// return 0;
if (count === -1) {
// Infinite solutions, so we have a horizontal curve.
// Find parameter through getParameterOf()
roots[0] = Curve.getParameterOf(vals, point.x, point.y);
count = roots[0] !== null ? 1 : 0;
}
for (var i = 0; i < count; i++) {
var t = roots[i];
if (t > -tolerance && t < 1 - tolerance) {
var pt = Curve.evaluate(vals, t, true, 0);
if (point.x < pt.x + tolerance) {
// Passing 1 for Curve.evaluate() type calculates tangents
var tan = Curve.evaluate(vals, t, true, 1);
// Handle all kind of edge cases when points are on contours
// or rays are touching countours, to termine wether the
// crossing counts or not.
// See if the actual point is on the countour:
if (abs(pt.x - point.x) < tolerance) {
// Do not count the crossing if it is on the left hand
// side of the shape (tangent pointing upwards), since
// the ray will go out the other end, count as
// crossing there, and the point is on the contour, so
// to be considered inside.
var angle = tan.getAngle();
if (angle > -180 && angle < 0
// Handle special case where point is on a corner,
// in which case this crossing is skipped if both
// tangents have the same orientation.
&& (t > tolerance || changesOrientation(tan)))
continue;
} else {
// Skip touching stationary points:
if (abs(tan.y) < tolerance
// Check derivate for stationary points. If root is
// close to 0 and not changing vertical orientation
// from the previous curve, do not count this root,
// as it's touching a corner.
|| t < tolerance && !changesOrientation(tan))
continue;
}
crossings++;
}
}
}
return crossings;
},
// TODO: adjustThroughPoint
/**
@ -670,6 +594,70 @@ statics: {
return new Rectangle(min[0], min[1], max[0] - min[0], max[1] - min[1]);
},
_getCrossings: function(v, prev, x, y, roots) {
// Implementation of the crossing number algorithm:
// http://en.wikipedia.org/wiki/Point_in_polygon
// Solve the y-axis cubic polynomial for y and count all solutions
// to the right of x as crossings.
var count = Curve.solveCubic(v, 1, y, roots),
crossings = 0,
tolerance = /*#=*/ Numerical.TOLERANCE,
abs = Math.abs;
// Checks the y-slope between the current curve and the previous for a
// change of orientation, when a solution is found at t == 0
function changesOrientation(tangent) {
return Curve.evaluate(prev, 1, true, 1).y
* tangent.y > 0;
}
if (count === -1) {
// Infinite solutions, so we have a horizontal curve.
// Find parameter through getParameterOf()
roots[0] = Curve.getParameterOf(v, x, y);
count = roots[0] !== null ? 1 : 0;
}
for (var i = 0; i < count; i++) {
var t = roots[i];
if (t > -tolerance && t < 1 - tolerance) {
var pt = Curve.evaluate(v, t, true, 0);
if (x < pt.x + tolerance) {
// Pass 1 for Curve.evaluate() type to calculate tangent
var tan = Curve.evaluate(v, t, true, 1);
// Handle all kind of edge cases when points are on
// contours or rays are touching countours, to termine
// wether the crossing counts or not.
// See if the actual point is on the countour:
if (abs(pt.x - x) < tolerance) {
// Do not count the crossing if it is on the left hand
// side of the shape (tangent pointing upwards), since
// the ray will go out the other end, count as crossing
// there, and the point is on the contour, so to be
// considered inside.
var angle = tan.getAngle();
if (angle > -180 && angle < 0
// Handle special case where point is on a corner,
// in which case this crossing is skipped if both
// tangents have the same orientation.
&& (t > tolerance || changesOrientation(tan)))
continue;
} else {
// Skip touching stationary points:
if (abs(tan.y) < tolerance
// Check derivate for stationary points. If root is
// close to 0 and not changing vertical orientation
// from the previous curve, do not count this root,
// as it's touching a corner.
|| t < tolerance && !changesOrientation(tan))
continue;
}
crossings++;
}
}
}
return crossings;
},
/**
* Private helper for both Curve.getBounds() and Path.getBounds(), which
* finds the 0-crossings of the derivative of a bezier curve polynomial, to

View file

@ -1613,22 +1613,29 @@ var Path = PathItem.extend(/** @lends Path# */{
crossings = 0,
// Reuse one array for root-finding, give garbage collector a break
roots = new Array(3),
last = closed
last = (closed
? curves[curves.length - 1]
// Create a straight closing line for open paths, just like
// how filling open paths works.
: new Curve(segments[segments.length - 1]._point,
segments[0]._point),
segments[0]._point)).getValues(),
previous = last;
for (var i = 0, l = curves.length; i < l; i++) {
var curve = curves[i];
if (!curve.isZero()) {
crossings += curve._getCrossings(point, previous, roots);
previous = curve;
var vals = curves[i].getValues(),
x = vals[0],
y = vals[1];
// Filter out curves with 0-lenght (all 4 points in the same place):
if (!(x === vals[2] && y === vals[3] && x === vals[4]
&& y === vals[5] && x === vals[6] && y === vals[7])) {
crossings += Curve._getCrossings(vals, previous,
point.x, point.y, roots);
previous = vals;
}
}
if (!closed)
crossings += last._getCrossings(point, previous, roots);
if (!closed) {
crossings += Curve._getCrossings(last, previous, point.x, point.y,
roots);
}
return (crossings & 1) === 1;
},