mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-07 13:22:07 -05:00
Find a better implementation for exclude() boolean operations, requiring only one pass instead of two.
This commit is contained in:
parent
f0f98daf69
commit
390ef324f2
1 changed files with 34 additions and 23 deletions
|
@ -32,30 +32,51 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PathItem.inject(new function() {
|
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
|
// Boolean operators return true if a curve with the given winding
|
||||||
// contribution contributes to the final result or not. They are called
|
// contribution contributes to the final result or not. They are called
|
||||||
// for each curve in the graph after curves in the operands are
|
// for each curve in the graph after curves in the operands are
|
||||||
// split at intersections.
|
// 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
|
// Creates a cloned version of the path that we can modify freely, with
|
||||||
// its matrix applied to its geometry. Calls #reduce() to simplify
|
// its matrix applied to its geometry. Calls #reduce() to simplify
|
||||||
// compound paths and remove empty curves, and #reorient() to make sure
|
// compound paths and remove empty curves, and #reorient() to make sure
|
||||||
// all paths have correct winding direction.
|
// all paths have correct winding direction.
|
||||||
function preparePath(path) {
|
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
|
// We do not modify the operands themselves, but create copies instead,
|
||||||
// The result might not belong to the same type
|
// 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.
|
// i.e. subtraction(A:Path, B:Path):CompoundPath etc.
|
||||||
var _path1 = preparePath(path1),
|
var _path1 = preparePath(path1),
|
||||||
_path2 = path2 && path1 !== path2 && preparePath(path2);
|
_path2 = path2 && path1 !== path2 && preparePath(path2);
|
||||||
// Do operator specific calculations before we begin
|
// Give both paths clockwise orientation except for subtraction
|
||||||
// Make both paths at clockwise orientation, except when subtract = true
|
// and exclusion, where we need them at opposite orientation.
|
||||||
// We need both paths at opposite orientation for subtraction.
|
|
||||||
if (!_path1.isClockwise())
|
if (!_path1.isClockwise())
|
||||||
_path1.reverse();
|
_path1.reverse();
|
||||||
if (_path2 && !(subtract ^ _path2.isClockwise()))
|
if (_path2 && !(/^(subtract|exclude)$/.test(operation)
|
||||||
|
^ _path2.isClockwise()))
|
||||||
_path2.reverse();
|
_path2.reverse();
|
||||||
// Split curves at intersections on both paths. Note that for self
|
// Split curves at intersections on both paths. Note that for self
|
||||||
// intersection, _path2 will be null and getIntersections() handles it.
|
// intersection, _path2 will be null and getIntersections() handles it.
|
||||||
|
@ -134,7 +155,7 @@ PathItem.inject(new function() {
|
||||||
// While subtracting, we need to omit this curve if this
|
// While subtracting, we need to omit this curve if this
|
||||||
// curve is contributing to the second operand and is
|
// curve is contributing to the second operand and is
|
||||||
// outside the first operand.
|
// outside the first operand.
|
||||||
windingSum += subtract && _path2
|
windingSum += operation === 'subtract' && _path2
|
||||||
&& (path === _path1 && _path2._getWinding(pt, hor)
|
&& (path === _path1 && _path2._getWinding(pt, hor)
|
||||||
|| path === _path2 && !_path1._getWinding(pt, hor))
|
|| path === _path2 && !_path1._getWinding(pt, hor))
|
||||||
? 0
|
? 0
|
||||||
|
@ -340,10 +361,6 @@ PathItem.inject(new function() {
|
||||||
* @return {Path[]} the contours traced
|
* @return {Path[]} the contours traced
|
||||||
*/
|
*/
|
||||||
function tracePaths(segments, operator, selfOp) {
|
function tracePaths(segments, operator, selfOp) {
|
||||||
// Choose a default operator which will return all contours
|
|
||||||
operator = operator || function() {
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
var paths = [],
|
var paths = [],
|
||||||
// Values for getTangentAt() that are almost 0 and 1.
|
// Values for getTangentAt() that are almost 0 and 1.
|
||||||
// TODO: Correctly support getTangentAt(0) / (1)?
|
// TODO: Correctly support getTangentAt(0) / (1)?
|
||||||
|
@ -481,9 +498,7 @@ PathItem.inject(new function() {
|
||||||
* @return {PathItem} the resulting path item
|
* @return {PathItem} the resulting path item
|
||||||
*/
|
*/
|
||||||
unite: function(path) {
|
unite: function(path) {
|
||||||
return computeBoolean(this, path, function(w) {
|
return computeBoolean(this, path, 'unite');
|
||||||
return w === 1 || w === 0;
|
|
||||||
}, false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -494,9 +509,7 @@ PathItem.inject(new function() {
|
||||||
* @return {PathItem} the resulting path item
|
* @return {PathItem} the resulting path item
|
||||||
*/
|
*/
|
||||||
intersect: function(path) {
|
intersect: function(path) {
|
||||||
return computeBoolean(this, path, function(w) {
|
return computeBoolean(this, path, 'intersect');
|
||||||
return w === 2;
|
|
||||||
}, false);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -507,9 +520,7 @@ PathItem.inject(new function() {
|
||||||
* @return {PathItem} the resulting path item
|
* @return {PathItem} the resulting path item
|
||||||
*/
|
*/
|
||||||
subtract: function(path) {
|
subtract: function(path) {
|
||||||
return computeBoolean(this, path, function(w) {
|
return computeBoolean(this, path, 'subtract');
|
||||||
return w === 1;
|
|
||||||
}, true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Compound boolean operators combine the basic boolean operations such
|
// Compound boolean operators combine the basic boolean operations such
|
||||||
|
@ -522,7 +533,7 @@ PathItem.inject(new function() {
|
||||||
* @return {Group} the resulting group item
|
* @return {Group} the resulting group item
|
||||||
*/
|
*/
|
||||||
exclude: function(path) {
|
exclude: function(path) {
|
||||||
return new Group([this.subtract(path), path.subtract(this)]);
|
return computeBoolean(this, path, 'exclude');
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in a new issue