@math.gl/polygon 4.0.0-beta.1 → 4.0.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.
@@ -1,5 +1,5 @@
1
1
  import type { NumericArray } from '@math.gl/core';
2
- export declare type Polygon = {
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
@@ -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
- const {
5
- size = 2,
6
- broken = false,
7
- gridResolution = 10,
8
- gridOffset = [0, 0],
9
- startIndex = 0,
10
- endIndex = positions.length
11
- } = options || {};
12
- const numPoints = (endIndex - startIndex) / size;
13
- let part = [];
14
- const result = [part];
15
- const a = getPointAtIndex(positions, 0, size, startIndex);
16
- let b;
17
- let codeB;
18
- const cell = getGridCell(a, gridResolution, gridOffset, []);
19
- const scratchPoint = [];
20
- push(part, a);
21
-
22
- for (let i = 1; i < numPoints; i++) {
23
- b = getPointAtIndex(positions, i, size, startIndex, b);
24
- codeB = bitCode(b, cell);
25
-
26
- while (codeB) {
27
- intersect(a, b, codeB, cell, scratchPoint);
28
- const codeAlt = bitCode(scratchPoint, cell);
29
-
30
- if (codeAlt) {
31
- intersect(a, scratchPoint, codeAlt, cell, scratchPoint);
32
- codeB = codeAlt;
33
- }
34
-
35
- push(part, scratchPoint);
36
- copy(a, scratchPoint);
37
- moveToNeighborCell(cell, gridResolution, codeB);
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
- if (!positions.length) {
58
- return [];
59
- }
60
-
61
- const {
62
- size = 2,
63
- gridResolution = 10,
64
- gridOffset = [0, 0],
65
- edgeTypes = false
66
- } = options || {};
67
- const result = [];
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
- if (parts[1]) {
113
- polygonHigh.holes.push(polygonHigh.pos.length);
114
- polygonHigh.pos = concatInPlace(polygonHigh.pos, parts[1].pos);
115
-
116
- if (edgeTypes) {
117
- polygonHigh.types = concatInPlace(polygonHigh.types, parts[1].types);
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
- const numPoints = (endIndex - startIndex) / size;
143
- const resultLow = [];
144
- const resultHigh = [];
145
- const typesLow = [];
146
- const typesHigh = [];
147
- const scratchPoint = [];
148
- let p;
149
- let side;
150
- let type;
151
- const prev = getPointAtIndex(positions, numPoints - 1, size, startIndex);
152
- let prevSide = Math.sign(edge & 8 ? prev[1] - bbox[3] : prev[0] - bbox[2]);
153
- let prevType = edgeTypes && edgeTypes[numPoints - 1];
154
- let lowPointCount = 0;
155
- let highPointCount = 0;
156
-
157
- for (let i = 0; i < numPoints; i++) {
158
- p = getPointAtIndex(positions, i, size, startIndex, p);
159
- side = Math.sign(edge & 8 ? p[1] - bbox[3] : p[0] - bbox[2]);
160
- type = edgeTypes && edgeTypes[startIndex / size + i];
161
-
162
- if (side && prevSide && prevSide !== side) {
163
- intersect(prev, p, edge, bbox, scratchPoint);
164
- push(resultLow, scratchPoint) && typesLow.push(prevType);
165
- push(resultHigh, scratchPoint) && typesHigh.push(prevType);
166
- }
167
-
168
- if (side <= 0) {
169
- push(resultLow, p) && typesLow.push(type);
170
- lowPointCount -= side;
171
- } else if (typesLow.length) {
172
- typesLow[typesLow.length - 1] = TYPE_INSIDE;
173
- }
174
-
175
- if (side >= 0) {
176
- push(resultHigh, p) && typesHigh.push(type);
177
- highPointCount += side;
178
- } else if (typesHigh.length) {
179
- typesHigh[typesHigh.length - 1] = TYPE_INSIDE;
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
- copy(prev, p);
183
- prevSide = side;
184
- prevType = type;
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
- const left = Math.floor((p[0] - gridOffset[0]) / gridResolution) * gridResolution + gridOffset[0];
198
- const bottom = Math.floor((p[1] - gridOffset[1]) / gridResolution) * gridResolution + gridOffset[1];
199
- out[0] = left;
200
- out[1] = bottom;
201
- out[2] = left + gridResolution;
202
- out[3] = bottom + gridResolution;
203
- return out;
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
- if (edge & 8) {
208
- cell[1] += gridResolution;
209
- cell[3] += gridResolution;
210
- } else if (edge & 4) {
211
- cell[1] -= gridResolution;
212
- cell[3] -= gridResolution;
213
- } else if (edge & 2) {
214
- cell[0] += gridResolution;
215
- cell[2] += gridResolution;
216
- } else if (edge & 1) {
217
- cell[0] -= gridResolution;
218
- cell[2] -= gridResolution;
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
- let minX = Infinity;
224
- let maxX = -Infinity;
225
- let minY = Infinity;
226
- let maxY = -Infinity;
227
-
228
- for (let i = 0; i < endIndex; i += size) {
229
- const x = positions[i];
230
- const y = positions[i + 1];
231
- minX = x < minX ? x : minX;
232
- maxX = x > maxX ? x : maxX;
233
- minY = y < minY ? y : minY;
234
- maxY = y > maxY ? y : maxY;
235
- }
236
-
237
- out[0][0] = minX;
238
- out[0][1] = minY;
239
- out[1][0] = maxX;
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
- for (let i = 0; i < arr2.length; i++) {
246
- arr1.push(arr2[i]);
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 './cut-by-grid';
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