@math.gl/polygon 4.0.0 → 4.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cut-by-grid.d.ts +1 -2
- package/dist/cut-by-grid.js +194 -228
- package/dist/cut-by-mercator-bounds.d.ts +1 -2
- package/dist/cut-by-mercator-bounds.js +124 -124
- package/dist/earcut.d.ts +1 -2
- package/dist/earcut.js +577 -490
- package/dist/index.cjs +16 -36
- package/dist/index.cjs.map +7 -0
- package/dist/index.d.ts +7 -8
- package/dist/index.js +1 -1
- package/dist/lineclip.d.ts +1 -2
- package/dist/lineclip.js +163 -114
- package/dist/polygon-utils.d.ts +5 -6
- package/dist/polygon-utils.js +124 -107
- package/dist/polygon.d.ts +2 -3
- package/dist/polygon.js +61 -49
- package/dist/utils.d.ts +0 -1
- package/dist/utils.js +26 -34
- package/package.json +5 -5
- package/dist/cut-by-grid.d.ts.map +0 -1
- package/dist/cut-by-grid.js.map +0 -1
- package/dist/cut-by-mercator-bounds.d.ts.map +0 -1
- package/dist/cut-by-mercator-bounds.js.map +0 -1
- package/dist/earcut.d.ts.map +0 -1
- package/dist/earcut.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lineclip.d.ts.map +0 -1
- package/dist/lineclip.js.map +0 -1
- package/dist/polygon-utils.d.ts.map +0 -1
- package/dist/polygon-utils.js.map +0 -1
- package/dist/polygon.d.ts.map +0 -1
- package/dist/polygon.js.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js.map +0 -1
package/dist/cut-by-grid.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { NumericArray } from '@math.gl/core';
|
|
2
|
-
export
|
|
2
|
+
export type Polygon = {
|
|
3
3
|
positions: Readonly<NumericArray>;
|
|
4
4
|
holeIndices?: Readonly<NumericArray>;
|
|
5
5
|
edgeTypes?: Readonly<NumericArray>;
|
|
@@ -21,4 +21,3 @@ export declare function cutPolygonByGrid(positions: Readonly<NumericArray>, hole
|
|
|
21
21
|
gridOffset?: [number, number];
|
|
22
22
|
edgeTypes?: boolean;
|
|
23
23
|
}): Polygon[];
|
|
24
|
-
//# sourceMappingURL=cut-by-grid.d.ts.map
|
package/dist/cut-by-grid.js
CHANGED
|
@@ -1,251 +1,217 @@
|
|
|
1
|
+
/* eslint-disable max-statements, max-depth, complexity, no-unused-expressions */
|
|
1
2
|
import { bitCode, intersect } from "./lineclip.js";
|
|
2
3
|
import { getPointAtIndex, copy, push } from "./utils.js";
|
|
3
4
|
export function cutPolylineByGrid(positions, options) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (broken && part.length > size) {
|
|
40
|
-
part = [];
|
|
41
|
-
result.push(part);
|
|
42
|
-
push(part, a);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
codeB = bitCode(b, cell);
|
|
5
|
+
const { size = 2, broken = false, gridResolution = 10, gridOffset = [0, 0], startIndex = 0, endIndex = positions.length } = options || {};
|
|
6
|
+
const numPoints = (endIndex - startIndex) / size;
|
|
7
|
+
let part = [];
|
|
8
|
+
const result = [part];
|
|
9
|
+
const a = getPointAtIndex(positions, 0, size, startIndex);
|
|
10
|
+
let b;
|
|
11
|
+
let codeB;
|
|
12
|
+
const cell = getGridCell(a, gridResolution, gridOffset, []);
|
|
13
|
+
const scratchPoint = [];
|
|
14
|
+
push(part, a);
|
|
15
|
+
for (let i = 1; i < numPoints; i++) {
|
|
16
|
+
b = getPointAtIndex(positions, i, size, startIndex, b);
|
|
17
|
+
codeB = bitCode(b, cell);
|
|
18
|
+
while (codeB) {
|
|
19
|
+
// find the intersection with the current cell
|
|
20
|
+
intersect(a, b, codeB, cell, scratchPoint);
|
|
21
|
+
const codeAlt = bitCode(scratchPoint, cell);
|
|
22
|
+
if (codeAlt) {
|
|
23
|
+
intersect(a, scratchPoint, codeAlt, cell, scratchPoint);
|
|
24
|
+
codeB = codeAlt;
|
|
25
|
+
}
|
|
26
|
+
push(part, scratchPoint);
|
|
27
|
+
// move to the next cell
|
|
28
|
+
copy(a, scratchPoint);
|
|
29
|
+
moveToNeighborCell(cell, gridResolution, codeB);
|
|
30
|
+
if (broken && part.length > size) {
|
|
31
|
+
part = [];
|
|
32
|
+
result.push(part);
|
|
33
|
+
push(part, a);
|
|
34
|
+
}
|
|
35
|
+
codeB = bitCode(b, cell);
|
|
36
|
+
}
|
|
37
|
+
push(part, b);
|
|
38
|
+
copy(a, b);
|
|
46
39
|
}
|
|
47
|
-
|
|
48
|
-
push(part, b);
|
|
49
|
-
copy(a, b);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return broken ? result : result[0];
|
|
40
|
+
return broken ? result : result[0];
|
|
53
41
|
}
|
|
54
42
|
const TYPE_INSIDE = 0;
|
|
55
43
|
const TYPE_BORDER = 1;
|
|
44
|
+
/**
|
|
45
|
+
* Cuts a polygon by a pre-defined grid
|
|
46
|
+
*/
|
|
56
47
|
export function cutPolygonByGrid(positions, holeIndices = null, options) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const queue = [{
|
|
69
|
-
pos: positions,
|
|
70
|
-
types: edgeTypes ? new Array(positions.length / size).fill(TYPE_BORDER) : null,
|
|
71
|
-
holes: holeIndices || []
|
|
72
|
-
}];
|
|
73
|
-
const bbox = [[], []];
|
|
74
|
-
let cell = [];
|
|
75
|
-
|
|
76
|
-
while (queue.length) {
|
|
77
|
-
const {
|
|
78
|
-
pos,
|
|
79
|
-
types,
|
|
80
|
-
holes
|
|
81
|
-
} = queue.shift();
|
|
82
|
-
getBoundingBox(pos, size, holes[0] || pos.length, bbox);
|
|
83
|
-
cell = getGridCell(bbox[0], gridResolution, gridOffset, cell);
|
|
84
|
-
const code = bitCode(bbox[1], cell);
|
|
85
|
-
|
|
86
|
-
if (code) {
|
|
87
|
-
let parts = bisectPolygon(pos, types, size, 0, holes[0] || pos.length, cell, code);
|
|
88
|
-
const polygonLow = {
|
|
89
|
-
pos: parts[0].pos,
|
|
90
|
-
types: parts[0].types,
|
|
91
|
-
holes: []
|
|
92
|
-
};
|
|
93
|
-
const polygonHigh = {
|
|
94
|
-
pos: parts[1].pos,
|
|
95
|
-
types: parts[1].types,
|
|
96
|
-
holes: []
|
|
97
|
-
};
|
|
98
|
-
queue.push(polygonLow, polygonHigh);
|
|
99
|
-
|
|
100
|
-
for (let i = 0; i < holes.length; i++) {
|
|
101
|
-
parts = bisectPolygon(pos, types, size, holes[i], holes[i + 1] || pos.length, cell, code);
|
|
102
|
-
|
|
103
|
-
if (parts[0]) {
|
|
104
|
-
polygonLow.holes.push(polygonLow.pos.length);
|
|
105
|
-
polygonLow.pos = concatInPlace(polygonLow.pos, parts[0].pos);
|
|
106
|
-
|
|
107
|
-
if (edgeTypes) {
|
|
108
|
-
polygonLow.types = concatInPlace(polygonLow.types, parts[0].types);
|
|
109
|
-
}
|
|
48
|
+
if (!positions.length) {
|
|
49
|
+
// input is empty
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const { size = 2, gridResolution = 10, gridOffset = [0, 0], edgeTypes = false } = options || {};
|
|
53
|
+
const result = [];
|
|
54
|
+
const queue = [
|
|
55
|
+
{
|
|
56
|
+
pos: positions,
|
|
57
|
+
types: edgeTypes ? new Array(positions.length / size).fill(TYPE_BORDER) : null,
|
|
58
|
+
holes: holeIndices || []
|
|
110
59
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
60
|
+
];
|
|
61
|
+
const bbox = [[], []];
|
|
62
|
+
// @ts-ignore
|
|
63
|
+
let cell = [];
|
|
64
|
+
// Recursively bisect polygon until every part fit in a single grid cell
|
|
65
|
+
while (queue.length) {
|
|
66
|
+
const { pos, types, holes } = queue.shift();
|
|
67
|
+
// Get the bounding box of the outer polygon
|
|
68
|
+
getBoundingBox(pos, size, holes[0] || pos.length, bbox);
|
|
69
|
+
cell = getGridCell(bbox[0], gridResolution, gridOffset, cell);
|
|
70
|
+
const code = bitCode(bbox[1], cell);
|
|
71
|
+
if (code) {
|
|
72
|
+
// Split the outer ring at the boundary
|
|
73
|
+
let parts = bisectPolygon(pos, types, size, 0, holes[0] || pos.length, cell, code);
|
|
74
|
+
const polygonLow = { pos: parts[0].pos, types: parts[0].types, holes: [] };
|
|
75
|
+
const polygonHigh = { pos: parts[1].pos, types: parts[1].types, holes: [] };
|
|
76
|
+
queue.push(polygonLow, polygonHigh);
|
|
77
|
+
// Split each hole at the boundary
|
|
78
|
+
for (let i = 0; i < holes.length; i++) {
|
|
79
|
+
parts = bisectPolygon(pos, types, size, holes[i], holes[i + 1] || pos.length, cell, code);
|
|
80
|
+
if (parts[0]) {
|
|
81
|
+
polygonLow.holes.push(polygonLow.pos.length);
|
|
82
|
+
polygonLow.pos = concatInPlace(polygonLow.pos, parts[0].pos);
|
|
83
|
+
if (edgeTypes) {
|
|
84
|
+
polygonLow.types = concatInPlace(polygonLow.types, parts[0].types);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (parts[1]) {
|
|
88
|
+
polygonHigh.holes.push(polygonHigh.pos.length);
|
|
89
|
+
polygonHigh.pos = concatInPlace(polygonHigh.pos, parts[1].pos);
|
|
90
|
+
if (edgeTypes) {
|
|
91
|
+
polygonHigh.types = concatInPlace(polygonHigh.types, parts[1].types);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Polygon fits in a single cell, no more processing required
|
|
98
|
+
const polygon = { positions: pos };
|
|
99
|
+
if (edgeTypes) {
|
|
100
|
+
polygon.edgeTypes = types;
|
|
101
|
+
}
|
|
102
|
+
if (holes.length) {
|
|
103
|
+
polygon.holeIndices = holes;
|
|
104
|
+
}
|
|
105
|
+
result.push(polygon);
|
|
119
106
|
}
|
|
120
|
-
}
|
|
121
|
-
} else {
|
|
122
|
-
const polygon = {
|
|
123
|
-
positions: pos
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
if (edgeTypes) {
|
|
127
|
-
polygon.edgeTypes = types;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
if (holes.length) {
|
|
131
|
-
polygon.holeIndices = holes;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
result.push(polygon);
|
|
135
107
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return result;
|
|
108
|
+
return result;
|
|
139
109
|
}
|
|
140
|
-
|
|
110
|
+
// edgeTypes:
|
|
111
|
+
// TYPE_BORDER - edge from the original polygon
|
|
112
|
+
// TYPE_INSIDE - inside the original polygon
|
|
113
|
+
// eslint-disable-next-line max-params
|
|
141
114
|
function bisectPolygon(positions, edgeTypes, size, startIndex, endIndex, bbox, edge) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
115
|
+
const numPoints = (endIndex - startIndex) / size;
|
|
116
|
+
const resultLow = [];
|
|
117
|
+
const resultHigh = [];
|
|
118
|
+
const typesLow = [];
|
|
119
|
+
const typesHigh = [];
|
|
120
|
+
const scratchPoint = [];
|
|
121
|
+
let p;
|
|
122
|
+
let side;
|
|
123
|
+
let type;
|
|
124
|
+
const prev = getPointAtIndex(positions, numPoints - 1, size, startIndex);
|
|
125
|
+
let prevSide = Math.sign(edge & 8 ? prev[1] - bbox[3] : prev[0] - bbox[2]);
|
|
126
|
+
let prevType = edgeTypes && edgeTypes[numPoints - 1];
|
|
127
|
+
let lowPointCount = 0;
|
|
128
|
+
let highPointCount = 0;
|
|
129
|
+
for (let i = 0; i < numPoints; i++) {
|
|
130
|
+
p = getPointAtIndex(positions, i, size, startIndex, p);
|
|
131
|
+
side = Math.sign(edge & 8 ? p[1] - bbox[3] : p[0] - bbox[2]);
|
|
132
|
+
type = edgeTypes && edgeTypes[startIndex / size + i];
|
|
133
|
+
// if segment goes through the boundary, add an intersection
|
|
134
|
+
if (side && prevSide && prevSide !== side) {
|
|
135
|
+
intersect(prev, p, edge, bbox, scratchPoint);
|
|
136
|
+
push(resultLow, scratchPoint) && typesLow.push(prevType);
|
|
137
|
+
push(resultHigh, scratchPoint) && typesHigh.push(prevType);
|
|
138
|
+
}
|
|
139
|
+
if (side <= 0) {
|
|
140
|
+
push(resultLow, p) && typesLow.push(type);
|
|
141
|
+
lowPointCount -= side;
|
|
142
|
+
}
|
|
143
|
+
else if (typesLow.length) {
|
|
144
|
+
typesLow[typesLow.length - 1] = TYPE_INSIDE;
|
|
145
|
+
}
|
|
146
|
+
if (side >= 0) {
|
|
147
|
+
push(resultHigh, p) && typesHigh.push(type);
|
|
148
|
+
highPointCount += side;
|
|
149
|
+
}
|
|
150
|
+
else if (typesHigh.length) {
|
|
151
|
+
typesHigh[typesHigh.length - 1] = TYPE_INSIDE;
|
|
152
|
+
}
|
|
153
|
+
copy(prev, p);
|
|
154
|
+
prevSide = side;
|
|
155
|
+
prevType = type;
|
|
180
156
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return [lowPointCount ? {
|
|
188
|
-
pos: resultLow,
|
|
189
|
-
types: edgeTypes && typesLow
|
|
190
|
-
} : null, highPointCount ? {
|
|
191
|
-
pos: resultHigh,
|
|
192
|
-
types: edgeTypes && typesHigh
|
|
193
|
-
} : null];
|
|
157
|
+
return [
|
|
158
|
+
lowPointCount ? { pos: resultLow, types: edgeTypes && typesLow } : null,
|
|
159
|
+
highPointCount ? { pos: resultHigh, types: edgeTypes && typesHigh } : null
|
|
160
|
+
];
|
|
194
161
|
}
|
|
195
|
-
|
|
196
162
|
function getGridCell(p, gridResolution, gridOffset, out) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
163
|
+
const left = Math.floor((p[0] - gridOffset[0]) / gridResolution) * gridResolution + gridOffset[0];
|
|
164
|
+
const bottom = Math.floor((p[1] - gridOffset[1]) / gridResolution) * gridResolution + gridOffset[1];
|
|
165
|
+
out[0] = left;
|
|
166
|
+
out[1] = bottom;
|
|
167
|
+
out[2] = left + gridResolution;
|
|
168
|
+
out[3] = bottom + gridResolution;
|
|
169
|
+
return out;
|
|
204
170
|
}
|
|
205
|
-
|
|
206
171
|
function moveToNeighborCell(cell, gridResolution, edge) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
172
|
+
if (edge & 8) {
|
|
173
|
+
// top
|
|
174
|
+
cell[1] += gridResolution;
|
|
175
|
+
cell[3] += gridResolution;
|
|
176
|
+
}
|
|
177
|
+
else if (edge & 4) {
|
|
178
|
+
// bottom
|
|
179
|
+
cell[1] -= gridResolution;
|
|
180
|
+
cell[3] -= gridResolution;
|
|
181
|
+
}
|
|
182
|
+
else if (edge & 2) {
|
|
183
|
+
// right
|
|
184
|
+
cell[0] += gridResolution;
|
|
185
|
+
cell[2] += gridResolution;
|
|
186
|
+
}
|
|
187
|
+
else if (edge & 1) {
|
|
188
|
+
// left
|
|
189
|
+
cell[0] -= gridResolution;
|
|
190
|
+
cell[2] -= gridResolution;
|
|
191
|
+
}
|
|
220
192
|
}
|
|
221
|
-
|
|
222
193
|
function getBoundingBox(positions, size, endIndex, out) {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
out[1][1] = maxY;
|
|
241
|
-
return out;
|
|
194
|
+
let minX = Infinity;
|
|
195
|
+
let maxX = -Infinity;
|
|
196
|
+
let minY = Infinity;
|
|
197
|
+
let maxY = -Infinity;
|
|
198
|
+
for (let i = 0; i < endIndex; i += size) {
|
|
199
|
+
const x = positions[i];
|
|
200
|
+
const y = positions[i + 1];
|
|
201
|
+
minX = x < minX ? x : minX;
|
|
202
|
+
maxX = x > maxX ? x : maxX;
|
|
203
|
+
minY = y < minY ? y : minY;
|
|
204
|
+
maxY = y > maxY ? y : maxY;
|
|
205
|
+
}
|
|
206
|
+
out[0][0] = minX;
|
|
207
|
+
out[0][1] = minY;
|
|
208
|
+
out[1][0] = maxX;
|
|
209
|
+
out[1][1] = maxY;
|
|
210
|
+
return out;
|
|
242
211
|
}
|
|
243
|
-
|
|
244
212
|
function concatInPlace(arr1, arr2) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
return arr1;
|
|
213
|
+
for (let i = 0; i < arr2.length; i++) {
|
|
214
|
+
arr1.push(arr2[i]);
|
|
215
|
+
}
|
|
216
|
+
return arr1;
|
|
250
217
|
}
|
|
251
|
-
//# sourceMappingURL=cut-by-grid.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Polygon } from
|
|
1
|
+
import type { Polygon } from "./cut-by-grid.js";
|
|
2
2
|
import type { NumericArray } from '@math.gl/core';
|
|
3
3
|
/** https://user-images.githubusercontent.com/2059298/78465769-938b7a00-76ae-11ea-9b95-1f4c26425ab9.png */
|
|
4
4
|
export declare function cutPolylineByMercatorBounds(positions: Readonly<NumericArray>, options?: {
|
|
@@ -14,4 +14,3 @@ export declare function cutPolygonByMercatorBounds(positions: Readonly<NumericAr
|
|
|
14
14
|
maxLatitude?: number;
|
|
15
15
|
edgeTypes?: boolean;
|
|
16
16
|
}): Polygon[];
|
|
17
|
-
//# sourceMappingURL=cut-by-mercator-bounds.d.ts.map
|