Boolean: update documentation and optimize #divide()

#divide() with options.trace = false can call splitBoolean() just once without removing any split sub-paths.

Relates to #1221
This commit is contained in:
Jürg Lehni 2017-03-22 23:45:11 +01:00
parent 15471c76ab
commit a43db8427a
2 changed files with 31 additions and 19 deletions

View file

@ -82,7 +82,7 @@ PathItem.inject(new function() {
// based boolean operations (options.split = true). // based boolean operations (options.split = true).
if (options && (options.trace == false || options.stroke) && if (options && (options.trace == false || options.stroke) &&
/^(subtract|intersect)$/.test(operation)) /^(subtract|intersect)$/.test(operation))
return splitBoolean(path1, path2, operation === 'subtract'); return splitBoolean(path1, path2, operation);
// We do not modify the operands themselves, but create copies instead, // We do not modify the operands themselves, but create copies instead,
// fas produced by the calls to preparePath(). // fas produced by the calls to preparePath().
// NOTE: The result paths might not belong to the same type i.e. // NOTE: The result paths might not belong to the same type i.e.
@ -159,19 +159,21 @@ PathItem.inject(new function() {
return createResult(paths, true, path1, path2, options); return createResult(paths, true, path1, path2, options);
} }
function splitBoolean(path1, path2, subtract) { function splitBoolean(path1, path2, operation) {
var _path1 = preparePath(path1), var _path1 = preparePath(path1),
_path2 = preparePath(path2), _path2 = preparePath(path2),
crossings = _path1.getCrossings(_path2), crossings = _path1.getCrossings(_path2),
added = {}, added = {},
paths = []; paths = [],
divide = operation === 'divide',
subtract = operation === 'subtract';
function addPath(path) { function addPath(path) {
// Simple see if the point halfway across the open path is inside // Simple see if the point halfway across the open path is inside
// path2, and include / exclude the path based on the operator. // path2, and include / exclude the path based on the operator.
if (!added[path._id] && if (!added[path._id] && (divide ||
_path2.contains(path.getPointAt(path.getLength() / 2)) _path2.contains(path.getPointAt(path.getLength() / 2))
^ subtract) { ^ subtract)) {
paths.unshift(path); paths.unshift(path);
return added[path._id] = true; return added[path._id] = true;
} }
@ -1051,8 +1053,11 @@ PathItem.inject(new function() {
* @option [options.insert=true] {Boolean} whether the resulting item * @option [options.insert=true] {Boolean} whether the resulting item
* should be inserted back into the scene graph, above both paths * should be inserted back into the scene graph, above both paths
* involved in the operation * involved in the operation
* @option [options.stroke=false] {Boolean} whether the operation should * @option [options.trace=true] {Boolean} whether the tracing method is
* be performed on the stroke or on the fill of the first path * used, treating both paths as areas when determining which parts
* of the paths are to be kept in the result, or whether the first
* path is only to be split at intersections, keeping the parts of
* the curves that intersect with the area of the second path.
* *
* @param {PathItem} path the path to intersect with * @param {PathItem} path the path to intersect with
* @param {Object} [options] the boolean operation options * @param {Object} [options] the boolean operation options
@ -1069,8 +1074,11 @@ PathItem.inject(new function() {
* @option [options.insert=true] {Boolean} whether the resulting item * @option [options.insert=true] {Boolean} whether the resulting item
* should be inserted back into the scene graph, above both paths * should be inserted back into the scene graph, above both paths
* involved in the operation * involved in the operation
* @option [options.stroke=false] {Boolean} whether the operation should * @option [options.trace=true] {Boolean} whether the tracing method is
* be performed on the stroke or on the fill of the first path * used, treating both paths as areas when determining which parts
* of the paths are to be kept in the result, or whether the first
* path is only to be split at intersections, removing the parts of
* the curves that intersect with the area of the second path.
* *
* @param {PathItem} path the path to subtract * @param {PathItem} path the path to subtract
* @param {Object} [options] the boolean operation options * @param {Object} [options] the boolean operation options
@ -1090,7 +1098,7 @@ PathItem.inject(new function() {
* *
* @param {PathItem} path the path to exclude the intersection of * @param {PathItem} path the path to exclude the intersection of
* @param {Object} [options] the boolean operation options * @param {Object} [options] the boolean operation options
* @return {PathItem} the resulting group item * @return {PathItem} the resulting path item
*/ */
exclude: function(path, options) { exclude: function(path, options) {
return traceBoolean(this, path, 'exclude', options); return traceBoolean(this, path, 'exclude', options);
@ -1105,18 +1113,22 @@ PathItem.inject(new function() {
* @option [options.insert=true] {Boolean} whether the resulting item * @option [options.insert=true] {Boolean} whether the resulting item
* should be inserted back into the scene graph, above both paths * should be inserted back into the scene graph, above both paths
* involved in the operation * involved in the operation
* @option [options.stroke=false] {Boolean} whether the operation should * @option [options.trace=true] {Boolean} whether the tracing method is
* be performed on the stroke or on the fill of the first path * used, treating both paths as areas when determining which parts
* of the paths are to be kept in the result, or whether the first
* path is only to be split at intersections.
* *
* @param {PathItem} path the path to divide by * @param {PathItem} path the path to divide by
* @param {Object} [options] the boolean operation options * @param {Object} [options] the boolean operation options
* @return {Group} the resulting group item * @return {PathItem} the resulting path item
*/ */
divide: function(path, options) { divide: function(path, options) {
return createResult([ return options && (options.trace == false || options.stroke)
this.subtract(path, options), ? splitBoolean(this, path, 'divide')
this.intersect(path, options) : createResult([
], true, this, path, options); this.subtract(path, options),
this.intersect(path, options)
], true, this, path, options);
}, },
/* /*

View file

@ -915,8 +915,8 @@ test('#1221', function() {
var division = line.divide(rect4, { trace: false }); var division = line.divide(rect4, { trace: false });
equals(function() { return division.children.length; }, 2); equals(function() { return division.children.length; }, 2);
compareBoolean(function() { return division.children[0]; }, 'M400,400v200'); compareBoolean(function() { return division.children[0]; }, 'M400,300v100');
compareBoolean(function() { return division.children[1]; }, 'M400,300v100'); compareBoolean(function() { return division.children[1]; }, 'M400,400v200');
}); });
test('#1239 / #1073', function() { test('#1239 / #1073', function() {