diff --git a/src/part.js b/src/part.js index eb5a02f5cb1..bf7260f274a 100644 --- a/src/part.js +++ b/src/part.js @@ -63,7 +63,7 @@ Part.prototype.debugClosure = function() { Part.prototype.debug = function(data) {}; /** Returns an unused ID */ -Part.prototype.getUid = function() { +Part.prototype.getId = function() { this.freeId += 1; return "" + this.freeId; diff --git a/src/path.js b/src/path.js index 6b8ffe13595..09432795a8d 100644 --- a/src/path.js +++ b/src/path.js @@ -3,9 +3,9 @@ import Point from "./point"; import Bezier from "bezier-js"; import { round } from "./round"; import { - linesCross, - curveCrossesLine, - curveCrossesCurve, + linesIntersect, + lineIntersectsCurve, + curvesIntersect, pointOnLine, pointOnCurve } from "./utils"; @@ -545,18 +545,19 @@ Path.prototype.divide = function() { }; /** Finds intersections between this path and an X value */ -Path.prototype.crossesX = function(x) { - return this.crossesAxis(x, "x"); +Path.prototype.intersectsX = function(x) { + return this.intersectsAxis(x, "x"); }; /** Finds intersections between this path and an Y value */ -Path.prototype.crossesY = function(y) { - return this.crossesAxis(y, "y"); +Path.prototype.intersectsY = function(y) { + return this.intersectsAxis(y, "y"); }; /** Finds intersections between this path and a X or Y value */ -Path.prototype.crossesAxis = function(val = false, mode) { - if (val === false) throw "Path.crosses[X-Y] requires an value as parameter"; +Path.prototype.intersectsAxis = function(val = false, mode) { + if (val === false) + throw "Path.intersects[X-Y] requires an value as parameter"; let intersections = []; let lineStart = mode === "x" ? new Point(val, -100000) : new Point(-10000, val); @@ -564,18 +565,18 @@ Path.prototype.crossesAxis = function(val = false, mode) { for (let path of this.divide()) { if (path.ops[1].type === "line") { addIntersectionsToArray( - linesCross(path.ops[0].to, path.ops[1].to, lineStart, lineEnd), + linesIntersect(path.ops[0].to, path.ops[1].to, lineStart, lineEnd), intersections ); } else if (path.ops[1].type === "curve") { addIntersectionsToArray( - curveCrossesLine( + lineIntersectsCurve( + lineStart, + lineEnd, path.ops[0].to, path.ops[1].cp1, path.ops[1].cp2, - path.ops[1].to, - lineStart, - lineEnd + path.ops[1].to ), intersections ); @@ -595,7 +596,7 @@ Path.prototype.intersects = function(path) { if (pathA.ops[1].type === "line") { if (pathB.ops[1].type === "line") { addIntersectionsToArray( - linesCross( + linesIntersect( pathA.ops[0].to, pathA.ops[1].to, pathB.ops[0].to, @@ -605,13 +606,13 @@ Path.prototype.intersects = function(path) { ); } else if (pathB.ops[1].type === "curve") { addIntersectionsToArray( - curveCrossesLine( + lineIntersectsCurve( + pathA.ops[0].to, + pathA.ops[1].to, pathB.ops[0].to, pathB.ops[1].cp1, pathB.ops[1].cp2, - pathB.ops[1].to, - pathA.ops[0].to, - pathA.ops[1].to + pathB.ops[1].to ), intersections ); @@ -619,19 +620,19 @@ Path.prototype.intersects = function(path) { } else if (pathA.ops[1].type === "curve") { if (pathB.ops[1].type === "line") { addIntersectionsToArray( - curveCrossesLine( + lineIntersectsCurve( + pathB.ops[0].to, + pathB.ops[1].to, pathA.ops[0].to, pathA.ops[1].cp1, pathA.ops[1].cp2, - pathA.ops[1].to, - pathB.ops[0].to, - pathB.ops[1].to + pathA.ops[1].to ), intersections ); } else if (pathB.ops[1].type === "curve") { addIntersectionsToArray( - curveCrossesCurve( + curvesIntersect( pathA.ops[0].to, pathA.ops[1].cp1, pathA.ops[1].cp2, diff --git a/src/svg.js b/src/svg.js index 5d52ff4ae61..36f8c61dc05 100644 --- a/src/svg.js +++ b/src/svg.js @@ -155,7 +155,7 @@ Svg.prototype.renderPart = function(part) { /** Returns SVG code for a Path object */ Svg.prototype.renderPath = function(path) { if (!path.attributes.get("id")) - path.attributes.add("id", this.idPrefix + this.getUid()); + path.attributes.add("id", this.idPrefix + this.getId()); path.attributes.set("d", path.asPathstring()); return `${this.nl()}${this.renderPathText( @@ -278,7 +278,7 @@ Svg.prototype.outdent = function() { }; /** Returns an unused ID */ -Svg.prototype.getUid = function() { +Svg.prototype.getId = function() { this.freeId += 1; return "" + this.freeId; diff --git a/src/utils.js b/src/utils.js index df54b070c3d..1f3f536b08a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -9,7 +9,7 @@ export function macroName(name) { } /** Find intersection of two (endless) lines */ -export function beamsCross(a1, a2, b1, b2) { +export function beamsIntersect(a1, a2, b1, b2) { let slopeA = a1.slope(a2); let slopeB = b1.slope(b2); if (slopeA === slopeB) return false; // Parallel lines @@ -37,8 +37,8 @@ export function beamsCross(a1, a2, b1, b2) { } /** Find intersection of two line segments */ -export function linesCross(a1, a2, b1, b2) { - let p = beamsCross(a1, a2, b1, b2); +export function linesIntersect(a1, a2, b1, b2) { + let p = beamsIntersect(a1, a2, b1, b2); if (!p) return false; let lenA = a1.dist(a2); let lenB = b1.dist(b2); @@ -53,18 +53,22 @@ export function linesCross(a1, a2, b1, b2) { } /** Finds out whether a point lies on an endless line */ -export function pointOnBeam(from, to, check) { +export function pointOnBeam(from, to, check, precision = 1e6) { 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; + let dxCheck = check.dx(from); + let dyCheck = check.dy(from); + let dxLine = to.dx(from); + let dyLine = to.dy(from); + let cross = check.dx(from) * to.dy(from) - check.dy(from) * to.dx(from); + + if (Math.abs(Math.round(cross * precision) / precision) === 0) 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; +export function pointOnLine(from, to, check, precision = 1e6) { + if (!pointOnBeam(from, to, check, precision)) return false; let lenA = from.dist(to); let lenB = from.dist(check) + check.dist(to); if (Math.round(lenA) == Math.round(lenB)) return true; @@ -89,22 +93,22 @@ export function pointOnCurve(start, cp1, cp2, end, check) { else return false; } -/** Find where an (endless) line crosses a certain X-value */ -export function beamCrossesX(from, to, x) { +/** Find where an (endless) line intersects with a certain X-value */ +export function beamIntersectsX(from, to, x) { if (from.x === to.x) return false; // Vertical line let top = new Point(x, -10); let bottom = new Point(x, 10); - return beamsCross(from, to, top, bottom); + return beamsIntersect(from, to, top, bottom); } -/** Find where an (endless) line crosses a certain Y-value */ -export function beamCrossesY(from, to, y) { +/** Find where an (endless) line intersects with a certain Y-value */ +export function beamIntersectsY(from, to, y) { if (from.y === to.y) return false; // Horizontal line let left = new Point(-10, y); let right = new Point(10, y); - return beamsCross(from, to, left, right); + return beamsIntersect(from, to, left, right); } /** Convert value in mm to cm or imperial units */ @@ -113,8 +117,8 @@ export function units(value, to = "metric") { else return round(value / 10) + "cm"; } -/** Find where a curve crosses a line */ -export function curveCrossesLine(from, cp1, cp2, to, start, end) { +/** Find where a curve intersects with line */ +export function lineIntersectsCurve(start, end, from, cp1, cp2, to) { let intersections = []; let bz = new Bezier( { x: from.x, y: from.y }, @@ -136,8 +140,8 @@ export function curveCrossesLine(from, cp1, cp2, to, start, end) { else return intersections; } -/** Find where a curve crosses another curve */ -export function curveCrossesCurve( +/** Find where a curve intersects with another curve */ +export function curvesIntersect( fromA, cp1A, cp2A, @@ -181,3 +185,82 @@ export function curveCrossesCurve( return unique; } } + +/** Find the intersections between two circles */ +export function circlesIntersect(c1, r1, c2, r2, sort = "x") { + let dx = c1.dx(c2); + let dy = c1.dy(c2); + let dist = c1.dist(c2); + // Check for edge cases + if (dist > parseFloat(r1) + parseFloat(r2)) return false; // Circles do not intersect + if (dist < parseFloat(r2) - parseFloat(r1)) return false; // One circle is contained in the other + if (dist === 0 && r1 === r2) return false; // Two circles are identical + let chorddistance = + (Math.pow(r1, 2) - Math.pow(r2, 2) + Math.pow(dist, 2)) / (2 * dist); + let halfchordlength = Math.sqrt(Math.pow(r1, 2) - Math.pow(chorddistance, 2)); + let chordmidpointx = c1.x + (chorddistance * dx) / dist; + let chordmidpointy = c1.y + (chorddistance * dy) / dist; + let i1 = new Point( + chordmidpointx + (halfchordlength * dy) / dist, + chordmidpointy - (halfchordlength * dx) / dist + ); + let i2 = new Point( + chordmidpointx - (halfchordlength * dy) / dist, + chordmidpointy + (halfchordlength * dx) / dist + ); + + if ((sort === "x" && i1.x <= i2.x) || (sort === "y" && i1.y <= i2.y)) + return [i1, i2]; + else return [i2, i1]; +} + +/** Find the intersections between a beam and a circle */ +export function beamIntersectsCircle(c, r, p1, p2, sort = "x") { + let dx = p2.x - p1.x; + let dy = p2.y - p1.y; + let A = Math.pow(dx, 2) + Math.pow(dy, 2); + let B = 2 * (dx * (p1.x - c.x) + dy * (p1.y - c.y)); + let C = Math.pow(p1.x - c.x, 2) + Math.pow(p1.y - c.y, 2) - Math.pow(r, 2); + + let det = Math.pow(B, 2) - 4 * A * C; + + if (A <= 0.0000001 || det < 0) return false; + // No real solutions + else if (det === 0) { + // One solution + let t = (-1 * B) / (2 * A); + let i1 = new Point(p1.x + t * dx, p1.y + t * dy); + return [i1]; + } else { + // Two solutions + let t = (-1 * B + Math.sqrt(det)) / (2 * A); + let i1 = new Point(p1.x + t * dx, p1.y + t * dy); + t = (-1 * B - Math.sqrt(det)) / (2 * A); + let i2 = new Point(p1.x + t * dx, p1.y + t * dy); + if ((sort === "x" && i1.x <= i2.x) || (sort === "y" && i1.y <= i2.y)) + return [i1, i2]; + else return [i2, i1]; + } +} +/** Find the intersections between a line and a circle */ +export function lineIntersectsCircle(c, r, p1, p2, sort = "x") { + let intersections = beamIntersectsCircle(c, r, p1, p2, sort); + if (intersections === false) return false; + else { + if (intersections.length === 1) { + if (pointOnLine(p1, p2, intersections[0])) return intersections; + else return false; + } else { + let i1 = intersections[0]; + let i2 = intersections[1]; + if (!pointOnLine(p1, p2, i1, 5) && !pointOnLine(p1, p2, i2, 5)) + return false; + else if (pointOnLine(p1, p2, i1, 5) && pointOnLine(p1, p2, i2, 5)) { + if ((sort === "x" && i1.x <= i2.x) || (sort === "y" && i1.y <= i2.y)) + return [i1, i2]; + else return [i2, i1]; + } else if (pointOnLine(p1, p2, i1, 5)) return [i1]; + else if (pointOnLine(p1, p2, i2, 5)) return [i2]; + } + } +} diff --git a/tests/part.test.js b/tests/part.test.js index 39e3d5ebd32..adcadbb0eb3 100644 --- a/tests/part.test.js +++ b/tests/part.test.js @@ -65,8 +65,8 @@ it("Should run debug", () => { it("Should return a free ID", () => { let pattern = new freesewing.Pattern(); let part = new pattern.Part(); - let free = part.getUid(); - expect(part.getUid()).to.equal("" + (parseInt(free) + 1)); + let free = part.getId(); + expect(part.getId()).to.equal("" + (parseInt(free) + 1)); }); it("Should return a function from unitsClosure", () => { diff --git a/tests/path.test.js b/tests/path.test.js index 14dd6105917..db18239c9b8 100644 --- a/tests/path.test.js +++ b/tests/path.test.js @@ -392,7 +392,7 @@ it("Should find the edges of a path for corner cases", () => { expect(round(a.paths.test.edge("right").y)).to.equal(60); }); -it("Should find where a path crosses an X value", () => { +it("Should find where a path intersects with an X value", () => { let pattern = new freesewing.Pattern(); pattern.parts.a = new pattern.Part(); let a = pattern.parts.a; @@ -409,7 +409,7 @@ it("Should find where a path crosses an X value", () => { .curve(a.points.BCp2, a.points.CCp1, a.points.C) .curve(a.points.DCp1, a.points.DCp1, a.points.D) .close(); - let intersections = a.paths.test.crossesX(60); + let intersections = a.paths.test.intersectsX(60); expect(intersections.length).to.equal(4); expect(intersections[0].x).to.equal(60); expect(intersections[0].y).to.equal(41.76); @@ -421,7 +421,7 @@ it("Should find where a path crosses an X value", () => { expect(intersections[3].y).to.equal(112.22); }); -it("Should find where a path crosses an Y value", () => { +it("Should find where a path intersects with an Y value", () => { let pattern = new freesewing.Pattern(); pattern.parts.a = new pattern.Part(); let a = pattern.parts.a; @@ -438,7 +438,7 @@ it("Should find where a path crosses an Y value", () => { .curve(a.points.BCp2, a.points.CCp1, a.points.C) .curve(a.points.DCp1, a.points.DCp1, a.points.D) .close(); - let intersections = a.paths.test.crossesY(60); + let intersections = a.paths.test.intersectsY(60); expect(intersections.length).to.equal(2); expect(intersections[0].x).to.equal(117.83); expect(intersections[0].y).to.equal(60); @@ -446,13 +446,13 @@ it("Should find where a path crosses an Y value", () => { expect(intersections[1].y).to.equal(60); }); -it("Should throw an error when not passing a value to path.crossesX", () => { +it("Should throw an error when not passing a value to path.intersectsX", () => { let pattern = new freesewing.Pattern(); pattern.parts.a = new pattern.Part(); let a = pattern.parts.a; a.paths.test = new a.Path(); - expect(() => a.paths.test.crossesX()).to.throw(); - expect(() => a.paths.test.crossesY()).to.throw(); + expect(() => a.paths.test.intersectsX()).to.throw(); + expect(() => a.paths.test.intersectsY()).to.throw(); }); it("Should find the intersections between two paths", () => { diff --git a/tests/utils.test.js b/tests/utils.test.js index 8936fa67c58..278264b681a 100644 --- a/tests/utils.test.js +++ b/tests/utils.test.js @@ -11,7 +11,7 @@ it("Should find the intersection of two endless line segments", () => { let b = new freesewing.Point(20, 24); let c = new freesewing.Point(90, 19); let d = new freesewing.Point(19, 70); - let X = freesewing.utils.beamsCross(a, b, c, d); + let X = freesewing.utils.beamsIntersect(a, b, c, d); expect(X.x).to.equal(60.49); expect(X.y).to.equal(40.2); }); @@ -21,8 +21,8 @@ it("Should detect parallel lines", () => { let b = new freesewing.Point(20, 20); let c = new freesewing.Point(90, 40); let d = new freesewing.Point(19, 40); - expect(freesewing.utils.beamsCross(a, b, c, d)).to.equal(false); - expect(freesewing.utils.linesCross(a, b, c, d)).to.equal(false); + expect(freesewing.utils.beamsIntersect(a, b, c, d)).to.equal(false); + expect(freesewing.utils.linesIntersect(a, b, c, d)).to.equal(false); }); it("Should detect vertical lines", () => { @@ -30,10 +30,10 @@ it("Should detect vertical lines", () => { let b = new freesewing.Point(10, 90); let c = new freesewing.Point(90, 40); let d = new freesewing.Point(19, 40); - let X = freesewing.utils.beamsCross(a, b, c, d); + let X = freesewing.utils.beamsIntersect(a, b, c, d); expect(X.x).to.equal(10); expect(X.y).to.equal(40); - X = freesewing.utils.beamsCross(c, d, a, b); + X = freesewing.utils.beamsIntersect(c, d, a, b); expect(X.x).to.equal(10); }); @@ -42,7 +42,7 @@ it("Should swap direction prior to finding beam intersection", () => { let b = new freesewing.Point(00, 90); let c = new freesewing.Point(90, 40); let d = new freesewing.Point(19, 40); - let X = freesewing.utils.beamsCross(a, b, c, d); + let X = freesewing.utils.beamsIntersect(a, b, c, d); expect(X.x).to.equal(7.14); expect(X.y).to.equal(40); }); @@ -52,7 +52,7 @@ it("Should return false when two lines don't intersect", () => { let b = new freesewing.Point(20, 24); let c = new freesewing.Point(90, 19); let d = new freesewing.Point(19, 70); - expect(freesewing.utils.linesCross(a, b, c, d)).to.equal(false); + expect(freesewing.utils.linesIntersect(a, b, c, d)).to.equal(false); }); it("Should find the intersection of two line segments", () => { @@ -60,7 +60,7 @@ it("Should find the intersection of two line segments", () => { let b = new freesewing.Point(90, 74); let c = new freesewing.Point(90, 19); let d = new freesewing.Point(11, 70); - let X = freesewing.utils.linesCross(a, b, c, d); + let X = freesewing.utils.linesIntersect(a, b, c, d); expect(X.x).to.equal(51.95); expect(X.y).to.equal(43.56); }); @@ -70,7 +70,7 @@ it("Should find the intersection of two line segments - round() edge case", () = let b = new freesewing.Point(10, 30); let c = new freesewing.Point(55, 40); let d = new freesewing.Point(0, 55); - let X = freesewing.utils.linesCross(a, b, c, d); + let X = freesewing.utils.linesIntersect(a, b, c, d); expect(X.x).to.equal(29.71); expect(X.y).to.equal(46.9); }); @@ -78,7 +78,7 @@ it("Should find the intersection of two line segments - round() edge case", () = it("Should find the intersection of an endles line and a give X-value", () => { let a = new freesewing.Point(10, 10); let b = new freesewing.Point(90, 74); - let X = freesewing.utils.beamCrossesX(a, b, 69); + let X = freesewing.utils.beamIntersectsX(a, b, 69); expect(X.x).to.equal(69); expect(X.y).to.equal(57.2); }); @@ -86,7 +86,7 @@ it("Should find the intersection of an endles line and a give X-value", () => { it("Should find the intersection of an endles line and a give Y-value", () => { let a = new freesewing.Point(10, 10); let b = new freesewing.Point(90, 74); - let X = freesewing.utils.beamCrossesY(a, b, 69); + let X = freesewing.utils.beamIntersectsY(a, b, 69); expect(X.x).to.equal(83.75); expect(X.y).to.equal(69); }); @@ -94,13 +94,13 @@ it("Should find the intersection of an endles line and a give Y-value", () => { it("Should detect vertical lines never pass a give X-value", () => { let a = new freesewing.Point(10, 10); let b = new freesewing.Point(10, 90); - expect(freesewing.utils.beamCrossesX(a, b, 69)).to.equal(false); + expect(freesewing.utils.beamIntersectsX(a, b, 69)).to.equal(false); }); it("Should detect horizontal lines never pass a give Y-value", () => { let a = new freesewing.Point(10, 10); let b = new freesewing.Point(90, 10); - expect(freesewing.utils.beamCrossesY(a, b, 69)).to.equal(false); + expect(freesewing.utils.beamIntersectsY(a, b, 69)).to.equal(false); }); it("Should find no intersections between a curve and a line", () => { @@ -111,7 +111,7 @@ it("Should find no intersections between a curve and a line", () => { 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); + let hit = freesewing.utils.lineIntersectsCurve(E, D, A, Acp, Bcp, B); expect(hit).to.equal(false); }); @@ -123,7 +123,7 @@ it("Should find 1 intersection between a curve and a line", () => { 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); + let hit = freesewing.utils.lineIntersectsCurve(E, D, A, Acp, Bcp, B); expect(hit.x).to.equal(20.85); expect(hit.y).to.equal(11.11); }); @@ -136,7 +136,7 @@ it("Should find 3 intersections between a curve and a line", () => { 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); + let hits = freesewing.utils.lineIntersectsCurve(E, D, A, Acp, Bcp, B); expect(hits.length).to.equal(3); }); @@ -150,7 +150,7 @@ it("Should find 9 intersections between two curves", () => { 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); + let hits = freesewing.utils.curvesIntersect(A, Acp, Bcp, B, C, Ccp, Dcp, D); expect(hits.length).to.equal(9); }); @@ -164,7 +164,7 @@ it("Should find 1 intersection between two curves", () => { 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); + let hit = freesewing.utils.curvesIntersect(A, Acp, Bcp, B, C, Ccp, Dcp, D); expect(hit.x).to.equal(15.58); expect(hit.y).to.equal(10.56); }); @@ -179,7 +179,7 @@ it("Should find no intersection between two curves", () => { 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); + let hit = freesewing.utils.curvesIntersect(A, Acp, Bcp, B, C, Ccp, Dcp, D); expect(hit).to.equal(false); }); @@ -216,3 +216,239 @@ it("Should find a start or end point on curve", () => { expect(freesewing.utils.pointOnCurve(A, Acp, Bcp, B, checkA)).to.equal(true); expect(freesewing.utils.pointOnCurve(A, Acp, Bcp, B, checkB)).to.equal(true); }); + +it("Should find the intersections of a beam and circle", () => { + let A = new freesewing.Point(45, 45).attr("data-circle", 35); + let B = new freesewing.Point(5, 50); + let C = new freesewing.Point(25, 30); + let intersections = freesewing.utils.beamIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C, + "y" + ); + expect(intersections.length).to.equal(2); + expect(intersections[0].x).to.equal(45); + expect(intersections[0].y).to.equal(10); + expect(intersections[1].x).to.equal(10); + expect(intersections[1].y).to.equal(45); +}); + +it("Should not find the intersections of this beam and circle", () => { + let A = new freesewing.Point(75, 75).attr("data-circle", 35); + let B = new freesewing.Point(5, 5); + let C = new freesewing.Point(10, 5); + let intersections = freesewing.utils.beamIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C + ); + expect(intersections).to.equal(false); +}); + +it("Should find one intersections between this beam and circle", () => { + let A = new freesewing.Point(5, 5).attr("data-circle", 5); + let B = new freesewing.Point(0, 0); + let C = new freesewing.Point(-10, 0); + let intersections = freesewing.utils.beamIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C + ); + expect(intersections.length).to.equal(1); + expect(intersections[0].x).to.equal(5); + expect(intersections[0].y).to.equal(0); +}); + +it("Should find one intersections between this tangent and circle", () => { + let A = new freesewing.Point(5, 5).attr("data-circle", 5); + let B = new freesewing.Point(0, 0); + let C = new freesewing.Point(10, 0); + let intersections = freesewing.utils.lineIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C + ); + expect(intersections.length).to.equal(1); + expect(intersections[0].x).to.equal(5); + expect(intersections[0].y).to.equal(0); +}); + +it("Should find one intersection between this line and circle", () => { + let A = new freesewing.Point(5, 5).attr("data-circle", 5); + let B = new freesewing.Point(5, 5); + let C = new freesewing.Point(26, 25); + let intersections = freesewing.utils.lineIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C + ); + expect(intersections.length).to.equal(1); + expect(intersections[0].x).to.equal(8.62); + expect(intersections[0].y).to.equal(8.45); +}); + +it("Should not find an intersections between this line and circle", () => { + let A = new freesewing.Point(5, 5).attr("data-circle", 5); + let B = new freesewing.Point(0, 0); + let C = new freesewing.Point(-10, 0); + let intersections = freesewing.utils.lineIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C + ); + expect(intersections).to.equal(false); +}); + +it("Should find two intersections between this line and circle", () => { + let A = new freesewing.Point(6, 7).attr("data-circle", 5); + let B = new freesewing.Point(0, 10); + let C = new freesewing.Point(10, 0); + let intersections1 = freesewing.utils.lineIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C, + "foo" + ); + let intersections2 = freesewing.utils.lineIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C, + "x" + ); + let intersections3 = freesewing.utils.lineIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C, + "y" + ); + expect(intersections1.length).to.equal(2); + expect(intersections2.length).to.equal(2); + expect(intersections3.length).to.equal(2); + expect(intersections1[0].sitsOn(intersections2[1])).to.equal(true); + expect(intersections1[1].sitsOn(intersections2[0])).to.equal(true); + expect(intersections1[0].sitsOn(intersections3[0])).to.equal(true); + expect(intersections1[1].sitsOn(intersections3[1])).to.equal(true); + expect(intersections1[0].x).to.equal(7.7); + expect(intersections1[0].y).to.equal(2.3); + expect(intersections1[1].x).to.equal(1.3); + expect(intersections1[1].y).to.equal(8.7); +}); + +it("Should find the intersections of a line and circle", () => { + let A = new freesewing.Point(45, 45).attr("data-circle", 35); + let B = new freesewing.Point(5, 50); + let C = new freesewing.Point(25, 30); + let intersections = freesewing.utils.lineIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C + ); + expect(intersections.length).to.equal(1); + expect(intersections[0].x).to.equal(10); + expect(intersections[0].y).to.equal(45); +}); + +it("Should not find intersections of this line and circle", () => { + let A = new freesewing.Point(75, 75).attr("data-circle", 35); + let B = new freesewing.Point(5, 5); + let C = new freesewing.Point(10, 5); + let intersections = freesewing.utils.lineIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C + ); + expect(intersections).to.equal(false); +}); + +it("Should not find intersections of this line and circle", () => { + let A = new freesewing.Point(45, 45).attr("data-circle", 35); + let B = new freesewing.Point(40, 40); + let C = new freesewing.Point(52, 50); + let intersections = freesewing.utils.lineIntersectsCircle( + A, + A.attributes.get("data-circle"), + B, + C + ); + expect(intersections).to.equal(false); +}); + +it("Should find intersections between circles", () => { + A = new freesewing.Point(10, 10).attr("data-circle", 15); + B = new freesewing.Point(30, 30).attr("data-circle", 35); + + let intersections1 = freesewing.utils.circlesIntersect( + A, + A.attributes.get("data-circle"), + B, + B.attributes.get("data-circle") + ); + let intersections2 = freesewing.utils.circlesIntersect( + A, + A.attributes.get("data-circle"), + B, + B.attributes.get("data-circle"), + "y" + ); + expect(intersections1.length).to.equal(2); + expect(intersections2.length).to.equal(2); + expect(intersections1[0].x).to.equal(-2.81); + expect(intersections1[0].y).to.equal(17.8); + expect(intersections1[1].x).to.equal(17.8); + expect(intersections1[1].y).to.equal(-2.81); + expect(intersections2[0].x).to.equal(17.8); + expect(intersections2[0].y).to.equal(-2.81); + expect(intersections2[1].x).to.equal(-2.81); + expect(intersections2[1].y).to.equal(17.8); +}); + +it("Should not find intersections between non-overlapping circles", () => { + A = new freesewing.Point(10, 10).attr("data-circle", 15); + B = new freesewing.Point(90, 90).attr("data-circle", 35); + + let intersections = freesewing.utils.circlesIntersect( + A, + A.attributes.get("data-circle"), + B, + B.attributes.get("data-circle") + ); + expect(intersections).to.equal(false); +}); + +it("Should not find intersections between contained circles", () => { + A = new freesewing.Point(10, 10).attr("data-circle", 15); + B = new freesewing.Point(10, 10).attr("data-circle", 35); + + let intersections = freesewing.utils.circlesIntersect( + A, + A.attributes.get("data-circle"), + B, + B.attributes.get("data-circle") + ); + expect(intersections).to.equal(false); +}); + +it("Should not find intersections between identical circles", () => { + A = new freesewing.Point(10, 10).attr("data-circle", 35); + B = new freesewing.Point(10, 10).attr("data-circle", 35); + + let intersections = freesewing.utils.circlesIntersect( + A, + A.attributes.get("data-circle"), + B, + B.attributes.get("data-circle") + ); + expect(intersections).to.equal(false); +});