1 | import { isPointInPolygon } from './is-point-in-polygon';
|
2 |
|
3 | type Point = {
|
4 | x: number;
|
5 | y: number;
|
6 | };
|
7 |
|
8 | type Line = {
|
9 | from: Point;
|
10 | to: Point;
|
11 | };
|
12 |
|
13 | const isBetween = (value: number, min: number, max: number) => value >= min && value <= max;
|
14 | function getLineIntersect(p0: Point, p1: Point, p2: Point, p3: Point): Point | null {
|
15 | const tolerance = 0.001;
|
16 | const E: Point = {
|
17 | x: p2.x - p0.x,
|
18 | y: p2.y - p0.y,
|
19 | };
|
20 | const D0: Point = {
|
21 | x: p1.x - p0.x,
|
22 | y: p1.y - p0.y,
|
23 | };
|
24 | const D1: Point = {
|
25 | x: p3.x - p2.x,
|
26 | y: p3.y - p2.y,
|
27 | };
|
28 | const kross: number = D0.x * D1.y - D0.y * D1.x;
|
29 | const sqrKross: number = kross * kross;
|
30 | const sqrLen0: number = D0.x * D0.x + D0.y * D0.y;
|
31 | const sqrLen1: number = D1.x * D1.x + D1.y * D1.y;
|
32 | let point: Point | null = null;
|
33 | if (sqrKross > tolerance * sqrLen0 * sqrLen1) {
|
34 | const s = (E.x * D1.y - E.y * D1.x) / kross;
|
35 | const t = (E.x * D0.y - E.y * D0.x) / kross;
|
36 | if (isBetween(s, 0, 1) && isBetween(t, 0, 1)) {
|
37 | point = {
|
38 | x: p0.x + s * D0.x,
|
39 | y: p0.y + s * D0.y,
|
40 | };
|
41 | }
|
42 | }
|
43 | return point;
|
44 | }
|
45 |
|
46 | function parseToLines(points: number[][]): Line[] {
|
47 | const lines = [];
|
48 | const count = points.length;
|
49 | for (let i = 0; i < count - 1; i++) {
|
50 | const point = points[i];
|
51 | const next = points[i + 1];
|
52 | lines.push({
|
53 | from: {
|
54 | x: point[0],
|
55 | y: point[1],
|
56 | },
|
57 | to: {
|
58 | x: next[0],
|
59 | y: next[1],
|
60 | },
|
61 | });
|
62 | }
|
63 | if (lines.length > 1) {
|
64 | const first = points[0];
|
65 | const last = points[count - 1];
|
66 | lines.push({
|
67 | from: {
|
68 | x: last[0],
|
69 | y: last[1],
|
70 | },
|
71 | to: {
|
72 | x: first[0],
|
73 | y: first[1],
|
74 | },
|
75 | });
|
76 | }
|
77 | return lines;
|
78 | }
|
79 |
|
80 | function lineIntersectPolygon(lines: Line[], line: Line) {
|
81 | let isIntersect = false;
|
82 | lines.forEach((l) => {
|
83 | if (getLineIntersect(l.from, l.to, line.from, line.to)) {
|
84 | isIntersect = true;
|
85 | return false;
|
86 | }
|
87 | });
|
88 | return isIntersect;
|
89 | }
|
90 |
|
91 | type BBox = {
|
92 | minX: number;
|
93 | minY: number;
|
94 | maxX: number;
|
95 | maxY: number;
|
96 | };
|
97 |
|
98 | function getBBox(points: number[][]): BBox {
|
99 | const xArr = points.map((p) => p[0]);
|
100 | const yArr = points.map((p) => p[1]);
|
101 | return {
|
102 | minX: Math.min.apply(null, xArr),
|
103 | maxX: Math.max.apply(null, xArr),
|
104 | minY: Math.min.apply(null, yArr),
|
105 | maxY: Math.max.apply(null, yArr),
|
106 | };
|
107 | }
|
108 |
|
109 | function intersectBBox(box1: BBox, box2: BBox) {
|
110 | return !(box2.minX > box1.maxX || box2.maxX < box1.minX || box2.minY > box1.maxY || box2.maxY < box1.minY);
|
111 | }
|
112 |
|
113 |
|
114 |
|
115 |
|
116 | export function isPolygonsIntersect(points1: number[][], points2: number[][]) {
|
117 |
|
118 | if (points1.length < 2 || points2.length < 2) {
|
119 | return false;
|
120 | }
|
121 |
|
122 | const bbox1 = getBBox(points1);
|
123 | const bbox2 = getBBox(points2);
|
124 |
|
125 | if (!intersectBBox(bbox1, bbox2)) {
|
126 | return false;
|
127 | }
|
128 |
|
129 | let isIn = false;
|
130 |
|
131 | points2.forEach((point) => {
|
132 | if (isPointInPolygon(points1, point[0], point[1])) {
|
133 | isIn = true;
|
134 | return false;
|
135 | }
|
136 | });
|
137 | if (isIn) {
|
138 | return true;
|
139 | }
|
140 |
|
141 | points1.forEach((point) => {
|
142 | if (isPointInPolygon(points2, point[0], point[1])) {
|
143 | isIn = true;
|
144 | return false;
|
145 | }
|
146 | });
|
147 | if (isIn) {
|
148 | return true;
|
149 | }
|
150 |
|
151 | const lines1 = parseToLines(points1);
|
152 | const lines2 = parseToLines(points2);
|
153 | let isIntersect = false;
|
154 | lines2.forEach((line) => {
|
155 | if (lineIntersectPolygon(lines1, line)) {
|
156 | isIntersect = true;
|
157 | return false;
|
158 | }
|
159 | });
|
160 | return isIntersect;
|
161 | }
|