More computeBoolean() optimizations and clean up.

This commit is contained in:
Jürg Lehni 2013-05-03 23:03:00 -07:00
parent 1031f4ecfb
commit 4a9e3924c6

View file

@ -76,9 +76,8 @@ PathItem.inject(new function() {
* @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)
*/ */
function reorientCompoundPath(path) { function reorientPath(path) {
if (!(path instanceof CompoundPath)) if (path instanceof CompoundPath) {
return path.isClockwise();
var children = path._children, var children = path._children,
length = children.length, length = children.length,
bounds = new Array(length), bounds = new Array(length),
@ -100,20 +99,20 @@ PathItem.inject(new function() {
children[i].setClockwise(clockwise); children[i].setClockwise(clockwise);
} }
} }
return clockwise; }
return path;
} }
function computeBoolean(path1, path2, operator, subtract, _cache) { function computeBoolean(path1, path2, operator, subtract, _cache) {
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.
var _path1 = path1.clone(), var _path1 = reorientPath(path1.clone()),
_path2 = path2.clone(), _path2 = reorientPath(path2.clone()),
path1Clockwise = _path1.isClockwise(),
path2Clockwise = _path2.isClockwise(),
path1Id = _path1.id, path1Id = _path1.id,
path2Id = _path2.id, path2Id = _path2.id,
path1Clockwise = reorientCompoundPath(_path1),
path2Clockwise = reorientCompoundPath(_path2),
// Calculate all the intersections // Calculate all the intersections
intersections = _cache && _cache.intersections intersections = _cache && _cache.intersections
|| _path1.getIntersections(_path2); || _path1.getIntersections(_path2);
@ -133,88 +132,80 @@ PathItem.inject(new function() {
path2Clockwise = !path2Clockwise; path2Clockwise = !path2Clockwise;
} }
var paths = [], var paths = []
.concat(_path1._children || [_path1])
.concat(_path2._children || [_path2]),
nodes = [], nodes = [],
result = new CompoundPath(), result = new CompoundPath();
push = paths.push;
if (_path1 instanceof CompoundPath) {
push.apply(paths, _path1._children);
} else {
paths.push(_path1);
}
if (_path2 instanceof CompoundPath) {
push.apply(paths, _path2._children);
} else {
paths.push(_path2);
}
// Step 1: Discard invalid links according to the boolean operator // Step 1: Discard invalid links according to the boolean operator
for (var i = 0, l = paths.length; i < l; i++) { for (var i = 0, l = paths.length; i < l; i++) {
var path = paths[i], var path = paths[i],
insidePath1 = false, parent = path._parent,
insidePath2 = false, id = parent instanceof CompoundPath ? parent._id : path._id,
thisId = path.parent instanceof CompoundPath
? path.parent.id : path.id,
clockwise = path.isClockwise(), clockwise = path.isClockwise(),
segments = path._segments; segments = path._segments,
for (var j = 0, k = segments.length; j < k; j++) { insidePath1 = false,
insidePath2 = false;
for (var j = segments.length - 1; j >= 0; j--) {
var segment = segments[j], var segment = segments[j],
curve = segment.getCurve(), midPoint = segment.getCurve().getPoint(0.5);
midPoint = curve.getPoint(0.5); if (id !== path1Id) {
if (thisId !== path1Id) {
insidePath1 = _path1.contains(midPoint) insidePath1 = _path1.contains(midPoint)
&& (clockwise === path1Clockwise || subtract && (clockwise === path1Clockwise || subtract
|| !testOnCurve(_path1, midPoint)); || !testOnCurve(_path1, midPoint));
} }
if (thisId !== path2Id) { if (id !== path2Id) {
insidePath2 = _path2.contains(midPoint) insidePath2 = _path2.contains(midPoint)
&& (clockwise === path2Clockwise && (clockwise === path2Clockwise
|| !testOnCurve(_path2, midPoint)); || !testOnCurve(_path2, midPoint));
} }
if (operator(thisId === path1Id, insidePath1, insidePath2)) { if (operator(id === path1Id, insidePath1, insidePath2)) {
curve._INVALID = true; segment._INVALID = true;
// markPoint(midPoint, '+'); // markPoint(midPoint, '+');
} else {
nodes.push(segment);
} }
} }
nodes = nodes.concat(path._segments);
} }
// Step 2: Retrieve the resulting paths from the graph // Step 2: Retrieve the resulting paths from the graph
for (var i = 0, l = nodes.length; i < l; i++) { for (var i = 0, l = nodes.length; i < l; i++) {
var node = nodes[i]; var node = nodes[i];
if (node.curve._INVALID || node._visited) { continue; } if (node._visited)
continue;
var path = node.path, var path = node.path,
thisId = (path.parent instanceof CompoundPath)? path.parent.id : path.id,
nuPath = new Path(), nuPath = new Path(),
firstNode = null, firstNode = node,
firstNode_ix = null; firstNode_ix = null;
if (node.previous.curve._INVALID) { if (node.getPrevious()._INVALID) {
node.setHandleIn(node._ixPair node.setHandleIn(node._ixPair
? node._ixPair.getIntersection().__segment._handleIn ? node._ixPair.getIntersection().__segment._handleIn
: Point.create(0, 0)); : Point.create(0, 0));
} }
while (node && !node._visited && (node !== firstNode && node !== firstNode_ix)) { while (node && !node._visited && node !== firstNode_ix) {
node._visited = true; node._visited = true;
firstNode = firstNode || node; firstNode_ix = firstNode_ix || firstNode._ixPair
firstNode_ix = !firstNode_ix && firstNode._ixPair && firstNode._ixPair.getIntersection().__segment;
? 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
var nextNode = (node._ixPair && node.curve._INVALID)? node._ixPair.getIntersection().__segment : node; var nextNode = node._ixPair && node._INVALID
? node._ixPair.getIntersection().__segment
: node;
if (node._ixPair) { if (node._ixPair) {
nextNode._visited = true;
nuPath.add(new Segment(node._point, node._handleIn, nuPath.add(new Segment(node._point, node._handleIn,
nextNode._handleOut)); nextNode._handleOut));
nextNode._visited = true;
node = nextNode; node = nextNode;
} else { } else {
nuPath.add(node); nuPath.add(node);
} }
node = node.next; node = node.getNext();
} }
// 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.closed = true; nuPath.setClosed(true);
result.addChild(nuPath, true); result.addChild(nuPath, true);
} else {
nuPath.remove();
} }
} }
// Delete the proxies // Delete the proxies