UNPKG

fabric

Version:

Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.

1 lines 14.8 kB
{"version":3,"file":"Intersection.min.mjs","sources":["../../src/Intersection.ts"],"sourcesContent":["import { Point } from './Point';\nimport { createVector } from './util/misc/vectors';\n\n/* Adaptation of work of Kevin Lindsey (kevin@kevlindev.com) */\n\nexport type IntersectionType = 'Intersection' | 'Coincident' | 'Parallel';\n\nexport class Intersection {\n declare points: Point[];\n\n declare status?: IntersectionType;\n\n constructor(status?: IntersectionType) {\n this.status = status;\n this.points = [];\n }\n\n /**\n * Used to verify if a point is alredy in the collection\n * @param {Point} point\n * @returns {boolean}\n */\n private includes(point: Point): boolean {\n return this.points.some((p) => p.eq(point));\n }\n\n /**\n * Appends points of intersection\n * @param {...Point[]} points\n * @return {Intersection} thisArg\n * @chainable\n */\n private append(...points: Point[]): Intersection {\n this.points = this.points.concat(\n points.filter((point) => {\n return !this.includes(point);\n }),\n );\n return this;\n }\n\n /**\n * check if point T is on the segment or line defined between A and B\n *\n * @param {Point} T the point we are checking for\n * @param {Point} A one extremity of the segment\n * @param {Point} B the other extremity of the segment\n * @param [infinite] if true checks if `T` is on the line defined by `A` and `B`\n * @returns true if `T` is contained\n */\n static isPointContained(T: Point, A: Point, B: Point, infinite = false) {\n if (A.eq(B)) {\n // Edge case: the segment is a point, we check for coincidence,\n // infinite param has no meaning because there are infinite lines to consider\n return T.eq(A);\n } else if (A.x === B.x) {\n // Edge case: horizontal line.\n // we first check if T.x has the same value, and then if T.y is contained between A.y and B.y\n return (\n T.x === A.x &&\n (infinite || (T.y >= Math.min(A.y, B.y) && T.y <= Math.max(A.y, B.y)))\n );\n } else if (A.y === B.y) {\n // Edge case: vertical line.\n // we first check if T.y has the same value, and then if T.x is contained between A.x and B.x\n return (\n T.y === A.y &&\n (infinite || (T.x >= Math.min(A.x, B.x) && T.x <= Math.max(A.x, B.x)))\n );\n } else {\n // Generic case: sloped line.\n // we check that AT has the same slope as AB\n // for the segment case we need both the vectors to have the same direction and for AT to be lte AB in size\n // for the infinite case we check the absolute value of the slope, since direction is meaningless\n const AB = createVector(A, B);\n const AT = createVector(A, T);\n const s = AT.divide(AB);\n return infinite\n ? Math.abs(s.x) === Math.abs(s.y)\n : s.x === s.y && s.x >= 0 && s.x <= 1;\n }\n }\n\n /**\n * Use the ray casting algorithm to determine if {@link Point} is in the polygon defined by [points]{@link Point}\n * @see https://en.wikipedia.org/wiki/Point_in_polygon\n * @param point\n * @param points polygon points\n * @returns\n */\n static isPointInPolygon(point: Point, points: Point[]) {\n const other = new Point(point).setX(\n Math.min(point.x - 1, ...points.map((p) => p.x)),\n );\n let hits = 0;\n for (let index = 0; index < points.length; index++) {\n const inter = this.intersectSegmentSegment(\n // polygon side\n points[index],\n points[(index + 1) % points.length],\n // ray\n point,\n other,\n );\n if (inter.includes(point)) {\n // point is on the polygon side\n return true;\n }\n hits += Number(inter.status === 'Intersection');\n }\n return hits % 2 === 1;\n }\n\n /**\n * Checks if a line intersects another\n * @see {@link https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection line intersection}\n * @see {@link https://en.wikipedia.org/wiki/Cramer%27s_rule Cramer's rule}\n * @static\n * @param {Point} a1\n * @param {Point} a2\n * @param {Point} b1\n * @param {Point} b2\n * @param {boolean} [aInfinite=true] check segment intersection by passing `false`\n * @param {boolean} [bInfinite=true] check segment intersection by passing `false`\n * @return {Intersection}\n */\n static intersectLineLine(\n a1: Point,\n a2: Point,\n b1: Point,\n b2: Point,\n aInfinite = true,\n bInfinite = true,\n ): Intersection {\n const a2xa1x = a2.x - a1.x,\n a2ya1y = a2.y - a1.y,\n b2xb1x = b2.x - b1.x,\n b2yb1y = b2.y - b1.y,\n a1xb1x = a1.x - b1.x,\n a1yb1y = a1.y - b1.y,\n uaT = b2xb1x * a1yb1y - b2yb1y * a1xb1x,\n ubT = a2xa1x * a1yb1y - a2ya1y * a1xb1x,\n uB = b2yb1y * a2xa1x - b2xb1x * a2ya1y;\n if (uB !== 0) {\n const ua = uaT / uB,\n ub = ubT / uB;\n if (\n (aInfinite || (0 <= ua && ua <= 1)) &&\n (bInfinite || (0 <= ub && ub <= 1))\n ) {\n return new Intersection('Intersection').append(\n new Point(a1.x + ua * a2xa1x, a1.y + ua * a2ya1y),\n );\n } else {\n return new Intersection();\n }\n } else {\n if (uaT === 0 || ubT === 0) {\n const segmentsCoincide =\n aInfinite ||\n bInfinite ||\n Intersection.isPointContained(a1, b1, b2) ||\n Intersection.isPointContained(a2, b1, b2) ||\n Intersection.isPointContained(b1, a1, a2) ||\n Intersection.isPointContained(b2, a1, a2);\n return new Intersection(segmentsCoincide ? 'Coincident' : undefined);\n } else {\n return new Intersection('Parallel');\n }\n }\n }\n\n /**\n * Checks if a segment intersects a line\n * @see {@link intersectLineLine} for line intersection\n * @static\n * @param {Point} s1 boundary point of segment\n * @param {Point} s2 other boundary point of segment\n * @param {Point} l1 point on line\n * @param {Point} l2 other point on line\n * @return {Intersection}\n */\n static intersectSegmentLine(\n s1: Point,\n s2: Point,\n l1: Point,\n l2: Point,\n ): Intersection {\n return Intersection.intersectLineLine(s1, s2, l1, l2, false, true);\n }\n\n /**\n * Checks if a segment intersects another\n * @see {@link intersectLineLine} for line intersection\n * @static\n * @param {Point} a1 boundary point of segment\n * @param {Point} a2 other boundary point of segment\n * @param {Point} b1 boundary point of segment\n * @param {Point} b2 other boundary point of segment\n * @return {Intersection}\n */\n static intersectSegmentSegment(\n a1: Point,\n a2: Point,\n b1: Point,\n b2: Point,\n ): Intersection {\n return Intersection.intersectLineLine(a1, a2, b1, b2, false, false);\n }\n\n /**\n * Checks if line intersects polygon\n *\n * @todo account for stroke\n *\n * @static\n * @see {@link intersectSegmentPolygon} for segment intersection\n * @param {Point} a1 point on line\n * @param {Point} a2 other point on line\n * @param {Point[]} points polygon points\n * @param {boolean} [infinite=true] check segment intersection by passing `false`\n * @return {Intersection}\n */\n static intersectLinePolygon(\n a1: Point,\n a2: Point,\n points: Point[],\n infinite = true,\n ): Intersection {\n const result = new Intersection();\n const length = points.length;\n\n for (let i = 0, b1, b2, inter; i < length; i++) {\n b1 = points[i];\n b2 = points[(i + 1) % length];\n inter = Intersection.intersectLineLine(a1, a2, b1, b2, infinite, false);\n if (inter.status === 'Coincident') {\n return inter;\n }\n result.append(...inter.points);\n }\n\n if (result.points.length > 0) {\n result.status = 'Intersection';\n }\n\n return result;\n }\n\n /**\n * Checks if segment intersects polygon\n * @static\n * @see {@link intersectLinePolygon} for line intersection\n * @param {Point} a1 boundary point of segment\n * @param {Point} a2 other boundary point of segment\n * @param {Point[]} points polygon points\n * @return {Intersection}\n */\n static intersectSegmentPolygon(\n a1: Point,\n a2: Point,\n points: Point[],\n ): Intersection {\n return Intersection.intersectLinePolygon(a1, a2, points, false);\n }\n\n /**\n * Checks if polygon intersects another polygon\n *\n * @todo account for stroke\n *\n * @static\n * @param {Point[]} points1\n * @param {Point[]} points2\n * @return {Intersection}\n */\n static intersectPolygonPolygon(\n points1: Point[],\n points2: Point[],\n ): Intersection {\n const result = new Intersection(),\n length = points1.length;\n const coincidences: Intersection[] = [];\n\n for (let i = 0; i < length; i++) {\n const a1 = points1[i],\n a2 = points1[(i + 1) % length],\n inter = Intersection.intersectSegmentPolygon(a1, a2, points2);\n if (inter.status === 'Coincident') {\n coincidences.push(inter);\n result.append(a1, a2);\n } else {\n result.append(...inter.points);\n }\n }\n\n if (coincidences.length > 0 && coincidences.length === points1.length) {\n return new Intersection('Coincident');\n } else if (result.points.length > 0) {\n result.status = 'Intersection';\n }\n\n return result;\n }\n\n /**\n * Checks if polygon intersects rectangle\n * @static\n * @see {@link intersectPolygonPolygon} for polygon intersection\n * @param {Point[]} points polygon points\n * @param {Point} r1 top left point of rect\n * @param {Point} r2 bottom right point of rect\n * @return {Intersection}\n */\n static intersectPolygonRectangle(\n points: Point[],\n r1: Point,\n r2: Point,\n ): Intersection {\n const min = r1.min(r2),\n max = r1.max(r2),\n topRight = new Point(max.x, min.y),\n bottomLeft = new Point(min.x, max.y);\n\n return Intersection.intersectPolygonPolygon(points, [\n min,\n topRight,\n max,\n bottomLeft,\n ]);\n }\n}\n"],"names":["Intersection","constructor","status","this","points","includes","point","some","p","eq","append","_len","arguments","length","Array","_key","concat","filter","isPointContained","T","A","B","infinite","undefined","x","y","Math","min","max","AB","createVector","s","divide","abs","isPointInPolygon","other","Point","setX","map","hits","index","inter","intersectSegmentSegment","Number","intersectLineLine","a1","a2","b1","b2","aInfinite","bInfinite","a2xa1x","a2ya1y","b2xb1x","b2yb1y","a1xb1x","a1yb1y","uaT","ubT","uB","ua","ub","segmentsCoincide","intersectSegmentLine","s1","s2","l1","l2","intersectLinePolygon","result","i","intersectSegmentPolygon","intersectPolygonPolygon","points1","points2","coincidences","push","intersectPolygonRectangle","r1","r2","topRight","bottomLeft"],"mappings":"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"}