mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-03 19:45:44 -05:00
Improve handling of excludeStart / excludeEnd in curve intersections.
This commit is contained in:
parent
45f5bf84e8
commit
df7323da32
1 changed files with 49 additions and 67 deletions
|
@ -450,7 +450,7 @@ var Curve = Base.extend(/** @lends Curve# */{
|
||||||
getIntersections: function(curve) {
|
getIntersections: function(curve) {
|
||||||
return Curve.getCurveIntersections(this.getValues(),
|
return Curve.getCurveIntersections(this.getValues(),
|
||||||
curve && curve !== this ? curve.getValues() : null,
|
curve && curve !== this ? curve.getValues() : null,
|
||||||
this, curve, [], {});
|
this, curve, []);
|
||||||
},
|
},
|
||||||
|
|
||||||
// TODO: adjustThroughPoint
|
// TODO: adjustThroughPoint
|
||||||
|
@ -1726,12 +1726,14 @@ new function() { // Scope for methods that require private functions
|
||||||
},
|
},
|
||||||
new function() { // Scope for intersection using bezier fat-line clipping
|
new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
|
|
||||||
function addLocation(locations, param, v1, c1, t1, p1, v2, c2, t2, p2,
|
function addLocation(locations, include, v1, c1, t1, p1, v2, c2, t2, p2,
|
||||||
overlap) {
|
overlap) {
|
||||||
// Do not exclude connecting points between two curves if they were part
|
// Determine if locations at the beginning / end of the curves should be
|
||||||
// of overlap checks, as they could be self-overlapping.
|
// excluded, in case the two curves are neighbors, but do not exclude
|
||||||
var excludeStart = !overlap && param.excludeStart,
|
// connecting points between two curves if they were part of overlap
|
||||||
excludeEnd = !overlap && param.excludeEnd,
|
// checks, as they could be self-overlapping.
|
||||||
|
var excludeStart = !overlap && c1.getPrevious() === c2,
|
||||||
|
excludeEnd = !overlap && c1 !== c2 && c1.getNext() === c2,
|
||||||
tMin = /*#=*/Numerical.CURVETIME_EPSILON,
|
tMin = /*#=*/Numerical.CURVETIME_EPSILON,
|
||||||
tMax = 1 - tMin;
|
tMax = 1 - tMin;
|
||||||
if (t1 == null)
|
if (t1 == null)
|
||||||
|
@ -1758,8 +1760,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
// matched to a potentially already existing intersection.
|
// matched to a potentially already existing intersection.
|
||||||
flip = loc1.getPath() === loc2.getPath()
|
flip = loc1.getPath() === loc2.getPath()
|
||||||
&& loc1.getIndex() > loc2.getIndex(),
|
&& loc1.getIndex() > loc2.getIndex(),
|
||||||
loc = flip ? loc2 : loc1,
|
loc = flip ? loc2 : loc1;
|
||||||
include = param.include;
|
|
||||||
// Link the two locations to each other.
|
// Link the two locations to each other.
|
||||||
loc1._intersection = loc2;
|
loc1._intersection = loc2;
|
||||||
loc2._intersection = loc1;
|
loc2._intersection = loc1;
|
||||||
|
@ -1770,8 +1771,8 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addCurveIntersections(v1, v2, c1, c2, locations, param, tMin, tMax,
|
function addCurveIntersections(v1, v2, c1, c2, locations, include,
|
||||||
uMin, uMax, flip, recursion, calls) {
|
tMin, tMax, uMin, uMax, flip, recursion, calls) {
|
||||||
// Avoid deeper recursion, by counting the total amount of recursions,
|
// Avoid deeper recursion, by counting the total amount of recursions,
|
||||||
// as well as the total amount of calls, to avoid massive call-trees as
|
// as well as the total amount of calls, to avoid massive call-trees as
|
||||||
// suggested by @iconexperience in #904#issuecomment-225283430.
|
// suggested by @iconexperience in #904#issuecomment-225283430.
|
||||||
|
@ -1825,7 +1826,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
// curve values to match the parameter space of t1 and t2:
|
// curve values to match the parameter space of t1 and t2:
|
||||||
v1 = c1.getValues();
|
v1 = c1.getValues();
|
||||||
v2 = c2.getValues();
|
v2 = c2.getValues();
|
||||||
addLocation(locations, param,
|
addLocation(locations, include,
|
||||||
flip ? v2 : v1, flip ? c2 : c1, flip ? u : t, null,
|
flip ? v2 : v1, flip ? c2 : c1, flip ? u : t, null,
|
||||||
flip ? v1 : v2, flip ? c1 : c2, flip ? t : u, null);
|
flip ? v1 : v2, flip ? c1 : c2, flip ? t : u, null);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1837,31 +1838,31 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
var parts = Curve.subdivide(v1, 0.5),
|
var parts = Curve.subdivide(v1, 0.5),
|
||||||
t = (tMinNew + tMaxNew) / 2;
|
t = (tMinNew + tMaxNew) / 2;
|
||||||
calls = addCurveIntersections(
|
calls = addCurveIntersections(
|
||||||
v2, parts[0], c2, c1, locations, param,
|
v2, parts[0], c2, c1, locations, include,
|
||||||
uMin, uMax, tMinNew, t, !flip, recursion, calls);
|
uMin, uMax, tMinNew, t, !flip, recursion, calls);
|
||||||
calls = addCurveIntersections(
|
calls = addCurveIntersections(
|
||||||
v2, parts[1], c2, c1, locations, param,
|
v2, parts[1], c2, c1, locations, include,
|
||||||
uMin, uMax, t, tMaxNew, !flip, recursion, calls);
|
uMin, uMax, t, tMaxNew, !flip, recursion, calls);
|
||||||
} else {
|
} else {
|
||||||
var parts = Curve.subdivide(v2, 0.5),
|
var parts = Curve.subdivide(v2, 0.5),
|
||||||
u = (uMin + uMax) / 2;
|
u = (uMin + uMax) / 2;
|
||||||
calls = addCurveIntersections(
|
calls = addCurveIntersections(
|
||||||
parts[0], v1, c2, c1, locations, param,
|
parts[0], v1, c2, c1, locations, include,
|
||||||
uMin, u, tMinNew, tMaxNew, !flip, recursion, calls);
|
uMin, u, tMinNew, tMaxNew, !flip, recursion, calls);
|
||||||
calls = addCurveIntersections(
|
calls = addCurveIntersections(
|
||||||
parts[1], v1, c2, c1, locations, param,
|
parts[1], v1, c2, c1, locations, include,
|
||||||
u, uMax, tMinNew, tMaxNew, !flip, recursion, calls);
|
u, uMax, tMinNew, tMaxNew, !flip, recursion, calls);
|
||||||
}
|
}
|
||||||
} else { // Iterate
|
} else { // Iterate
|
||||||
if (uMax - uMin >= fatLineEpsilon) {
|
if (uMax - uMin >= fatLineEpsilon) {
|
||||||
calls = addCurveIntersections(
|
calls = addCurveIntersections(
|
||||||
v2, v1, c2, c1, locations, param,
|
v2, v1, c2, c1, locations, include,
|
||||||
uMin, uMax, tMinNew, tMaxNew, !flip, recursion, calls);
|
uMin, uMax, tMinNew, tMaxNew, !flip, recursion, calls);
|
||||||
} else {
|
} else {
|
||||||
// The interval on the other curve is already tight enough,
|
// The interval on the other curve is already tight enough,
|
||||||
// therefore we keep iterating on the same curve.
|
// therefore we keep iterating on the same curve.
|
||||||
calls = addCurveIntersections(
|
calls = addCurveIntersections(
|
||||||
v1, v2, c1, c2, locations, param,
|
v1, v2, c1, c2, locations, include,
|
||||||
tMinNew, tMaxNew, uMin, uMax, flip, recursion, calls);
|
tMinNew, tMaxNew, uMin, uMax, flip, recursion, calls);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1985,7 +1986,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
return roots;
|
return roots;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addCurveLineIntersections(v1, v2, c1, c2, locations, param) {
|
function addCurveLineIntersections(v1, v2, c1, c2, locations, include) {
|
||||||
var flip = Curve.isStraight(v1),
|
var flip = Curve.isStraight(v1),
|
||||||
vc = flip ? v2 : v1,
|
vc = flip ? v2 : v1,
|
||||||
vl = flip ? v1 : v2,
|
vl = flip ? v1 : v2,
|
||||||
|
@ -2001,34 +2002,27 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
pc = Curve.getPoint(vc, tc),
|
pc = Curve.getPoint(vc, tc),
|
||||||
tl = Curve.getTimeOf(vl, pc);
|
tl = Curve.getTimeOf(vl, pc);
|
||||||
if (tl !== null) {
|
if (tl !== null) {
|
||||||
var pl = Curve.getPoint(vl, tl),
|
var pl = Curve.getPoint(vl, tl);
|
||||||
t1 = flip ? tl : tc,
|
addLocation(locations, include,
|
||||||
t2 = flip ? tc : tl;
|
v1, c1, flip ? tl : tc, flip ? pl : pc,
|
||||||
// If the two curves are connected and the 2nd is very short,
|
v2, c2, flip ? tc : tl, flip ? pc : pl);
|
||||||
// (l < Numerical.GEOMETRIC_EPSILON), we need to filter out an
|
|
||||||
// invalid intersection at the beginning of this short curve.
|
|
||||||
if (!param.excludeEnd || t2 > Numerical.CURVETIME_EPSILON) {
|
|
||||||
addLocation(locations, param,
|
|
||||||
v1, c1, t1, flip ? pl : pc,
|
|
||||||
v2, c2, t2, flip ? pc : pl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLineIntersection(v1, v2, c1, c2, locations, param) {
|
function addLineIntersection(v1, v2, c1, c2, locations, include) {
|
||||||
var pt = Line.intersect(
|
var pt = Line.intersect(
|
||||||
v1[0], v1[1], v1[6], v1[7],
|
v1[0], v1[1], v1[6], v1[7],
|
||||||
v2[0], v2[1], v2[6], v2[7]);
|
v2[0], v2[1], v2[6], v2[7]);
|
||||||
if (pt) {
|
if (pt) {
|
||||||
addLocation(locations, param, v1, c1, null, pt, v2, c2, null, pt);
|
addLocation(locations, include, v1, c1, null, pt, v2, c2, null, pt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCurveIntersections(v1, v2, c1, c2, locations, param) {
|
function getCurveIntersections(v1, v2, c1, c2, locations, include) {
|
||||||
if (!v2) {
|
if (!v2) {
|
||||||
// If v2 is not provided, search for a self-intersection on v1.
|
// If v2 is not provided, search for a self-intersection on v1.
|
||||||
return getLoopIntersection(v1, c1, locations, param);
|
return getLoopIntersection(v1, c1, locations, include);
|
||||||
}
|
}
|
||||||
// Avoid checking curves if completely out of control bounds.
|
// Avoid checking curves if completely out of control bounds.
|
||||||
var epsilon = /*#=*/Numerical.EPSILON,
|
var epsilon = /*#=*/Numerical.EPSILON,
|
||||||
|
@ -2056,7 +2050,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
if (overlaps) {
|
if (overlaps) {
|
||||||
for (var i = 0; i < 2; i++) {
|
for (var i = 0; i < 2; i++) {
|
||||||
var overlap = overlaps[i];
|
var overlap = overlaps[i];
|
||||||
addLocation(locations, param,
|
addLocation(locations, include,
|
||||||
v1, c1, overlap[0], null,
|
v1, c1, overlap[0], null,
|
||||||
v2, c2, overlap[1], null, true);
|
v2, c2, overlap[1], null, true);
|
||||||
}
|
}
|
||||||
|
@ -2074,7 +2068,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
: straight1 || straight2
|
: straight1 || straight2
|
||||||
? addCurveLineIntersections
|
? addCurveLineIntersections
|
||||||
: addCurveIntersections)(
|
: addCurveIntersections)(
|
||||||
v1, v2, c1, c2, locations, param,
|
v1, v2, c1, c2, locations, include,
|
||||||
// Define the defaults for these parameters of
|
// Define the defaults for these parameters of
|
||||||
// addCurveIntersections():
|
// addCurveIntersections():
|
||||||
// tMin, tMax, uMin, uMax, flip, recursion, calls
|
// tMin, tMax, uMin, uMax, flip, recursion, calls
|
||||||
|
@ -2090,21 +2084,21 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
c2p0 = new Point(c2x0, c2y0),
|
c2p0 = new Point(c2x0, c2y0),
|
||||||
c2p3 = new Point(c2x3, c2y3);
|
c2p3 = new Point(c2x3, c2y3);
|
||||||
if (c1p0.isClose(c2p0, epsilon))
|
if (c1p0.isClose(c2p0, epsilon))
|
||||||
addLocation(locations, param, v1, c1, 0, c1p0, v2, c2, 0, c2p0);
|
addLocation(locations, include, v1, c1, 0, c1p0, v2, c2, 0, c2p0);
|
||||||
if (!param.excludeStart && c1p0.isClose(c2p3, epsilon))
|
if (c1p0.isClose(c2p3, epsilon))
|
||||||
addLocation(locations, param, v1, c1, 0, c1p0, v2, c2, 1, c2p3);
|
addLocation(locations, include, v1, c1, 0, c1p0, v2, c2, 1, c2p3);
|
||||||
if (!param.excludeEnd && c1p3.isClose(c2p0, epsilon))
|
if (c1p3.isClose(c2p0, epsilon))
|
||||||
addLocation(locations, param, v1, c1, 1, c1p3, v2, c2, 0, c2p0);
|
addLocation(locations, include, v1, c1, 1, c1p3, v2, c2, 0, c2p0);
|
||||||
if (c1p3.isClose(c2p3, epsilon))
|
if (c1p3.isClose(c2p3, epsilon))
|
||||||
addLocation(locations, param, v1, c1, 1, c1p3, v2, c2, 1, c2p3);
|
addLocation(locations, include, v1, c1, 1, c1p3, v2, c2, 1, c2p3);
|
||||||
return locations;
|
return locations;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLoopIntersection(v1, c1, locations, param) {
|
function getLoopIntersection(v1, c1, locations, include) {
|
||||||
var info = Curve.classify(v1);
|
var info = Curve.classify(v1);
|
||||||
if (info.type === 'loop') {
|
if (info.type === 'loop') {
|
||||||
var roots = info.roots;
|
var roots = info.roots;
|
||||||
addLocation(locations, param,
|
addLocation(locations, include,
|
||||||
v1, c1, roots[0], null,
|
v1, c1, roots[0], null,
|
||||||
v1, c1, roots[1], null);
|
v1, c1, roots[1], null);
|
||||||
}
|
}
|
||||||
|
@ -2140,12 +2134,7 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
}
|
}
|
||||||
if (self) {
|
if (self) {
|
||||||
// First check for self-intersections within the same curve.
|
// First check for self-intersections within the same curve.
|
||||||
getLoopIntersection(values1, curve1, locations, {
|
getLoopIntersection(values1, curve1, locations, include);
|
||||||
include: include,
|
|
||||||
// Only possible if there is only one closed curve:
|
|
||||||
excludeStart: length1 === 1 &&
|
|
||||||
curve1.getPoint1().equals(curve1.getPoint2())
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
// Check for intersections with other curves.
|
// Check for intersections with other curves.
|
||||||
// For self-intersection, we can start at i + 1 instead of 0.
|
// For self-intersection, we can start at i + 1 instead of 0.
|
||||||
|
@ -2157,17 +2146,8 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
var curve2 = curves2[j];
|
var curve2 = curves2[j];
|
||||||
// Avoid end point intersections on consecutive curves when
|
// Avoid end point intersections on consecutive curves when
|
||||||
// self-intersecting.
|
// self-intersecting.
|
||||||
getCurveIntersections(
|
getCurveIntersections(values1, values2[j], curve1, curve2,
|
||||||
values1, values2[j], curve1, curve2, locations,
|
locations, include);
|
||||||
{
|
|
||||||
include: include,
|
|
||||||
// Do not compare indices to determine connection, since
|
|
||||||
// one array of curves can contain curves from separate
|
|
||||||
// sup-paths of a compound path.
|
|
||||||
excludeStart: curve1.getPrevious() === curve2,
|
|
||||||
excludeEnd: curve1.getNext() === curve2
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Flatten the list of location arrays to one array and return it.
|
// Flatten the list of location arrays to one array and return it.
|
||||||
|
@ -2275,11 +2255,13 @@ new function() { // Scope for intersection using bezier fat-line clipping
|
||||||
return pairs;
|
return pairs;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { statics: /** @lends Curve */{
|
return {
|
||||||
getCurveIntersections: getCurveIntersections,
|
statics: /** @lends Curve */{
|
||||||
getCurvesIntersections: getCurvesIntersections,
|
getCurveIntersections: getCurveIntersections,
|
||||||
getOverlaps: getOverlaps,
|
getCurvesIntersections: getCurvesIntersections,
|
||||||
// Exposed for use in boolean offsetting
|
getOverlaps: getOverlaps,
|
||||||
getCurveLineIntersections: getCurveLineIntersections
|
// Exposed for use in boolean offsetting
|
||||||
}};
|
getCurveLineIntersections: getCurveLineIntersections
|
||||||
|
}
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue