✨ Added Path.split() and the utility methods that support it
This commit is contained in:
parent
753c1e913f
commit
a4647dfebb
4 changed files with 191 additions and 1 deletions
69
src/path.js
69
src/path.js
|
@ -2,7 +2,13 @@ import Attributes from "./attributes";
|
||||||
import Point from "./point";
|
import Point from "./point";
|
||||||
import Bezier from "bezier-js";
|
import Bezier from "bezier-js";
|
||||||
import { round } from "./round";
|
import { round } from "./round";
|
||||||
import { linesCross, curveCrossesLine, curveCrossesCurve } from "./utils";
|
import {
|
||||||
|
linesCross,
|
||||||
|
curveCrossesLine,
|
||||||
|
curveCrossesCurve,
|
||||||
|
pointOnLine,
|
||||||
|
pointOnCurve
|
||||||
|
} from "./utils";
|
||||||
|
|
||||||
function Path() {
|
function Path() {
|
||||||
this.render = true;
|
this.render = true;
|
||||||
|
@ -655,4 +661,65 @@ function addIntersectionsToArray(candidates, intersections) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Splits path on point, and retuns both halves */
|
||||||
|
Path.prototype.split = function(point) {
|
||||||
|
let divided = this.divide();
|
||||||
|
let firstHalf = false;
|
||||||
|
let secondHalf = false;
|
||||||
|
for (let pi in divided) {
|
||||||
|
let path = divided[pi];
|
||||||
|
if (path.ops[1].type === "line") {
|
||||||
|
if (pointOnLine(path.ops[0].to, path.ops[1].to, point)) {
|
||||||
|
firstHalf = divided.slice(0, pi);
|
||||||
|
firstHalf.push(new Path().move(path.ops[0].to).line(point));
|
||||||
|
pi++;
|
||||||
|
secondHalf = divided.slice(pi);
|
||||||
|
secondHalf.unshift(new Path().move(point).line(path.ops[1].to));
|
||||||
|
}
|
||||||
|
} else if (path.ops[1].type === "curve") {
|
||||||
|
let t = pointOnCurve(
|
||||||
|
path.ops[0].to,
|
||||||
|
path.ops[1].cp1,
|
||||||
|
path.ops[1].cp2,
|
||||||
|
path.ops[1].to,
|
||||||
|
point
|
||||||
|
);
|
||||||
|
if (t !== false) {
|
||||||
|
let curve = new Bezier(
|
||||||
|
{ x: path.ops[0].to.x, y: path.ops[0].to.y },
|
||||||
|
{ x: path.ops[1].cp1.x, y: path.ops[1].cp1.y },
|
||||||
|
{ x: path.ops[1].cp2.x, y: path.ops[1].cp2.y },
|
||||||
|
{ x: path.ops[1].to.x, y: path.ops[1].to.y }
|
||||||
|
);
|
||||||
|
let split = curve.split(t);
|
||||||
|
firstHalf = divided.slice(0, pi);
|
||||||
|
firstHalf.push(
|
||||||
|
new Path()
|
||||||
|
.move(new Point(split.left.points[0].x, split.left.points[0].y))
|
||||||
|
.curve(
|
||||||
|
new Point(split.left.points[1].x, split.left.points[1].y),
|
||||||
|
new Point(split.left.points[2].x, split.left.points[2].y),
|
||||||
|
new Point(split.left.points[3].x, split.left.points[3].y)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
pi++;
|
||||||
|
secondHalf = divided.slice(pi);
|
||||||
|
secondHalf.unshift(
|
||||||
|
new Path()
|
||||||
|
.move(new Point(split.right.points[0].x, split.right.points[0].y))
|
||||||
|
.curve(
|
||||||
|
new Point(split.right.points[1].x, split.right.points[1].y),
|
||||||
|
new Point(split.right.points[2].x, split.right.points[2].y),
|
||||||
|
new Point(split.right.points[3].x, split.right.points[3].y)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (firstHalf) firstHalf = joinPaths(firstHalf);
|
||||||
|
if (secondHalf) secondHalf = joinPaths(secondHalf);
|
||||||
|
|
||||||
|
return [firstHalf, secondHalf];
|
||||||
|
};
|
||||||
|
|
||||||
export default Path;
|
export default Path;
|
||||||
|
|
38
src/utils.js
38
src/utils.js
|
@ -1,4 +1,5 @@
|
||||||
import Point from "./point";
|
import Point from "./point";
|
||||||
|
import Path from "./path";
|
||||||
import Bezier from "bezier-js";
|
import Bezier from "bezier-js";
|
||||||
import { round } from "./round";
|
import { round } from "./round";
|
||||||
|
|
||||||
|
@ -51,6 +52,43 @@ export function linesCross(a1, a2, b1, b2) {
|
||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Finds out whether a point lies on an endless line */
|
||||||
|
export function pointOnBeam(from, to, check) {
|
||||||
|
if (from.sitsOn(check)) return true;
|
||||||
|
if (to.sitsOn(check)) return true;
|
||||||
|
let angleA = from.angle(to);
|
||||||
|
let angleB = from.angle(check);
|
||||||
|
if (angleA === angleB || Math.abs(angleA - angleB) === 180) return true;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Finds out whether a point lies on a line segment */
|
||||||
|
export function pointOnLine(from, to, check) {
|
||||||
|
if (!pointOnBeam(from, to, check)) return false;
|
||||||
|
let lenA = from.dist(to);
|
||||||
|
let lenB = from.dist(check) + check.dist(to);
|
||||||
|
if (Math.round(lenA) == Math.round(lenB)) return true;
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Finds out whether a point lies on a curve */
|
||||||
|
export function pointOnCurve(start, cp1, cp2, end, check) {
|
||||||
|
if (start.sitsOn(check)) return true;
|
||||||
|
if (end.sitsOn(check)) return true;
|
||||||
|
let curve = new Bezier(
|
||||||
|
{ x: start.x, y: start.y },
|
||||||
|
{ x: cp1.x, y: cp1.y },
|
||||||
|
{ x: cp2.x, y: cp2.y },
|
||||||
|
{ x: end.x, y: end.y }
|
||||||
|
);
|
||||||
|
let intersections = curve.intersects({
|
||||||
|
p1: { x: check.x - 1, y: check.y },
|
||||||
|
p2: { x: check.x + 1, y: check.y }
|
||||||
|
});
|
||||||
|
if (intersections.length > 0) return intersections.shift();
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Find where an (endless) line crosses a certain X-value */
|
/** Find where an (endless) line crosses a certain X-value */
|
||||||
export function beamCrossesX(from, to, x) {
|
export function beamCrossesX(from, to, x) {
|
||||||
if (from.x === to.x) return false; // Vertical line
|
if (from.x === to.x) return false; // Vertical line
|
||||||
|
|
|
@ -562,6 +562,62 @@ it("Should divide a path", () => {
|
||||||
expect(divided[3].ops[1].to.y).to.equal(60);
|
expect(divided[3].ops[1].to.y).to.equal(60);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should split a path on a curve", () => {
|
||||||
|
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(50, 130);
|
||||||
|
a.points.DCp1 = new a.Point(150, 30);
|
||||||
|
|
||||||
|
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.DCp1, a.points.DCp1, a.points.D);
|
||||||
|
|
||||||
|
a.points.split = a.paths.test.shiftAlong(120);
|
||||||
|
let halves = a.paths.test.split(a.points.split);
|
||||||
|
let curve = halves[0].ops.pop();
|
||||||
|
expect(curve.type).to.equal("curve");
|
||||||
|
expect(curve.cp1.x).to.equal(35.2);
|
||||||
|
expect(curve.cp1.y).to.equal(21.6);
|
||||||
|
expect(curve.cp2.x).to.equal(46.29);
|
||||||
|
expect(curve.cp2.y).to.equal(-15.02);
|
||||||
|
expect(curve.to.x).to.equal(72.9);
|
||||||
|
expect(curve.to.y).to.equal(9.03);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should split a path on a line", () => {
|
||||||
|
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(50, 130);
|
||||||
|
a.points.DCp1 = new a.Point(150, 30);
|
||||||
|
|
||||||
|
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.DCp1, a.points.DCp1, a.points.D);
|
||||||
|
|
||||||
|
a.points.split = a.paths.test.shiftAlong(20);
|
||||||
|
let halves = a.paths.test.split(a.points.split);
|
||||||
|
let line = halves[0].ops.pop();
|
||||||
|
expect(line.type).to.equal("line");
|
||||||
|
expect(line.to.x).to.equal(29.81);
|
||||||
|
expect(line.to.y).to.equal(46.98);
|
||||||
|
});
|
||||||
|
|
||||||
function round(value) {
|
function round(value) {
|
||||||
return Math.round(value * 1e2) / 1e2;
|
return Math.round(value * 1e2) / 1e2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,3 +187,32 @@ it("Should correctly format units", () => {
|
||||||
expect(freesewing.utils.units(123.456)).to.equal("12.35cm");
|
expect(freesewing.utils.units(123.456)).to.equal("12.35cm");
|
||||||
expect(freesewing.utils.units(123.456, "imperial")).to.equal('4.86"');
|
expect(freesewing.utils.units(123.456, "imperial")).to.equal('4.86"');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Should find a start or end point on beam", () => {
|
||||||
|
let A = new freesewing.Point(12, 34);
|
||||||
|
let B = new freesewing.Point(56, 78);
|
||||||
|
let checkA = new freesewing.Point(12, 34);
|
||||||
|
let checkB = new freesewing.Point(56, 78);
|
||||||
|
expect(freesewing.utils.pointOnBeam(A, B, checkA)).to.equal(true);
|
||||||
|
expect(freesewing.utils.pointOnBeam(A, B, checkB)).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should find whether a point lies on a line segment", () => {
|
||||||
|
let A = new freesewing.Point(12, 34);
|
||||||
|
let B = new freesewing.Point(56, 78);
|
||||||
|
let check1 = A.shiftTowards(B, 10);
|
||||||
|
let check2 = A.shiftTowards(B, 210);
|
||||||
|
expect(freesewing.utils.pointOnLine(A, B, check1)).to.equal(true);
|
||||||
|
expect(freesewing.utils.pointOnLine(A, B, check2)).to.equal(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should find a start or end point on curve", () => {
|
||||||
|
let A = new freesewing.Point(12, 34);
|
||||||
|
let Acp = new freesewing.Point(123, 4);
|
||||||
|
let B = new freesewing.Point(56, 78);
|
||||||
|
let Bcp = new freesewing.Point(5, 678);
|
||||||
|
let checkA = new freesewing.Point(12, 34);
|
||||||
|
let checkB = new freesewing.Point(56, 78);
|
||||||
|
expect(freesewing.utils.pointOnCurve(A, Acp, Bcp, B, checkA)).to.equal(true);
|
||||||
|
expect(freesewing.utils.pointOnCurve(A, Acp, Bcp, B, checkB)).to.equal(true);
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue