mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-08 05:42:07 -05:00
_getMonotoneCurves method which returns and caches curves that are monotonic in Y direction
This commit is contained in:
parent
b4c7bcae5e
commit
0134596f66
2 changed files with 150 additions and 43 deletions
|
@ -236,14 +236,27 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
||||||
return paths.join(' ');
|
return paths.join(' ');
|
||||||
},
|
},
|
||||||
|
|
||||||
_getWinding: function(point) {
|
/**
|
||||||
|
* Private method that returns all the curves in this CompoundPath, which
|
||||||
|
* are monotonically decreasing or increasing in the 'y' direction.
|
||||||
|
* Used by PathItem#_getWinding method.
|
||||||
|
*/
|
||||||
|
_getMonotoneCurves: function() {
|
||||||
var children = this._children,
|
var children = this._children,
|
||||||
winding = 0;
|
monoCurves = [];
|
||||||
for (var i = 0, l = children.length; i < l; i++)
|
for (var i = 0, l = children.length; i < l; i++)
|
||||||
winding += children[i]._getWinding(point);
|
monoCurves.push.apply(monoCurves, children[i]._getMonotoneCurves());
|
||||||
return winding;
|
return monoCurves;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// _getWinding: function(point) {
|
||||||
|
// var children = this._children,
|
||||||
|
// winding = 0;
|
||||||
|
// for (var i = 0, l = children.length; i < l; i++)
|
||||||
|
// winding += children[i]._getWinding(point);
|
||||||
|
// return winding;
|
||||||
|
// },
|
||||||
|
|
||||||
_getChildHitTestOptions: function(options) {
|
_getChildHitTestOptions: function(options) {
|
||||||
// If we're not specifically asked to returns paths through
|
// If we're not specifically asked to returns paths through
|
||||||
// options.type == 'path' do not test children for fill, since a
|
// options.type == 'path' do not test children for fill, since a
|
||||||
|
|
172
src/path/Path.js
172
src/path/Path.js
|
@ -145,6 +145,9 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
for (var i = 0, l = this._curves.length; i < l; i++)
|
for (var i = 0, l = this._curves.length; i < l; i++)
|
||||||
this._curves[i]._changed(/*#=*/ Change.GEOMETRY);
|
this._curves[i]._changed(/*#=*/ Change.GEOMETRY);
|
||||||
}
|
}
|
||||||
|
// Clear cached curves used for winding direction and containment
|
||||||
|
// calculation.
|
||||||
|
this._monotoneCurves = undefined;
|
||||||
} else if (flags & /*#=*/ ChangeFlag.STROKE) {
|
} else if (flags & /*#=*/ ChangeFlag.STROKE) {
|
||||||
// TODO: We could preserve the purely geometric bounds that are not
|
// TODO: We could preserve the purely geometric bounds that are not
|
||||||
// affected by stroke: _bounds.bounds and _bounds.handleBounds
|
// affected by stroke: _bounds.bounds and _bounds.handleBounds
|
||||||
|
@ -1708,49 +1711,140 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
_getWinding: function(point) {
|
/**
|
||||||
var closed = this._closed;
|
* Private method that returns and caches all the curves in this Path, which
|
||||||
// If the path is not closed, we should not bail out in case it has a
|
* are monotonically decreasing or increasing in the 'y' direction.
|
||||||
// fill color!
|
* Used by PathItem#_getWinding method.
|
||||||
if (!closed && !this.hasFill()
|
*/
|
||||||
|| !this.getInternalRoughBounds()._containsPoint(point))
|
_getMonotoneCurves: function() {
|
||||||
return 0;
|
var monoCurves = this._monotoneCurves,
|
||||||
// Use the crossing number algorithm, by counting the crossings of the
|
// TODO: replace instances with constants ( /*#=*/ ?)
|
||||||
// beam in right y-direction with the shape, and see if it's an odd
|
INCREASING = 1,
|
||||||
// number, meaning the starting point is inside the shape.
|
DECREASING = -1;
|
||||||
// http://en.wikipedia.org/wiki/Point_in_polygon
|
if (!monoCurves) {
|
||||||
var curves = this.getCurves(),
|
// Insert curve values into a cached array
|
||||||
segments = this._segments,
|
// Always avoid horizontal curves
|
||||||
winding = 0,
|
function insertValues(v, dir) {
|
||||||
// Reuse arrays for root-finding, give garbage collector a break
|
var y0 = v[1], y1 = v[7];
|
||||||
roots1 = [],
|
dir = dir || INCREASING;
|
||||||
roots2 = [],
|
if (y0 === y1) {
|
||||||
last = (closed
|
return;
|
||||||
? curves[curves.length - 1]
|
} else if (y0 > y1) {
|
||||||
// Create a straight closing line for open paths, just like
|
dir = DECREASING;
|
||||||
// how filling open paths works.
|
}
|
||||||
: new Curve(segments[segments.length - 1]._point,
|
v.push(dir);
|
||||||
segments[0]._point)).getValues(),
|
monoCurves.push(v);
|
||||||
previous = last;
|
}
|
||||||
for (var i = 0, l = curves.length; i < l; i++) {
|
// Handle bezier curves. We need to chop them into smaller curves
|
||||||
var curve = curves[i].getValues(),
|
// with defined orientation, by solving the derivative curve for
|
||||||
x = curve[0],
|
// Y extrema.
|
||||||
y = curve[1];
|
function insertCurves(v, dir) {
|
||||||
// Filter out curves with 0-length (all 4 points in the same place):
|
var y0 = v[1], y1 = v[3],
|
||||||
if (!(x === curve[2] && y === curve[3] && x === curve[4]
|
y2 = v[5], y3 = v[7],
|
||||||
&& y === curve[5] && x === curve[6] && y === curve[7])) {
|
roots = [], tolerance = /*#=*/ Numerical.TOLERANCE,
|
||||||
winding += Curve._getWinding(curve, previous, point.x, point.y,
|
i, li;
|
||||||
roots1, roots2);
|
// Split the curve at y extrema, to get bezier curves with clear
|
||||||
previous = curve;
|
// orientation: Calculate the derivative and find its roots.
|
||||||
|
var a = 3 * (y1 - y2) - y0 + y3,
|
||||||
|
b = 2 * (y0 + y2) - 4 * y1,
|
||||||
|
c = y1 - y0;
|
||||||
|
// Keep then range to 0 .. 1 (excluding) in the search
|
||||||
|
// for y extrema
|
||||||
|
var count = Numerical.solveQuadratic(a, b, c, roots, tolerance,
|
||||||
|
1 - tolerance);
|
||||||
|
if (count === 0) {
|
||||||
|
insertValues(v, dir);
|
||||||
|
} else {
|
||||||
|
roots.sort();
|
||||||
|
var parts, t = roots[0];
|
||||||
|
parts = Curve.subdivide(v, t);
|
||||||
|
if (count > 1) {
|
||||||
|
// Now renormalize t1 to the range of the next part.
|
||||||
|
t = (roots[1] - t) / (1 - t);
|
||||||
|
var subparts = Curve.subdivide(parts[1], t);
|
||||||
|
parts.splice(1, 1, subparts[0], subparts[1]);
|
||||||
|
}
|
||||||
|
for (i = 0, li = parts.length; i < li; i++)
|
||||||
|
insertValues(parts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Insert curves that are monotonic in y direction into a cached array
|
||||||
|
monoCurves = this._monotoneCurves = [];
|
||||||
|
var curves = this.getCurves(),
|
||||||
|
crv, vals, i, li;
|
||||||
|
// If the path is not closed, we should join the end points
|
||||||
|
// with a straight line, just like how filling open paths works.
|
||||||
|
if (!this._closed) {
|
||||||
|
// if (!this.hasFill()
|
||||||
|
// || !this.getInternalRoughBounds()._containsPoint(point))
|
||||||
|
// return 0;
|
||||||
|
var segments = this._segments;
|
||||||
|
curves.push(new Curve(segments[segments.length - 1]._point,
|
||||||
|
segments[0]._point).getValues());
|
||||||
|
}
|
||||||
|
for (i = 0, li = curves.length; i < li; i++) {
|
||||||
|
crv = curves[i];
|
||||||
|
vals = crv.getValues();
|
||||||
|
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]) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
insertCurves(vals, INCREASING);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!closed) {
|
return monoCurves;
|
||||||
winding += Curve._getWinding(last, previous, point.x, point.y,
|
|
||||||
roots1, roots2);
|
|
||||||
}
|
|
||||||
return winding;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// _getWinding: function(point) {
|
||||||
|
// var closed = this._closed;
|
||||||
|
// // If the path is not closed, we should not bail out in case it has a
|
||||||
|
// // fill color!
|
||||||
|
// if (!closed && !this.hasFill()
|
||||||
|
// || !this.getInternalRoughBounds()._containsPoint(point))
|
||||||
|
// return 0;
|
||||||
|
// // Use the crossing number algorithm, by counting the crossings of the
|
||||||
|
// // beam in right y-direction with the shape, and see if it's an odd
|
||||||
|
// // number, meaning the starting point is inside the shape.
|
||||||
|
// // http://en.wikipedia.org/wiki/Point_in_polygon
|
||||||
|
// var curves = this.getCurves(),
|
||||||
|
// segments = this._segments,
|
||||||
|
// winding = 0,
|
||||||
|
// // Reuse arrays for root-finding, give garbage collector a break
|
||||||
|
// roots1 = [],
|
||||||
|
// roots2 = [],
|
||||||
|
// 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)).getValues(),
|
||||||
|
// previous = last;
|
||||||
|
// for (var i = 0, l = curves.length; i < l; i++) {
|
||||||
|
// var curve = curves[i].getValues(),
|
||||||
|
// x = curve[0],
|
||||||
|
// y = curve[1];
|
||||||
|
// // Filter out curves with 0-length (all 4 points in the same place):
|
||||||
|
// if (!(x === curve[2] && y === curve[3] && x === curve[4]
|
||||||
|
// && y === curve[5] && x === curve[6] && y === curve[7])) {
|
||||||
|
// winding += Curve._getWinding(curve, previous, point.x, point.y,
|
||||||
|
// roots1, roots2);
|
||||||
|
// previous = curve;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (!closed) {
|
||||||
|
// winding += Curve._getWinding(last, previous, point.x, point.y,
|
||||||
|
// roots1, roots2);
|
||||||
|
// }
|
||||||
|
// return winding;
|
||||||
|
// },
|
||||||
|
|
||||||
_hitTest: function(point, options) {
|
_hitTest: function(point, options) {
|
||||||
var that = this,
|
var that = this,
|
||||||
style = this.getStyle(),
|
style = this.getStyle(),
|
||||||
|
|
Loading…
Reference in a new issue