From 04bf540a5a3c884b162e124c6f132b63b8028e85 Mon Sep 17 00:00:00 2001 From: Joost De Cock Date: Mon, 20 Aug 2018 17:10:28 +0200 Subject: [PATCH] :sparkles: Added Path.edge() --- src/path.js | 118 ++++++++++++++++++++++++++++++++++++++++++--- tests/path.test.js | 66 +++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 8 deletions(-) diff --git a/src/path.js b/src/path.js index 11969b2862e..c889f198ec7 100644 --- a/src/path.js +++ b/src/path.js @@ -123,13 +123,26 @@ Path.prototype.boundary = function() { let current; let topLeft = new Point(Infinity, Infinity); let bottomRight = new Point(-Infinity, -Infinity); + let edges = []; for (let i in this.ops) { let op = this.ops[i]; if (op.type === "move" || op.type === "line") { - if (op.to.x < topLeft.x) topLeft.x = op.to.x; - if (op.to.y < topLeft.y) topLeft.y = op.to.y; - if (op.to.x > bottomRight.x) bottomRight.x = op.to.x; - if (op.to.y > bottomRight.y) bottomRight.y = op.to.y; + if (op.to.x < topLeft.x) { + topLeft.x = op.to.x; + edges["leftOp"] = i; + } + if (op.to.y < topLeft.y) { + topLeft.y = op.to.y; + edges["topOp"] = i; + } + if (op.to.x > bottomRight.x) { + bottomRight.x = op.to.x; + edges["rightOp"] = i; + } + if (op.to.y > bottomRight.y) { + bottomRight.y = op.to.y; + edges["bottomOp"] = i; + } } else if (op.type === "curve") { let bb = new Bezier( { x: current.x, y: current.y }, @@ -137,10 +150,22 @@ Path.prototype.boundary = function() { { x: op.cp2.x, y: op.cp2.y }, { x: op.to.x, y: op.to.y } ).bbox(); - if (bb.x.min < topLeft.x) topLeft.x = bb.x.min; - if (bb.y.min < topLeft.y) topLeft.y = bb.y.min; - if (bb.x.max > bottomRight.x) bottomRight.x = bb.x.max; - if (bb.y.max > bottomRight.y) bottomRight.y = bb.y.max; + if (bb.x.min < topLeft.x) { + topLeft.x = bb.x.min; + edges["leftOp"] = i; + } + if (bb.y.min < topLeft.y) { + topLeft.y = bb.y.min; + edges["topOp"] = i; + } + if (bb.x.max > bottomRight.x) { + bottomRight.x = bb.x.max; + edges["rightOp"] = i; + } + if (bb.y.max > bottomRight.y) { + bottomRight.y = bb.y.max; + edges["bottomOp"] = i; + } } if (op.to) current = op.to; } @@ -148,6 +173,13 @@ Path.prototype.boundary = function() { this.topLeft = topLeft; this.bottomRight = bottomRight; + for (let side of ["top", "left", "bottom", "right"]) { + let s = side + "Op"; + this[s] = this.ops[edges[s]]; + this[s].from = + this[s].type === "move" ? this[s].to : this.ops[edges[s] - 1].to; + } + return this; }; @@ -418,4 +450,74 @@ Path.prototype.reverse = function() { return rev; }; +/** Returns a reversed version of this */ +Path.prototype.edge = function(side) { + this.boundary(); + if (side === "topLeft") return this.topLeft; + else if (side === "bottomRight") return this.bottomRight; + else if (side === "topRight") + return new Point(this.bottomRight.x, this.topLeft.y); + else if (side === "bottomLeft") + return new Point(this.topLeft.x, this.bottomRight.y); + else { + let s = side + "Op"; + if (this[s].type === "move") return this[s].to; + else if (this[s].type === "line") { + if (side === "top") { + if (this.topOp.to.y < this.topOp.from.y) return this.topOp.to; + else return this.topOp.to; + } else if (side === "left") { + if (this.leftOp.to.x < this.leftOp.from.x) return this.leftOp.to; + else return this.leftOp.to; + } else if (side === "bottom") { + if (this.bottomOp.to.y > this.bottomOp.from.y) return this.bottomOp.to; + else return this.bottomOp.to; + } else if (side === "right") { + if (this.rightOp.to.x > this.rightOp.from.x) return this.rightOp.to; + else return this.rightOp.to; + } + } else if (this[s].type === "curve") { + let line; + if (side === "top") + line = { + p1: { x: this.topLeft.x, y: this.topLeft.y }, + p2: { x: this.bottomRight.x, y: this.topLeft.y } + }; + else if (side === "left") + line = { + p1: { x: this.topLeft.x, y: this.topLeft.y }, + p2: { x: this.topLeft.x, y: this.bottomRight.y } + }; + else if (side === "bottom") + line = { + p1: { x: this.topLeft.x, y: this.bottomRight.y }, + p2: { x: this.bottomRight.x, y: this.bottomRight.y } + }; + else if (side === "right") + line = { + p1: { x: this.bottomRight.x, y: this.topLeft.y }, + p2: { x: this.bottomRight.x, y: this.bottomRight.y } + }; + let bz = edgeCurveAsBezier(this[s]); + let isect = bz.intersects(line); + let edge = bz.get(isect[0]); + return new Point(edge.x, edge.y); + } + } +}; + +function edgeCurveAsBezier(op) { + return new Bezier( + { x: op.from.x, y: op.from.y }, + { x: op.cp1.x, y: op.cp1.y }, + { x: op.cp2.x, y: op.cp2.y }, + { x: op.to.x, y: op.to.y } + ); +} +///* Returns the edge of a single path operation */ +//function opEdge(op, side) { +// if(op.type === 'move' || op.type +// +//} + export default Path; diff --git a/tests/path.test.js b/tests/path.test.js index 5592e838f7a..50de2bd7131 100644 --- a/tests/path.test.js +++ b/tests/path.test.js @@ -329,3 +329,69 @@ it("Should reverse a path", () => { expect(rev.ops[1].type).to.equal("curve"); expect(rev.ops[2].type).to.equal("line"); }); + +it("Should find the edges of a path", () => { + let pattern = new freesewing.Pattern(); + pattern.parts.a = new pattern.Part(); + let a = pattern.parts.a; + a.points.A = new a.Point(45, 60); + a.points.B = new a.Point(10, 30); + a.points.BCp2 = new a.Point(40, 20); + a.points.C = new a.Point(90, 30); + a.points.CCp1 = new a.Point(50, -30); + a.points.D = new a.Point(-60, 90); + a.points.E = new a.Point(90, 190); + a.paths.test = new a.Path() + .move(a.points.A) + .line(a.points.B) + .curve(a.points.BCp2, a.points.CCp1, a.points.C) + .curve(a.points.E, a.points.D, a.points.A) + .close(); + expect(round(a.paths.test.edge("topLeft").x)).to.equal(7.7); + expect(round(a.paths.test.edge("topLeft").y)).to.equal(0.97); + expect(round(a.paths.test.edge("bottomLeft").x)).to.equal(7.7); + expect(round(a.paths.test.edge("bottomLeft").y)).to.equal(118.46); + expect(round(a.paths.test.edge("bottomRight").x)).to.equal(90); + expect(round(a.paths.test.edge("bottomRight").y)).to.equal(118.46); + expect(round(a.paths.test.edge("topRight").x)).to.equal(90); + expect(round(a.paths.test.edge("topRight").y)).to.equal(0.97); + expect(round(a.paths.test.edge("left").x)).to.equal(7.7); + expect(round(a.paths.test.edge("left").y)).to.equal(91.7); + expect(round(a.paths.test.edge("bottom").x)).to.equal(40.75); + expect(round(a.paths.test.edge("bottom").y)).to.equal(118.46); + expect(round(a.paths.test.edge("right").x)).to.equal(90); + expect(round(a.paths.test.edge("right").y)).to.equal(30); + expect(round(a.paths.test.edge("top").x)).to.equal(55.97); + expect(round(a.paths.test.edge("top").y)).to.equal(0.97); +}); + +it("Should find the edges of a path for corner cases", () => { + let pattern = new freesewing.Pattern(); + pattern.parts.a = new pattern.Part(); + let a = pattern.parts.a; + a.points.A = new a.Point(-45, -60); + a.points.B = new a.Point(45, 60); + a.points.C = new a.Point(-90, -160); + a.paths.test = new a.Path().move(a.points.A).line(a.points.B); + expect(round(a.paths.test.edge("top").x)).to.equal(-45); + expect(round(a.paths.test.edge("top").y)).to.equal(-60); + expect(round(a.paths.test.edge("left").x)).to.equal(-45); + expect(round(a.paths.test.edge("left").y)).to.equal(-60); + expect(round(a.paths.test.edge("bottom").x)).to.equal(45); + expect(round(a.paths.test.edge("bottom").y)).to.equal(60); + expect(round(a.paths.test.edge("right").x)).to.equal(45); + expect(round(a.paths.test.edge("right").y)).to.equal(60); + a.paths.test = new a.Path().move(a.points.B).line(a.points.A); + expect(round(a.paths.test.edge("top").x)).to.equal(-45); + expect(round(a.paths.test.edge("top").y)).to.equal(-60); + expect(round(a.paths.test.edge("left").x)).to.equal(-45); + expect(round(a.paths.test.edge("left").y)).to.equal(-60); + expect(round(a.paths.test.edge("bottom").x)).to.equal(45); + expect(round(a.paths.test.edge("bottom").y)).to.equal(60); + expect(round(a.paths.test.edge("right").x)).to.equal(45); + expect(round(a.paths.test.edge("right").y)).to.equal(60); +}); + +function round(value) { + return Math.round(value * 1e2) / 1e2; +}