fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
1 lines • 13.3 kB
Source Map (JSON)
{"version":3,"file":"Intersection.min.mjs","names":[],"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 */\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 point is in the polygon defined by points\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 * @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 * @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 * @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 * @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 * @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 * @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 * @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"],"mappings":"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"}