Clean up computeBoolean().

Work in progress.
This commit is contained in:
Jürg Lehni 2013-05-03 22:38:29 -07:00
parent d014ed3c68
commit 1031f4ecfb

View file

@ -104,133 +104,124 @@ PathItem.inject(new function() {
} }
function computeBoolean(path1, path2, operator, subtract, _cache) { function computeBoolean(path1, path2, operator, subtract, _cache) {
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(); var _path1 = path1.clone(),
_path2 = path2.clone(); _path2 = path2.clone(),
_path1.style = _path2.style = null; path1Id = _path1.id,
_path1.selected = _path2.selected = false; path2Id = _path2.id,
path1Clockwise = reorientCompoundPath(_path1); path1Clockwise = reorientCompoundPath(_path1),
path2Clockwise = reorientCompoundPath(_path2); path2Clockwise = reorientCompoundPath(_path2),
path1Id = _path1.id;
path2Id = _path2.id;
// Calculate all the intersections // Calculate all the intersections
ixs = _cache && _cache.intersections || _path1.getIntersections(_path2); intersections = _cache && _cache.intersections
// if we have a empty _cache object as an operand, || _path1.getIntersections(_path2);
// skip calculating boolean and cache the intersections // if we have a empty _cache object as an operand, skip calculating
if (_cache && !_cache.intersections) // boolean and cache the intersections
return _cache.intersections = ixs; if (_cache && !_cache.intersections) {
splitPath(splitPath(ixs, true)); // TODO: Don't we need to clear up and remove _path1 & _path2 again?
path1Id = _path1.id; return _cache.intersections = intersections;
path2Id = _path2.id; }
// Now split intersections on both curves, by asking the first call to
// collect the 'other' intersections for us and passing that on to the
// second call.
splitPath(splitPath(intersections, true));
// Do operator specific calculations before we begin // Do operator specific calculations before we begin
if (subtract) { if (subtract) {
_path2.reverse(); _path2.reverse();
path2Clockwise = !path2Clockwise; path2Clockwise = !path2Clockwise;
} }
var i, j, len, path, crv; var paths = [],
var paths = []; nodes = [],
result = new CompoundPath(),
push = paths.push;
if (_path1 instanceof CompoundPath) { if (_path1 instanceof CompoundPath) {
paths = paths.concat(_path1.children); push.apply(paths, _path1._children);
} else { } else {
paths = [ _path1 ]; paths.push(_path1);
} }
if (_path2 instanceof CompoundPath) { if (_path2 instanceof CompoundPath) {
paths = paths.concat(_path2.children); push.apply(paths, _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; for (var i = 0, l = paths.length; i < l; i++) {
var thisId, thisWinding, contains; var path = paths[i],
for (i = 0, len = paths.length; i < len; i++) { insidePath1 = false,
insidePath1 = insidePath2 = false; insidePath2 = false,
path = paths[i]; thisId = path.parent instanceof CompoundPath
thisId = (path.parent instanceof CompoundPath)? path.parent.id : path.id; ? path.parent.id : path.id,
thisWinding = path.clockwise; clockwise = path.isClockwise(),
lastNode = path.lastSegment; segments = path._segments;
firstNode = path.firstSegment; for (var j = 0, k = segments.length; j < k; j++) {
nextNode = null; var segment = segments[j],
while (nextNode !== firstNode) { curve = segment.getCurve(),
nextNode = (nextNode)? nextNode.previous: lastNode; midPoint = curve.getPoint(0.5);
crv = nextNode.curve;
midPoint = crv.getPoint(0.5);
if (thisId !== path1Id) { if (thisId !== path1Id) {
contains = _path1. insidePath1 = _path1.contains(midPoint)
contains(midPoint); && (clockwise === path1Clockwise || subtract
insidePath1 = thisWinding === path1Clockwise || subtract || !testOnCurve(_path1, midPoint));
? contains
: contains && !testOnCurve(_path1, midPoint);
} }
if (thisId !== path2Id) { if (thisId !== path2Id) {
contains = _path2.contains(midPoint); insidePath2 = _path2.contains(midPoint)
insidePath2 = thisWinding === path2Clockwise && (clockwise === path2Clockwise
? contains || !testOnCurve(_path2, midPoint));
: contains && !testOnCurve(_path2, midPoint);
} }
if (operator(thisId === path1Id, insidePath1, insidePath2)) { if (operator(thisId === path1Id, insidePath1, insidePath2)) {
crv._INVALID = true; curve._INVALID = true;
// markPoint(midPoint, '+'); // markPoint(midPoint, '+');
} }
} }
nodes = nodes.concat(path._segments);
} }
// Final step: Retrieve the resulting paths from the graph // Step 2: Retrieve the resulting paths from the graph
var boolResult = new CompoundPath(); for (var i = 0, l = nodes.length; i < l; i++) {
var node, nuNode, nuPath, nodeList = [], handle; var node = nodes[i];
for (i = 0, len = paths.length; i < len; i++) {
nodeList = nodeList.concat(paths[i].segments);
}
for (i = 0, len = nodeList.length; i < len; i++) {
node = nodeList[i];
if (node.curve._INVALID || node._visited) { continue; } if (node.curve._INVALID || node._visited) { continue; }
path = node.path; var 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; 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.setHandleIn(node._ixPair
node._ixPair.getIntersection().__segment.handleIn : [ 0, 0 ]; ? node._ixPair.getIntersection().__segment._handleIn
: Point.create(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 || 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; var 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); nuPath.add(new Segment(node._point, node._handleIn,
nuPath.add(nuNode); nextNode._handleOut));
node = nextNode; node = nextNode;
path = node.path;
thisWinding = path.clockwise;
} else { } else {
nuPath.add(node); nuPath.add(node);
} }
node = node.next; node = node.next;
} }
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); result.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 result.reduce();
} }
function testOnCurve(path, point) { function testOnCurve(path, point) {