diff --git a/config/changelog.yaml b/config/changelog.yaml index 791beef0ca2..dc010fc6f6c 100644 --- a/config/changelog.yaml +++ b/config/changelog.yaml @@ -1,3 +1,13 @@ +2.15.2: + date: 2021-04-28 + + Fixed: + core: + - Path.shiftAlong takes now an optional second paramter to control + the number of steps the path will be divided in per Mm (if it's a curve) + default is 25 + See [#976](https://github.com/freesewing/freesewing/issues/976) + 2.15.1: date: 2021-04-24 diff --git a/packages/core/src/path.js b/packages/core/src/path.js index 189547c0fa0..796a95cfe03 100644 --- a/packages/core/src/path.js +++ b/packages/core/src/path.js @@ -420,7 +420,7 @@ function joinPaths(paths, closed = false, raise = false) { } /** Returns a point that lies at distance along this */ -Path.prototype.shiftAlong = function (distance) { +Path.prototype.shiftAlong = function (distance, stepsPerMm = 25) { if (typeof distance !== 'number') this.raise.error('Called `Path.shiftAlong(distance)` but `distance` is not a number') let len = 0 @@ -439,7 +439,8 @@ Path.prototype.shiftAlong = function (distance) { { x: op.to.x, y: op.to.y } ) let thisLen = bezier.length() - if (len + thisLen > distance) return shiftAlongBezier(distance - len, bezier) + if (len + thisLen > distance) + return shiftAlongBezier(distance - len, bezier, thisLen * stepsPerMm) else len += thisLen } current = op.to @@ -450,15 +451,14 @@ Path.prototype.shiftAlong = function (distance) { } /** Returns a point that lies at fraction along this */ -Path.prototype.shiftFractionAlong = function (fraction) { +Path.prototype.shiftFractionAlong = function (fraction, stepsPerMm = 25) { if (typeof fraction !== 'number') this.raise.error('Called `Path.shiftFractionAlong(fraction)` but `fraction` is not a number') - return this.shiftAlong(this.length() * fraction) + return this.shiftAlong(this.length() * fraction, stepsPerMm) } /** Returns a point that lies at distance along bezier */ -function shiftAlongBezier(distance, bezier) { - let steps = 100 +function shiftAlongBezier(distance, bezier, steps = 100) { let previous, next, t, thisLen let len = 0 for (let i = 0; i <= steps; i++) { diff --git a/packages/core/tests/path.test.js b/packages/core/tests/path.test.js index f1f48e0895e..8edb5f579ec 100644 --- a/packages/core/tests/path.test.js +++ b/packages/core/tests/path.test.js @@ -38,8 +38,8 @@ it("Should offset a curve where cp1 = start", () => { .curve(new a.Point(0, 0), new a.Point(123, 34), new a.Point(23, 4)); a.paths.offset = a.paths.curve.offset(10); pattern.render(); - expect(round(a.paths.offset.bottomRight.x)).to.equal(72.68); - expect(round(a.paths.offset.bottomRight.y)).to.equal(26.49); + expect(round(a.paths.offset.bottomRight.x)).to.equal(72.63); + expect(round(a.paths.offset.bottomRight.y)).to.equal(26.48); }); it("Should offset a curve where cp2 = end", () => { @@ -221,6 +221,30 @@ it("Should throw error when shifting along path further than it's long", () => { expect(() => a.paths.test.shiftAlong(500)).to.throw(); }); +it("Should shift along with sufficient precision", () => { + let pattern = new freesewing.Pattern(); + pattern.parts.a = new pattern.Part(); + let a = pattern.parts.a; + a.paths.test = new a.Path() + .move(new a.Point(0, 0)) + .curve(new a.Point(123, 123), new a.Point(-123, 456), new a.Point(456, -123)) + a.points.a = a.paths.test.shiftAlong(100) + a.points.b = a.paths.test.reverse().shiftAlong(a.paths.test.length()-100) + expect(a.points.a.dist(a.points.b)).to.below(0.05); +}); + +it("Should shift fraction with sufficient precision", () => { + let pattern = new freesewing.Pattern(); + pattern.parts.a = new pattern.Part(); + let a = pattern.parts.a; + a.paths.test = new a.Path() + .move(new a.Point(0, 0)) + .curve(new a.Point(123, 123), new a.Point(-123, 456), new a.Point(456, -123)) + a.points.a = a.paths.test.shiftFractionAlong(0.5) + a.points.b = a.paths.test.reverse().shiftFractionAlong(0.5) + expect(a.points.a.dist(a.points.b)).to.below(0.05); +}); + it("Should shift a fraction along a line", () => { let pattern = new freesewing.Pattern(); pattern.parts.a = new pattern.Part(); @@ -600,12 +624,12 @@ it("Should split a path on a curve", () => { let halves = a.paths.test.split(a.points.split); let curve = halves[0].ops.pop(); expect(curve.type).to.equal("curve"); - expect(round(curve.cp1.x)).to.equal(35.2); - expect(round(curve.cp1.y)).to.equal(21.6); - expect(round(curve.cp2.x)).to.equal(46.29); - expect(round(curve.cp2.y)).to.equal(-15.02); - expect(round(curve.to.x)).to.equal(72.9); - expect(round(curve.to.y)).to.equal(9.03); + expect(round(curve.cp1.x)).to.equal(35.08); + expect(round(curve.cp1.y)).to.equal(21.64); + expect(round(curve.cp2.x)).to.equal(46.19); + expect(round(curve.cp2.y)).to.equal(-14.69); + expect(round(curve.to.x)).to.equal(72.53); + expect(round(curve.to.y)).to.equal(8.71); }); it("Should split a path on a line", () => { @@ -750,3 +774,4 @@ it("Should overwrite a path attribute", () => { .attr("class", "overwritten", true); expect(a.paths.line.attributes.get("class")).to.equal("overwritten"); }); +