From 3177c7ac4666552260124d57659846c4f18f7d61 Mon Sep 17 00:00:00 2001 From: sasensi Date: Fri, 23 Nov 2018 11:04:45 +0100 Subject: [PATCH] Fix Path#arcTo() when from/to points are equal Closes #1613 --- CHANGELOG.md | 1 + src/path/Path.js | 81 ++++++++++++++++++++++++---------------------- test/tests/Path.js | 8 +++++ 3 files changed, 51 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b9ffd96..57384e02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Do not ignore `Group#clipItem.matrix` in `Group#internalBounds` (#1427). - Correctly calculate bounds with nested empty items (#1467). - Fix color change propagation on groups (#1152). +- Fix `Path#arcTo()` where `from` and `to` points are equal (#1613). - SVG Export: Fix error when `Item#matrix` is not invertible (#1580). - SVG Export: Include missing viewBox attribute (#1576). - SVG Import: Use correct default values for gradients (#1632, #1661). diff --git a/src/path/Path.js b/src/path/Path.js index 22318354..71454a0d 100644 --- a/src/path/Path.js +++ b/src/path/Path.js @@ -2467,9 +2467,10 @@ new function() { // PostScript-style drawing commands // #2: arcTo(through, to) through = to; to = Point.read(arguments); - } else { + } else if (!from.equals(to)) { // #3: arcTo(to, radius, rotation, clockwise, large) - // Drawing arcs in SVG style: + // Draw arc in SVG style, but only if `from` and `to` are not + // equal (#1613). var radius = Size.read(arguments), isZero = Numerical.isZero; // If rx = 0 or ry = 0 then this arc is treated as a @@ -2565,47 +2566,49 @@ new function() { // PostScript-style drawing commands extent += extent < 0 ? 360 : -360; } } - var epsilon = /*#=*/Numerical.GEOMETRIC_EPSILON, - ext = abs(extent), - // Calculate the amount of segments required to approximate over - // `extend` degrees (extend / 90), but prevent ceil() from - // rounding up small imprecisions by subtracting epsilon first. - count = ext >= 360 ? 4 : Math.ceil((ext - epsilon) / 90), - inc = extent / count, - half = inc * Math.PI / 360, - z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)), - segments = []; - for (var i = 0; i <= count; i++) { - // Explicitly use to point for last segment, since depending - // on values the calculation adds imprecision: - var pt = to, - out = null; - if (i < count) { - out = vector.rotate(90).multiply(z); - if (matrix) { - pt = matrix._transformPoint(vector); - out = matrix._transformPoint(vector.add(out)) - .subtract(pt); + if (extent) { + var epsilon = /*#=*/Numerical.GEOMETRIC_EPSILON, + ext = abs(extent), + // Calculate amount of segments required to approximate over + // `extend` degrees (extend / 90), but prevent ceil() from + // rounding up small imprecisions by subtracting epsilon. + count = ext >= 360 ? 4 : Math.ceil((ext - epsilon) / 90), + inc = extent / count, + half = inc * Math.PI / 360, + z = 4 / 3 * Math.sin(half) / (1 + Math.cos(half)), + segments = []; + for (var i = 0; i <= count; i++) { + // Explicitly use to point for last segment, since depending + // on values the calculation adds imprecision: + var pt = to, + out = null; + if (i < count) { + out = vector.rotate(90).multiply(z); + if (matrix) { + pt = matrix._transformPoint(vector); + out = matrix._transformPoint(vector.add(out)) + .subtract(pt); + } else { + pt = center.add(vector); + } + } + if (!i) { + // Modify startSegment + current.setHandleOut(out); } else { - pt = center.add(vector); + // Add new Segment + var _in = vector.rotate(-90).multiply(z); + if (matrix) { + _in = matrix._transformPoint(vector.add(_in)) + .subtract(pt); + } + segments.push(new Segment(pt, _in, out)); } + vector = vector.rotate(inc); } - if (!i) { - // Modify startSegment - current.setHandleOut(out); - } else { - // Add new Segment - var _in = vector.rotate(-90).multiply(z); - if (matrix) { - _in = matrix._transformPoint(vector.add(_in)) - .subtract(pt); - } - segments.push(new Segment(pt, _in, out)); - } - vector = vector.rotate(inc); + // Add all segments at once at the end for higher performance + this._add(segments); } - // Add all segments at once at the end for higher performance - this._add(segments); }, lineBy: function(/* to */) { diff --git a/test/tests/Path.js b/test/tests/Path.js index 4fce91d9..47678d73 100644 --- a/test/tests/Path.js +++ b/test/tests/Path.js @@ -645,3 +645,11 @@ test('Path#arcTo(through, to) is on through point side (#1477)', function() { path.arcTo(p2, p3); equals(true, path.segments[1].point.x > p1.x); }); + +test('Path#arcTo(to, radius, rotation, clockwise, large) when from and to are equal (#1613)', function(){ + var point = new Point(10,10); + var path = new Path(); + path.moveTo(point); + path.arcTo(point, new Size(10), 0, true, true); + expect(0); +});