mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-07-26 13:49:21 -04:00
Merge remote-tracking branch 'origin/master' into boolean-operations
Conflicts: src/path/PathItem.Boolean.js
This commit is contained in:
commit
d522e4aec2
8 changed files with 198 additions and 66 deletions
src/path
|
@ -32,30 +32,49 @@
|
|||
*/
|
||||
|
||||
PathItem.inject(new function() {
|
||||
var operators = {
|
||||
unite: function(w) {
|
||||
return w === 1 || w === 0;
|
||||
},
|
||||
|
||||
intersect: function(w) {
|
||||
return w === 2;
|
||||
},
|
||||
|
||||
subtract: function(w) {
|
||||
return w === 1;
|
||||
},
|
||||
|
||||
exclude: function(w) {
|
||||
return w === 1;
|
||||
}
|
||||
};
|
||||
|
||||
// Boolean operators return true if a curve with the given winding
|
||||
// contribution contributes to the final result or not. They are called
|
||||
// for each curve in the graph after curves in the operands are
|
||||
// split at intersections.
|
||||
function computeBoolean(path1, path2, operator, subtract) {
|
||||
function computeBoolean(path1, path2, operation) {
|
||||
var operator = operators[operation];
|
||||
// Creates a cloned version of the path that we can modify freely, with
|
||||
// its matrix applied to its geometry. Calls #reduce() to simplify
|
||||
// compound paths and remove empty curves, and #reorient() to make sure
|
||||
// all paths have correct winding direction.
|
||||
function preparePath(path) {
|
||||
return path.clone(false).reduce().reorient().transform(null, true);
|
||||
return path.clone(false).reduce().reorient().transform(null, true,
|
||||
true);
|
||||
}
|
||||
|
||||
// We do not modify the operands themselves
|
||||
// The result might not belong to the same type
|
||||
// We do not modify the operands themselves, but create copies instead,
|
||||
// fas produced by the calls to preparePath().
|
||||
// Note that the result paths might not belong to the same type
|
||||
// i.e. subtraction(A:Path, B:Path):CompoundPath etc.
|
||||
var _path1 = preparePath(path1),
|
||||
_path2 = path2 && path1 !== path2 && preparePath(path2);
|
||||
// Do operator specific calculations before we begin
|
||||
// Make both paths at clockwise orientation, except when subtract = true
|
||||
// We need both paths at opposite orientation for subtraction.
|
||||
if (!_path1.isClockwise())
|
||||
_path1.reverse();
|
||||
if (_path2 && !(subtract ^ _path2.isClockwise()))
|
||||
// Give both paths the same orientation except for subtraction
|
||||
// and exclusion, where we need them at opposite orientation.
|
||||
if (_path2 && /^(subtract|exclude)$/.test(operation)
|
||||
^ (_path2.isClockwise() !== _path1.isClockwise()))
|
||||
_path2.reverse();
|
||||
// Split curves at intersections on both paths. Note that for self
|
||||
// intersection, _path2 will be null and getIntersections() handles it.
|
||||
|
@ -134,7 +153,7 @@ PathItem.inject(new function() {
|
|||
// While subtracting, we need to omit this curve if this
|
||||
// curve is contributing to the second operand and is
|
||||
// outside the first operand.
|
||||
windingSum += subtract && _path2
|
||||
windingSum += operation === 'subtract' && _path2
|
||||
&& (path === _path1 && _path2._getWinding(pt, hor)
|
||||
|| path === _path2 && !_path1._getWinding(pt, hor))
|
||||
? 0
|
||||
|
@ -172,7 +191,8 @@ PathItem.inject(new function() {
|
|||
* @param {CurveLocation[]} intersections Array of CurveLocation objects
|
||||
*/
|
||||
function splitPath(intersections) {
|
||||
var tolerance = /*#=*/Numerical.TOLERANCE,
|
||||
var tMin = /*#=*/Numerical.TOLERANCE,
|
||||
tMax = 1 - tMin,
|
||||
linearHandles;
|
||||
|
||||
function resetLinear() {
|
||||
|
@ -182,23 +202,22 @@ PathItem.inject(new function() {
|
|||
linearHandles[i].set(0, 0);
|
||||
}
|
||||
|
||||
for (var i = intersections.length - 1, curve, prevLoc; i >= 0; i--) {
|
||||
for (var i = intersections.length - 1, curve, prev; i >= 0; i--) {
|
||||
var loc = intersections[i],
|
||||
t = loc._parameter;
|
||||
// Check if we are splitting same curve multiple times
|
||||
if (prevLoc && prevLoc._curve === loc._curve
|
||||
// Avoid dividing with zero
|
||||
&& prevLoc._parameter > 0) {
|
||||
// Check if we are splitting same curve multiple times, but avoid
|
||||
// dividing with zero.
|
||||
if (prev && prev._curve === loc._curve && prev._parameter > 0) {
|
||||
// Scale parameter after previous split.
|
||||
t /= prevLoc._parameter;
|
||||
t /= prev._parameter;
|
||||
} else {
|
||||
curve = loc._curve;
|
||||
if (linearHandles)
|
||||
resetLinear();
|
||||
curve = loc._curve;
|
||||
linearHandles = curve.isLinear() && [];
|
||||
if (linearHandles)
|
||||
linearHandles.push(curve._segment1._handleOut,
|
||||
curve._segment2._handleIn);
|
||||
linearHandles = curve.isLinear() ? [
|
||||
curve._segment1._handleOut,
|
||||
curve._segment2._handleIn
|
||||
] : null;
|
||||
}
|
||||
var newCurve,
|
||||
segment;
|
||||
|
@ -209,9 +228,9 @@ PathItem.inject(new function() {
|
|||
if (linearHandles)
|
||||
linearHandles.push(segment._handleOut, segment._handleIn);
|
||||
} else {
|
||||
segment = t < tolerance
|
||||
segment = t < tMin
|
||||
? curve._segment1
|
||||
: t > 1 - tolerance
|
||||
: t > tMax
|
||||
? curve._segment2
|
||||
: curve.getPartLength(0, t) < curve.getPartLength(t, 1)
|
||||
? curve._segment1
|
||||
|
@ -220,7 +239,7 @@ PathItem.inject(new function() {
|
|||
// Link the new segment with the intersection on the other curve
|
||||
segment._intersection = loc.getIntersection();
|
||||
loc._segment = segment;
|
||||
prevLoc = loc;
|
||||
prev = loc;
|
||||
}
|
||||
if (linearHandles)
|
||||
resetLinear();
|
||||
|
@ -231,14 +250,7 @@ PathItem.inject(new function() {
|
|||
* with respect to a given set of monotone curves.
|
||||
*/
|
||||
function getWinding(point, curves, horizontal, testContains) {
|
||||
// We need to use a smaller tolerance here than in the rest of the
|
||||
// library when dealing with curve time parameters and coordinates, in
|
||||
// order to get really precise values for winding tests. 1e-7 was
|
||||
// determined through a lot of trial and error, and boolean-test suites.
|
||||
// Further decreasing it produces new errors.
|
||||
// The value of 1e-7 also solves issue #559:
|
||||
// https://github.com/paperjs/paper.js/issues/559
|
||||
var tolerance = 1e-7,
|
||||
var tolerance = /*#=*/Numerical.TOLERANCE,
|
||||
tMin = tolerance,
|
||||
tMax = 1 - tMin,
|
||||
x = point.x,
|
||||
|
@ -344,10 +356,6 @@ PathItem.inject(new function() {
|
|||
* @return {Path[]} the contours traced
|
||||
*/
|
||||
function tracePaths(segments, operator, selfOp) {
|
||||
// Choose a default operator which will return all contours
|
||||
operator = operator || function() {
|
||||
return true;
|
||||
};
|
||||
var paths = [],
|
||||
// Values for getTangentAt() that are almost 0 and 1.
|
||||
// TODO: Correctly support getTangentAt(0) / (1)?
|
||||
|
@ -485,9 +493,7 @@ PathItem.inject(new function() {
|
|||
* @return {PathItem} the resulting path item
|
||||
*/
|
||||
unite: function(path) {
|
||||
return computeBoolean(this, path, function(w) {
|
||||
return w === 1 || w === 0;
|
||||
}, false);
|
||||
return computeBoolean(this, path, 'unite');
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -498,9 +504,7 @@ PathItem.inject(new function() {
|
|||
* @return {PathItem} the resulting path item
|
||||
*/
|
||||
intersect: function(path) {
|
||||
return computeBoolean(this, path, function(w) {
|
||||
return w === 2;
|
||||
}, false);
|
||||
return computeBoolean(this, path, 'intersect');
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -511,9 +515,7 @@ PathItem.inject(new function() {
|
|||
* @return {PathItem} the resulting path item
|
||||
*/
|
||||
subtract: function(path) {
|
||||
return computeBoolean(this, path, function(w) {
|
||||
return w === 1;
|
||||
}, true);
|
||||
return computeBoolean(this, path, 'subtract');
|
||||
},
|
||||
|
||||
// Compound boolean operators combine the basic boolean operations such
|
||||
|
@ -526,7 +528,7 @@ PathItem.inject(new function() {
|
|||
* @return {Group} the resulting group item
|
||||
*/
|
||||
exclude: function(path) {
|
||||
return new Group([this.subtract(path), path.subtract(this)]);
|
||||
return computeBoolean(this, path, 'exclude');
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue