diff --git a/src/path/Path.js b/src/path/Path.js index f5bb4046..1d7d4d3c 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -291,7 +291,8 @@ var Path = PathItem.extend(/** @lends Path# */{ } } }, /** @lends Path# */{ - // Enforce bean creation for getPathData(), as it has hidden parameters. + // Enforce bean creation for getPathData() and getArea(), as they have + // hidden parameters. beans: true, getPathData: function(_matrix, _precision) { @@ -348,8 +349,7 @@ var Path = PathItem.extend(/** @lends Path# */{ parts.push('z'); } return parts.join(''); - } -}, /** @lends Path# */{ + }, // TODO: Consider adding getSubPath(a, b), returning a part of the current // path, with the added benefit that b can be < a, and closed looping is @@ -820,19 +820,25 @@ var Path = PathItem.extend(/** @lends Path# */{ * @bean * @type Number */ - getArea: function() { - if (this._area == null) { + getArea: function(_closed) { + // If the call overrides the 'closed' state, do not cache the result. + // This is used in tracePaths(). + var cached = _closed === undefined, + area = this._area; + if (!cached || area == null) { var segments = this._segments, count = segments.length, - last = count - 1, - area = 0; - for (var i = 0, l = this._closed ? count : last; i < l; i++) { + closed = cached ? this._closed : _closed; + last = count - 1; + area = 0; + for (var i = 0, l = closed ? count : last; i < l; i++) { area += Curve.getArea(Curve.getValues( segments[i], segments[i < last ? i + 1 : 0])); } - this._area = area; + if (cached) + this._area = area; } - return this._area; + return area; }, /** diff --git a/src/path/PathItem.Boolean.js b/src/path/PathItem.Boolean.js index cb416107..a4d99a1b 100644 --- a/src/path/PathItem.Boolean.js +++ b/src/path/PathItem.Boolean.js @@ -661,14 +661,20 @@ PathItem.inject(new function() { path.firstSegment.setHandleIn(handleIn); path.setClosed(true); } else if (path) { - var length = path.getLength(); - // Only complain about open paths if they are long enough. - if (length >= /*#=*/Numerical.GEOMETRIC_EPSILON) { + // Only complain about open paths if they would actually contain + // an area when closed. Such open paths can occur due to + // epsilons, e.g. when two segments are so close to each other + // that they are considered the same, but the winding + // calculation still produces a valid winding due to their + // slight differences. + var area = path.getArea(true); + if (Math.abs(area) >= /*#=*/Numerical.GEOMETRIC_EPSILON) { // This path wasn't finished and is hence invalid. // Report the error to the console for the time being. console.error('Boolean operation resulted in open path', 'segments =', path._segments.length, - 'length =', length); + 'length =', path.getLength(), + 'area=', area); } path = null; }