fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
1 lines • 13.4 kB
Source Map (JSON)
{"version":3,"file":"Intersection.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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"}