✨ Added utils.curveCrossesLine and utils.curveCrossesCurve
This commit is contained in:
parent
aa80da37cc
commit
f43be99877
3 changed files with 160 additions and 0 deletions
10
src/point.js
10
src/point.js
|
@ -100,6 +100,16 @@ Point.prototype.sitsOn = function(that) {
|
|||
else return false;
|
||||
};
|
||||
|
||||
/** Checks whether this has roughly the same coordinates as that */
|
||||
Point.prototype.sitsRoughlyOn = function(that) {
|
||||
if (
|
||||
Math.round(this.x) === Math.round(that.x) &&
|
||||
Math.round(this.y) === Math.round(that.y)
|
||||
)
|
||||
return true;
|
||||
else return false;
|
||||
};
|
||||
|
||||
/** Shifts this point fraction of the distance towards that point */
|
||||
Point.prototype.shiftFractionTowards = function(that, fraction) {
|
||||
return this.shiftTowards(that, this.dist(that) * fraction);
|
||||
|
|
70
src/utils.js
70
src/utils.js
|
@ -1,4 +1,5 @@
|
|||
import Point from "./point";
|
||||
import Bezier from "bezier-js";
|
||||
import { round } from "./round";
|
||||
|
||||
/** Returns internal hook name for a macro */
|
||||
|
@ -69,3 +70,72 @@ export function units(value, to = "metric") {
|
|||
if (to === "imperial") return round(value / 25.4) + '"';
|
||||
else return round(value / 10) + "cm";
|
||||
}
|
||||
|
||||
/** Find where a curve crosses a line */
|
||||
export function curveCrossesLine(from, cp1, cp2, to, start, end) {
|
||||
let intersections = [];
|
||||
let bz = new Bezier(
|
||||
{ x: from.x, y: from.y },
|
||||
{ x: cp1.x, y: cp1.y },
|
||||
{ x: cp2.x, y: cp2.y },
|
||||
{ x: to.x, y: to.y }
|
||||
);
|
||||
let line = {
|
||||
p1: { x: start.x, y: start.y },
|
||||
p2: { x: end.x, y: end.y }
|
||||
};
|
||||
for (let t of bz.intersects(line)) {
|
||||
let isect = bz.get(t);
|
||||
intersections.push(new Point(isect.x, isect.y));
|
||||
}
|
||||
|
||||
if (intersections.length === 0) return false;
|
||||
else if (intersections.length === 1) return intersections[0];
|
||||
else return intersections;
|
||||
}
|
||||
|
||||
/** Find where a curve crosses another curve */
|
||||
export function curveCrossesCurve(
|
||||
fromA,
|
||||
cp1A,
|
||||
cp2A,
|
||||
toA,
|
||||
fromB,
|
||||
cp1B,
|
||||
cp2B,
|
||||
toB
|
||||
) {
|
||||
let precision = 0.005; // See https://github.com/Pomax/bezierjs/issues/99
|
||||
let intersections = [];
|
||||
let curveA = new Bezier(
|
||||
{ x: fromA.x, y: fromA.y },
|
||||
{ x: cp1A.x, y: cp1A.y },
|
||||
{ x: cp2A.x, y: cp2A.y },
|
||||
{ x: toA.x, y: toA.y }
|
||||
);
|
||||
let curveB = new Bezier(
|
||||
{ x: fromB.x, y: fromB.y },
|
||||
{ x: cp1B.x, y: cp1B.y },
|
||||
{ x: cp2B.x, y: cp2B.y },
|
||||
{ x: toB.x, y: toB.y }
|
||||
);
|
||||
|
||||
for (let tvalues of curveA.intersects(curveB, precision)) {
|
||||
let intersection = curveA.get(tvalues.substr(0, tvalues.indexOf("/")));
|
||||
intersections.push(new Point(intersection.x, intersection.y));
|
||||
}
|
||||
|
||||
if (intersections.length === 0) return false;
|
||||
else if (intersections.length === 1) return intersections.shift();
|
||||
else {
|
||||
let unique = [];
|
||||
for (let i of intersections) {
|
||||
let dupe = false;
|
||||
for (let u of unique) {
|
||||
if (i.sitsRoughlyOn(u)) dupe = true;
|
||||
}
|
||||
if (!dupe) unique.push(i);
|
||||
}
|
||||
return unique;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,86 @@ it("Should detect horizontal lines never pass a give Y-value", () => {
|
|||
expect(freesewing.utils.beamCrossesY(a, b, 69)).to.equal(false);
|
||||
});
|
||||
|
||||
it("Should find no intersections between a curve and a line", () => {
|
||||
let A = new freesewing.Point(10, 10);
|
||||
let Acp = new freesewing.Point(310, 40);
|
||||
let B = new freesewing.Point(110, 70);
|
||||
let Bcp = new freesewing.Point(-210, 40);
|
||||
let E = new freesewing.Point(-20, -20);
|
||||
let D = new freesewing.Point(30, -85);
|
||||
|
||||
let hit = freesewing.utils.curveCrossesLine(A, Acp, Bcp, B, E, D);
|
||||
expect(hit).to.equal(false);
|
||||
});
|
||||
|
||||
it("Should find 1 intersection between a curve and a line", () => {
|
||||
let A = new freesewing.Point(10, 10);
|
||||
let Acp = new freesewing.Point(310, 40);
|
||||
let B = new freesewing.Point(110, 70);
|
||||
let Bcp = new freesewing.Point(-210, 40);
|
||||
let E = new freesewing.Point(20, 20);
|
||||
let D = new freesewing.Point(30, -85);
|
||||
|
||||
let hit = freesewing.utils.curveCrossesLine(A, Acp, Bcp, B, E, D);
|
||||
expect(hit.x).to.equal(20.85);
|
||||
expect(hit.y).to.equal(11.11);
|
||||
});
|
||||
|
||||
it("Should find 3 intersections between a curve and a line", () => {
|
||||
let A = new freesewing.Point(10, 10);
|
||||
let Acp = new freesewing.Point(310, 40);
|
||||
let B = new freesewing.Point(110, 70);
|
||||
let Bcp = new freesewing.Point(-210, 40);
|
||||
let E = new freesewing.Point(20, -5);
|
||||
let D = new freesewing.Point(100, 85);
|
||||
|
||||
let hits = freesewing.utils.curveCrossesLine(A, Acp, Bcp, B, E, D);
|
||||
expect(hits.length).to.equal(3);
|
||||
});
|
||||
|
||||
it("Should find 9 intersections between two curves", () => {
|
||||
let A = new freesewing.Point(10, 10);
|
||||
let Acp = new freesewing.Point(310, 40);
|
||||
let B = new freesewing.Point(110, 70);
|
||||
let Bcp = new freesewing.Point(-210, 40);
|
||||
let C = new freesewing.Point(20, -5);
|
||||
let Ccp = new freesewing.Point(60, 300);
|
||||
let D = new freesewing.Point(100, 85);
|
||||
let Dcp = new freesewing.Point(70, -220);
|
||||
|
||||
let hits = freesewing.utils.curveCrossesCurve(A, Acp, Bcp, B, C, Ccp, Dcp, D);
|
||||
expect(hits.length).to.equal(9);
|
||||
});
|
||||
|
||||
it("Should find 1 intersection between two curves", () => {
|
||||
let A = new freesewing.Point(10, 10);
|
||||
let Acp = new freesewing.Point(310, 40);
|
||||
let B = new freesewing.Point(110, 70);
|
||||
let Bcp = new freesewing.Point(-210, 40);
|
||||
let C = new freesewing.Point(20, -5);
|
||||
let Ccp = new freesewing.Point(-60, 300);
|
||||
let D = new freesewing.Point(-200, 85);
|
||||
let Dcp = new freesewing.Point(-270, -220);
|
||||
|
||||
let hit = freesewing.utils.curveCrossesCurve(A, Acp, Bcp, B, C, Ccp, Dcp, D);
|
||||
expect(hit.x).to.equal(15.58);
|
||||
expect(hit.y).to.equal(10.56);
|
||||
});
|
||||
|
||||
it("Should find no intersection between two curves", () => {
|
||||
let A = new freesewing.Point(10, 10);
|
||||
let Acp = new freesewing.Point(310, 40);
|
||||
let B = new freesewing.Point(110, 70);
|
||||
let Bcp = new freesewing.Point(-210, 40);
|
||||
let C = new freesewing.Point(20, -5);
|
||||
let Ccp = new freesewing.Point(-60, -300);
|
||||
let D = new freesewing.Point(-200, 85);
|
||||
let Dcp = new freesewing.Point(-270, -220);
|
||||
|
||||
let hit = freesewing.utils.curveCrossesCurve(A, Acp, Bcp, B, C, Ccp, Dcp, D);
|
||||
expect(hit).to.equal(false);
|
||||
});
|
||||
|
||||
it("Should correctly format units", () => {
|
||||
expect(freesewing.utils.units(123.456)).to.equal("12.35cm");
|
||||
expect(freesewing.utils.units(123.456, "imperial")).to.equal('4.86"');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue