Simplify and streamline Path._getMonotoneCurves() code.

This commit is contained in:
Jürg Lehni 2014-02-19 23:55:56 +01:00
parent f6061905be
commit 6af639946b

View file

@ -1716,98 +1716,94 @@ var Path = PathItem.extend(/** @lends Path# */{
*/ */
_getMonotoneCurves: function() { _getMonotoneCurves: function() {
var monoCurves = this._monotoneCurves, var monoCurves = this._monotoneCurves,
lastCurve, prevCurve;
INCREASING = 1,
DECREASING = -1,
HORIZONTAL = 0;
if (!monoCurves) {
// Insert curve values into a cached array // Insert curve values into a cached array
// Always avoid horizontal curves function insertCurve(v) {
function insertValues(v, dir) { var y0 = v[1],
var y0 = v[1], y1 = v[7]; y1 = v[7];
dir = dir || INCREASING; // Add the winding direction to the end of the curve values.
if (y0 === y1) { v[8] = y0 === y1
dir = HORIZONTAL; ? 0 // Horizontal
} else if (y0 > y1) { : y0 > y1
dir = DECREASING; ? -1 // Decreasing
: 1; // Increasing
// Add a reference to neighboring curves
if (prevCurve) {
v[9] = prevCurve;
prevCurve[10] = v;
} }
// Add a reference to subsequent curves
v.push(dir);
if (lastCurve) {
v[9] = lastCurve;
lastCurve[10] = v;
}
lastCurve = v;
monoCurves.push(v); monoCurves.push(v);
prevCurve = v;
} }
// Handle bezier curves. We need to chop them into smaller curves // Handle bezier curves. We need to chop them into smaller curves
// with defined orientation, by solving the derivative curve for // with defined orientation, by solving the derivative curve for
// Y extrema. // Y extrema.
function insertCurves(v, dir) { function handleCurve(v) {
var y0 = v[1], y1 = v[3], // Filter out curves of zero length.
y2 = v[5], y3 = v[7], // TODO: Do not filter this here.
roots = [], tolerance = /*#=*/ Numerical.TOLERANCE, if (Curve.getLength(v) === 0)
i, li; return;
var y0 = v[1],
y1 = v[3],
y2 = v[5],
y3 = v[7];
if (Curve.isLinear(v)) {
// Handling linear curves is easy.
insertCurve(v);
} else {
// Split the curve at y extrema, to get bezier curves with clear // Split the curve at y extrema, to get bezier curves with clear
// orientation: Calculate the derivative and find its roots. // orientation: Calculate the derivative and find its roots.
var a = 3 * (y1 - y2) - y0 + y3, var a = 3 * (y1 - y2) - y0 + y3,
b = 2 * (y0 + y2) - 4 * y1, b = 2 * (y0 + y2) - 4 * y1,
c = y1 - y0; c = y1 - y0,
// Keep then range to 0 .. 1 (excluding) in the search tolerance = /*#=*/ Numerical.TOLERANCE,
// for y extrema roots = [];
// Keep then range to 0 .. 1 (excluding) in the search for y
// extrema.
var count = Numerical.solveQuadratic(a, b, c, roots, tolerance, var count = Numerical.solveQuadratic(a, b, c, roots, tolerance,
1 - tolerance); 1 - tolerance);
if (count === 0) { if (count === 0) {
insertValues(v, dir); insertCurve(v);
} else { } else {
roots.sort(); roots.sort();
var parts, t = roots[0]; var t = roots[0],
parts = Curve.subdivide(v, t); parts = Curve.subdivide(v, t);
insertCurve(parts[0]);
if (count > 1) { if (count > 1) {
// Now renormalize t1 to the range of the next part. // If there are two extremas, renormalize t to the range
// of the second range and split again.
t = (roots[1] - t) / (1 - t); t = (roots[1] - t) / (1 - t);
var subparts = Curve.subdivide(parts[1], t); // Since we already processed parts[0], we can override
parts.splice(1, 1, subparts[0], subparts[1]); // the parts array with the new pair now.
parts = Curve.subdivide(parts[1], t);
insertCurve(parts[0]);
} }
for (i = 0, li = parts.length; i < li; i++) insertCurve(parts[1]);
insertValues(parts[i]);
} }
} }
// Insert curves that are monotonic in y direction into a cached array }
if (!monoCurves) {
// Insert curves that are monotonic in y direction into cached array
monoCurves = this._monotoneCurves = []; monoCurves = this._monotoneCurves = [];
var curves = this.getCurves(), var curves = this.getCurves(),
crv, vals, i, li,
segments = this._segments; segments = this._segments;
// If the path is not closed, we should join the end points for (var i = 0, l = curves.length; i < l; i++)
// with a straight line, just like how filling open paths works. handleCurve(curves[i].getValues());
// If the path is not closed, we need to join the end points with a
// straight line, just like how filling open paths works.
if (!this._closed && segments.length > 1) { if (!this._closed && segments.length > 1) {
curves.push(new Curve(segments[segments.length - 1]._point, var p1 = segments[segments.length - 1]._point,
segments[0]._point)); p2 = segments[0]._point,
p1x = p1._x, p1y = p1._y,
p2x = p2._x, p2y = p2._y;
handleCurve([p1x, p1y, p1x, p1y, p2x, p2y, p2x, p2y]);
} }
for (i = 0, li = curves.length; i < li; i++) { // Link first and last curves
crv = curves[i]; monoCurves[0][9] = prevCurve = monoCurves[monoCurves.length - 1];
// Filter out curves of zero length prevCurve[10] = monoCurves[0];
vals = crv.getValues();
if (Curve.getLength(vals) === 0)
continue;
// Handle linear and cubic curves seperately
if (crv.isLinear()) {
insertValues(vals);
} else {
var y0 = vals[1], y1 = vals[7];
if (y0 > y1) {
insertCurves(vals, DECREASING);
} else if (y0 == y1 && y0 == vals[3] && y0 == vals[5]) {
insertValues(vals, HORIZONTAL);
} else {
insertCurves(vals, INCREASING);
}
}
}
// Link, first and last curves
lastCurve = monoCurves[monoCurves.length - 1];
monoCurves[0][9] = lastCurve;
lastCurve.push(monoCurves[0]);
} }
return monoCurves; return monoCurves;
}, },