Start converting boolean code to Paper.js conventions.

- Tabs instead of white-space
- Different rules about spaces before / after parenthesis
This commit is contained in:
Jürg Lehni 2013-05-03 16:21:44 -07:00
parent 26cb5791bc
commit 1fe83a482f

View file

@ -35,41 +35,41 @@ PathItem.inject({
/** /**
* A boolean operator is a binary operator function of the form * A boolean operator is a binary operator function of the form
* f( isPath1:boolean, isInsidePath1:Boolean, isInsidePath2:Boolean ) :Boolean * f(isPath1:boolean, isInsidePath1:Boolean, isInsidePath2:Boolean) :Boolean
* *
* Boolean operator determines whether a curve segment in the operands is part * Boolean operator determines whether a curve segment in the operands is part
* of the boolean result, and will be called for each curve segment in the graph after * of the boolean result, and will be called for each curve segment in the graph after
* all the intersections between the operands are calculated and curves in the operands * all the intersections between the operands are calculated and curves in the operands
* are split at intersections. * are split at intersections.
* *
* These functions should have a name ( "union", "subtraction" etc. below ), if we need to * These functions should have a name ("union", "subtraction" etc. below), if we need to
* do operator specific operations on paths inside the computeBoolean function. * do operator specific operations on paths inside the computeBoolean function.
* for example: if the name of the operator is "subtraction" then we need to reverse the second * for example: if the name of the operator is "subtraction" then we need to reverse the second
* operand. Subtraction is neither associative nor commutative. * operand. Subtraction is neither associative nor commutative.
* *
* The boolean operator should return a Boolean value indicating whether to keep the curve or not. * The boolean operator should return a Boolean value indicating whether to keep the curve or not.
* return true - keep the curve * return true - keep the curve
* return false - discard the curve * return false - discard the curve
*/ */
unite: function( path, _cache ){ unite: function(path, _cache) {
var unionOp = function union( isPath1, isInsidePath1, isInsidePath2 ){ var unionOp = function union(isPath1, isInsidePath1, isInsidePath2) {
return ( isInsidePath1 || isInsidePath2 )? false : true; return (isInsidePath1 || isInsidePath2)? false : true;
}; };
return this._computeBoolean( this, path, unionOp, _cache ); return this._computeBoolean(this, path, unionOp, _cache);
}, },
intersect: function( path, _cache ){ intersect: function(path, _cache) {
var intersectionOp = function intersection( isPath1, isInsidePath1, isInsidePath2 ){ var intersectionOp = function intersection(isPath1, isInsidePath1, isInsidePath2) {
return ( !isInsidePath1 && !isInsidePath2 )? false : true; return (!isInsidePath1 && !isInsidePath2)? false : true;
}; };
return this._computeBoolean( this, path, intersectionOp, _cache ); return this._computeBoolean(this, path, intersectionOp, _cache);
}, },
subtract: function( path, _cache ){ subtract: function(path, _cache) {
var subtractionOp = function subtraction( isPath1, isInsidePath1, isInsidePath2 ){ var subtractionOp = function subtraction(isPath1, isInsidePath1, isInsidePath2) {
return ( (isPath1 && isInsidePath2) || (!isPath1 && !isInsidePath1) )? false : true; return ((isPath1 && isInsidePath2) || (!isPath1 && !isInsidePath1))? false : true;
}; };
return this._computeBoolean( this, path, subtractionOp, _cache ); return this._computeBoolean(this, path, subtractionOp, _cache);
}, },
/* /*
@ -79,86 +79,86 @@ PathItem.inject({
* TODO: cache the split objects and find a way to properly clone them! * TODO: cache the split objects and find a way to properly clone them!
*/ */
// a.k.a. eXclusiveOR // a.k.a. eXclusiveOR
exclude: function( path ){ exclude: function(path) {
var res1 = this.subtract( path ); var res1 = this.subtract(path);
var res2 = path.subtract( this ); var res2 = path.subtract(this);
var res = new Group( [res1, res2] ); var res = new Group([res1, res2]);
return res; return res;
}, },
// Divide path1 by path2 // Divide path1 by path2
divide: function( path ){ divide: function(path) {
var res1 = this.subtract( path ); var res1 = this.subtract(path);
var res2 = this.intersect( path ); var res2 = this.intersect(path);
var res = new Group( [res1, res2] ); var res = new Group([res1, res2]);
return res; return res;
}, },
_splitPath: function( _ixs, other ) { _splitPath: function(_ixs, other) {
// Sort function for sorting intersections in the descending order // Sort function for sorting intersections in the descending order
function sortIx( a, b ) { return b.parameter - a.parameter; } function sortIx(a, b) { return b.parameter - a.parameter; }
other = other || false; other = other || false;
var i, j, k, l, len, ixs, ix, path, crv, vals; var i, j, k, l, len, ixs, ix, path, crv, vals;
var ixPoint, nuSeg; var ixPoint, nuSeg;
var paths = {}, lastPathId = null; var paths = {}, lastPathId = null;
for (i = 0, l = _ixs.length; i < l; i++) { for (i = 0, l = _ixs.length; i < l; i++) {
ix = ( other )? _ixs[i].getIntersection() : _ixs[i]; ix = (other)? _ixs[i].getIntersection() : _ixs[i];
if( !paths[ix.path.id] ){ if (!paths[ix.path.id]) {
paths[ix.path.id] = ix.path; paths[ix.path.id] = ix.path;
} }
if( !ix.curve._ixParams ){ix.curve._ixParams = []; } if (!ix.curve._ixParams) {ix.curve._ixParams = []; }
ix.curve._ixParams.push( { parameter: ix.parameter, pair: ix.getIntersection() } ); ix.curve._ixParams.push({ parameter: ix.parameter, pair: ix.getIntersection() });
} }
for (k in paths) { for (k in paths) {
if( !paths.hasOwnProperty( k ) ){ continue; } if (!paths.hasOwnProperty(k)) { continue; }
path = paths[k]; path = paths[k];
var lastNode = path.lastSegment, firstNode = path.firstSegment; var lastNode = path.lastSegment, firstNode = path.firstSegment;
var nextNode = null, left = null, right = null, parts = null, isLinear; var nextNode = null, left = null, right = null, parts = null, isLinear;
var handleIn, handleOut; var handleIn, handleOut;
while( nextNode !== firstNode){ while (nextNode !== firstNode) {
nextNode = ( nextNode )? nextNode.previous: lastNode; nextNode = (nextNode)? nextNode.previous: lastNode;
if( nextNode.curve._ixParams ){ if (nextNode.curve._ixParams) {
ixs = nextNode.curve._ixParams; ixs = nextNode.curve._ixParams;
ixs.sort( sortIx ); ixs.sort(sortIx);
crv = nextNode.getCurve(); crv = nextNode.getCurve();
isLinear = crv.isLinear(); isLinear = crv.isLinear();
crv = vals = null; crv = vals = null;
for (i = 0, l = ixs.length; i < l; i++) { for (i = 0, l = ixs.length; i < l; i++) {
ix = ixs[i]; ix = ixs[i];
crv = nextNode.getCurve(); crv = nextNode.getCurve();
if( !vals ) vals = crv.getValues(); if (!vals) vals = crv.getValues();
if( ix.parameter === 0.0 || ix.parameter === 1.0 ){ if (ix.parameter === 0.0 || ix.parameter === 1.0) {
// Intersection is on an existing node // Intersection is on an existing node
// no need to create a new segment, // no need to create a new segment,
// we just link the corresponding intersections together // we just link the corresponding intersections together
nuSeg = ( ix.parameter === 0.0 )? crv.segment1 : crv.segment2; nuSeg = (ix.parameter === 0.0)? crv.segment1 : crv.segment2;
nuSeg._ixPair = ix.pair; nuSeg._ixPair = ix.pair;
nuSeg._ixPair._segment = nuSeg; nuSeg._ixPair._segment = nuSeg;
} else { } else {
parts = Curve.subdivide( vals, ix.parameter ); parts = Curve.subdivide(vals, ix.parameter);
left = parts[0]; left = parts[0];
right = parts[1]; right = parts[1];
handleIn = handleOut = null; handleIn = handleOut = null;
ixPoint = new Point( right[0], right[1] ); ixPoint = new Point(right[0], right[1]);
if( !isLinear ){ if (!isLinear) {
crv.segment1.handleOut = new Point( left[2] - left[0], left[3] - left[1] ); crv.segment1.handleOut = new Point(left[2] - left[0], left[3] - left[1]);
crv.segment2.handleIn = new Point( right[4] - right[6], right[5] - right[7] ); crv.segment2.handleIn = new Point(right[4] - right[6], right[5] - right[7]);
handleIn = new Point( left[4] - ixPoint.x, left[5] - ixPoint.y ); handleIn = new Point(left[4] - ixPoint.x, left[5] - ixPoint.y);
handleOut = new Point( right[2] - ixPoint.x, right[3] - ixPoint.y ); handleOut = new Point(right[2] - ixPoint.x, right[3] - ixPoint.y);
} }
nuSeg = new Segment( ixPoint, handleIn, handleOut ); nuSeg = new Segment(ixPoint, handleIn, handleOut);
nuSeg._ixPair = ix.pair; nuSeg._ixPair = ix.pair;
nuSeg._ixPair._segment = nuSeg; nuSeg._ixPair._segment = nuSeg;
path.insert( nextNode.index + 1, nuSeg ); path.insert(nextNode.index + 1, nuSeg);
} }
for (j = i + 1; j < l; j++) { for (j = i + 1; j < l; j++) {
ixs[j].parameter = ixs[j].parameter / ix.parameter; ixs[j].parameter = ixs[j].parameter / ix.parameter;
} }
vals = left; vals = left;
} }
} }
} }
} }
}, },
/** /**
@ -166,207 +166,207 @@ PathItem.inject({
* has to be of different winding direction for correctly filling holes. * has to be of different winding direction for correctly filling holes.
* But if some individual countours are disjoint, i.e. islands, we have to * But if some individual countours are disjoint, i.e. islands, we have to
* reorient them so that * reorient them so that
* the holes have opposit winding direction ( already handled by paperjs ) * the holes have opposit winding direction (already handled by paperjs)
* islands has to have same winding direction ( as the first child of the path ) * islands has to have same winding direction (as the first child of the path)
* *
* Does NOT handle selfIntersecting CompoundPaths. * Does NOT handle selfIntersecting CompoundPaths.
* *
* @param {CompoundPath} path - Input CompoundPath, Note: This path could be modified if need be. * @param {CompoundPath} path - Input CompoundPath, Note: This path could be modified if need be.
* @return {boolean} the winding direction of the base contour( true if clockwise ) * @return {boolean} the winding direction of the base contour(true if clockwise)
*/ */
_reorientCompoundPath: function( path ){ _reorientCompoundPath: function(path) {
if( !(path instanceof CompoundPath) ){ if (!(path instanceof CompoundPath)) {
path.closed = true; path.closed = true;
return path.clockwise; return path.clockwise;
} }
var children = path.children, len = children.length, baseWinding; var children = path.children, len = children.length, baseWinding;
var bounds = new Array( len ); var bounds = new Array(len);
var tmparray = new Array( len ); var tmparray = new Array(len);
baseWinding = children[0].clockwise; baseWinding = children[0].clockwise;
// Omit the first path // Omit the first path
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
children[i].closed = true; children[i].closed = true;
bounds[i] = children[i].bounds; bounds[i] = children[i].bounds;
tmparray[i] = 0; tmparray[i] = 0;
} }
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
var p1 = children[i]; var p1 = children[i];
for (j = 0; j < len; j++) { for (j = 0; j < len; j++) {
var p2 = children[j]; var p2 = children[j];
if( i !== j && bounds[i].contains( bounds[j] ) ){ if (i !== j && bounds[i].contains(bounds[j])) {
tmparray[j]++; tmparray[j]++;
} }
} }
} }
for (i = 1; i < len; i++) { for (i = 1; i < len; i++) {
if ( tmparray[i] % 2 === 0 ) { if (tmparray[i] % 2 === 0) {
children[i].clockwise = baseWinding; children[i].clockwise = baseWinding;
} }
} }
return baseWinding; return baseWinding;
}, },
_reversePath: function( path ){ _reversePath: function(path) {
var baseWinding; var baseWinding;
if( path instanceof CompoundPath ){ if (path instanceof CompoundPath) {
var children = path.children, i, len; var children = path.children, i, len;
for (i = 0, len = children.length; i < len; i++) { for (i = 0, len = children.length; i < len; i++) {
children[i].reverse(); children[i].reverse();
children[i]._curves = null; children[i]._curves = null;
} }
baseWinding = children[0].clockwise; baseWinding = children[0].clockwise;
} else { } else {
path.reverse(); path.reverse();
baseWinding = path.clockwise; baseWinding = path.clockwise;
path._curves = null; path._curves = null;
} }
return baseWinding; return baseWinding;
}, },
_computeBoolean: function( path1, path2, operator, _splitCache ){ _computeBoolean: function(path1, path2, operator, _splitCache) {
var _path1, _path2, path1Clockwise, path2Clockwise; var _path1, _path2, path1Clockwise, path2Clockwise;
var ixs, path1Id, path2Id; var ixs, path1Id, path2Id;
// We do not modify the operands themselves // We do not modify the operands themselves
// The result might not belong to the same type // The result 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.
_path1 = path1.clone(); _path1 = path1.clone();
_path2 = path2.clone(); _path2 = path2.clone();
_path1.style = _path2.style = null; _path1.style = _path2.style = null;
_path1.selected = _path2.selected = false; _path1.selected = _path2.selected = false;
path1Clockwise = this._reorientCompoundPath( _path1 ); path1Clockwise = this._reorientCompoundPath(_path1);
path2Clockwise = this._reorientCompoundPath( _path2 ); path2Clockwise = this._reorientCompoundPath(_path2);
path1Id = _path1.id; path1Id = _path1.id;
path2Id = _path2.id; path2Id = _path2.id;
// Calculate all the intersections // Calculate all the intersections
ixs = ( _splitCache && _splitCache.intersections )? ixs = (_splitCache && _splitCache.intersections)?
_splitCache.intersections : _path1.getIntersections( _path2 ); _splitCache.intersections : _path1.getIntersections(_path2);
// if we have a empty _splitCache object as an operand, // if we have a empty _splitCache object as an operand,
// skip calculating boolean and cache the intersections // skip calculating boolean and cache the intersections
if( _splitCache && !_splitCache.intersections ){ if (_splitCache && !_splitCache.intersections) {
_splitCache.intersections = ixs; _splitCache.intersections = ixs;
return; return;
} }
this._splitPath( ixs ); this._splitPath(ixs);
this._splitPath( ixs, true ); this._splitPath(ixs, true);
path1Id = _path1.id; path1Id = _path1.id;
path2Id = _path2.id; path2Id = _path2.id;
// Do operator specific calculations before we begin // Do operator specific calculations before we begin
if( operator.name === "subtraction" ) { if (operator.name === "subtraction") {
path2Clockwise = this._reversePath( _path2 ); path2Clockwise = this._reversePath(_path2);
} }
var i, j, len, path, crv; var i, j, len, path, crv;
var paths = []; var paths = [];
if( _path1 instanceof CompoundPath ){ if (_path1 instanceof CompoundPath) {
paths = paths.concat( _path1.children ); paths = paths.concat(_path1.children);
} else { } else {
paths = [ _path1 ]; paths = [ _path1 ];
} }
if( _path2 instanceof CompoundPath ){ if (_path2 instanceof CompoundPath) {
paths = paths.concat( _path2.children ); paths = paths.concat(_path2.children);
} else { } else {
paths.push( _path2 ); paths.push(_path2);
} }
// step 1: discard invalid links according to the boolean operator // step 1: discard invalid links according to the boolean operator
var lastNode, firstNode, nextNode, midPoint, insidePath1, insidePath2; var lastNode, firstNode, nextNode, midPoint, insidePath1, insidePath2;
var thisId, thisWinding, contains, subtractionOp = (operator.name === 'subtraction'); var thisId, thisWinding, contains, subtractionOp = (operator.name === 'subtraction');
for (i = 0, len = paths.length; i < len; i++) { for (i = 0, len = paths.length; i < len; i++) {
insidePath1 = insidePath2 = false; insidePath1 = insidePath2 = false;
path = paths[i]; path = paths[i];
thisId = ( path.parent instanceof CompoundPath )? path.parent.id : path.id; thisId = (path.parent instanceof CompoundPath)? path.parent.id : path.id;
thisWinding = path.clockwise; thisWinding = path.clockwise;
lastNode = path.lastSegment; lastNode = path.lastSegment;
firstNode = path.firstSegment; firstNode = path.firstSegment;
nextNode = null; nextNode = null;
while( nextNode !== firstNode){ while (nextNode !== firstNode) {
nextNode = ( nextNode )? nextNode.previous: lastNode; nextNode = (nextNode)? nextNode.previous: lastNode;
crv = nextNode.curve; crv = nextNode.curve;
midPoint = crv.getPoint( 0.5 ); midPoint = crv.getPoint(0.5);
if( thisId !== path1Id ){ if (thisId !== path1Id) {
contains = _path1. contains = _path1.
contains( midPoint ); contains(midPoint);
insidePath1 = (thisWinding === path1Clockwise || subtractionOp )? contains : insidePath1 = (thisWinding === path1Clockwise || subtractionOp)? contains :
contains && !this._testOnCurve( _path1, midPoint ); contains && !this._testOnCurve(_path1, midPoint);
} }
if( thisId !== path2Id ){ if (thisId !== path2Id) {
contains = _path2.contains( midPoint ); contains = _path2.contains(midPoint);
insidePath2 = (thisWinding === path2Clockwise )? contains : insidePath2 = (thisWinding === path2Clockwise)? contains :
contains && !this._testOnCurve( _path2, midPoint ); contains && !this._testOnCurve(_path2, midPoint);
} }
if( !operator( thisId === path1Id, insidePath1, insidePath2 ) ){ if (!operator(thisId === path1Id, insidePath1, insidePath2)) {
crv._INVALID = true; crv._INVALID = true;
// markPoint( midPoint, '+' ); // markPoint(midPoint, '+');
} }
} }
} }
// Final step: Retrieve the resulting paths from the graph // Final step: Retrieve the resulting paths from the graph
var boolResult = new CompoundPath(); var boolResult = new CompoundPath();
var node, nuNode, nuPath, nodeList = [], handle; var node, nuNode, nuPath, nodeList = [], handle;
for (i = 0, len = paths.length; i < len; i++) { for (i = 0, len = paths.length; i < len; i++) {
nodeList = nodeList.concat( paths[i].segments ); nodeList = nodeList.concat(paths[i].segments);
} }
for (i = 0, len = nodeList.length; i < len; i++) { for (i = 0, len = nodeList.length; i < len; i++) {
node = nodeList[i]; node = nodeList[i];
if( node.curve._INVALID || node._visited ){ continue; } if (node.curve._INVALID || node._visited) { continue; }
path = node.path; path = node.path;
thisId = ( path.parent instanceof CompoundPath )? path.parent.id : path.id; thisId = (path.parent instanceof CompoundPath)? path.parent.id : path.id;
thisWinding = path.clockwise; thisWinding = path.clockwise;
nuPath = new Path(); nuPath = new Path();
firstNode = null; firstNode = null;
firstNode_ix = null; firstNode_ix = null;
if( node.previous.curve._INVALID ) { if (node.previous.curve._INVALID) {
node.handleIn = ( node._ixPair )? node.handleIn = (node._ixPair)?
node._ixPair.getIntersection()._segment.handleIn : [ 0, 0 ]; node._ixPair.getIntersection()._segment.handleIn : [ 0, 0 ];
} }
while( node && !node._visited && ( node !== firstNode && node !== firstNode_ix ) ){ while (node && !node._visited && (node !== firstNode && node !== firstNode_ix)) {
node._visited = true; node._visited = true;
firstNode = ( firstNode )? firstNode: node; firstNode = (firstNode)? firstNode: node;
firstNode_ix = ( !firstNode_ix && firstNode._ixPair )? firstNode_ix = (!firstNode_ix && firstNode._ixPair)?
firstNode._ixPair.getIntersection()._segment: firstNode_ix; firstNode._ixPair.getIntersection()._segment: firstNode_ix;
// node._ixPair is this node's intersection CurveLocation object // node._ixPair is this node's intersection CurveLocation object
// node._ixPair.getIntersection() is the other CurveLocation object this node intersects with // node._ixPair.getIntersection() is the other CurveLocation object this node intersects with
nextNode = ( node._ixPair && node.curve._INVALID )? node._ixPair.getIntersection()._segment : node; nextNode = (node._ixPair && node.curve._INVALID)? node._ixPair.getIntersection()._segment : node;
if( node._ixPair ) { if (node._ixPair) {
nextNode._visited = true; nextNode._visited = true;
nuNode = new Segment( node.point, node.handleIn, nextNode.handleOut ); nuNode = new Segment(node.point, node.handleIn, nextNode.handleOut);
nuPath.add( nuNode ); nuPath.add(nuNode);
node = nextNode; node = nextNode;
path = node.path; path = node.path;
thisWinding = path.clockwise; thisWinding = path.clockwise;
} else { } else {
nuPath.add( node ); nuPath.add(node);
} }
node = node.next; node = node.next;
} }
if( nuPath.segments.length > 1 ) { if (nuPath.segments.length > 1) {
// avoid stray segments and incomplete paths // avoid stray segments and incomplete paths
if( nuPath.segments.length > 2 || !nuPath.curves[0].isLinear() ){ if (nuPath.segments.length > 2 || !nuPath.curves[0].isLinear()) {
nuPath.closed = true; nuPath.closed = true;
boolResult.addChild( nuPath, true ); boolResult.addChild(nuPath, true);
} }
} }
} }
// Delete the proxies // Delete the proxies
_path1.remove(); _path1.remove();
_path2.remove(); _path2.remove();
// And then, we are done. // And then, we are done.
return boolResult.reduce(); return boolResult.reduce();
}, },
_testOnCurve: function( path, point ){ _testOnCurve: function(path, point) {
var res = 0; var res = 0;
var crv = path.getCurves(); var crv = path.getCurves();
var i = 0; var i = 0;
var bounds = path.bounds; var bounds = path.bounds;
if( bounds && bounds.contains( point ) ){ if (bounds && bounds.contains(point)) {
for( i = 0; i < crv.length && !res; i++ ){ for(i = 0; i < crv.length && !res; i++) {
var crvi = crv[i]; var crvi = crv[i];
if( crvi.bounds.contains( point ) && crvi.getParameterOf( point ) ){ if (crvi.bounds.contains(point) && crvi.getParameterOf(point)) {
res = 1; res = 1;
} }
} }
} }
return res; return res;
} }
}); });