UNPKG

3.84 kBPlain TextView Raw
1import { isPointInPolygon } from './is-point-in-polygon';
2
3type Point = {
4 x: number;
5 y: number;
6};
7
8type Line = {
9 from: Point;
10 to: Point;
11};
12
13const isBetween = (value: number, min: number, max: number) => value >= min && value <= max;
14function 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
46function 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
80function 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
91type BBox = {
92 minX: number;
93 minY: number;
94 maxX: number;
95 maxY: number;
96};
97
98function 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
109function 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 * @see https://stackoverflow.com/questions/753140/how-do-i-determine-if-two-convex-polygons-intersect
115 */
116export function isPolygonsIntersect(points1: number[][], points2: number[][]) {
117 // 空数组,或者一个点返回 false
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}