@math.gl/polygon 3.5.7 → 3.6.0-alpha.3
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 +21 -0
- package/dist/cut-by-grid.d.ts.map +1 -0
- package/dist/cut-by-grid.js +214 -0
- package/dist/cut-by-mercator-bounds.d.ts +17 -0
- package/dist/cut-by-mercator-bounds.d.ts.map +1 -0
- package/dist/cut-by-mercator-bounds.js +144 -0
- package/dist/{esm/earcut.d.ts → earcut.d.ts} +4 -2
- package/dist/earcut.d.ts.map +1 -0
- package/dist/earcut.js +611 -0
- package/dist/es5/cut-by-grid.js +29 -25
- package/dist/es5/cut-by-grid.js.map +1 -1
- package/dist/es5/cut-by-mercator-bounds.js +24 -19
- package/dist/es5/cut-by-mercator-bounds.js.map +1 -1
- package/dist/es5/earcut.js +11 -10
- package/dist/es5/earcut.js.map +1 -1
- package/dist/es5/index.js.map +1 -1
- package/dist/es5/lineclip.js +20 -16
- package/dist/es5/lineclip.js.map +1 -1
- package/dist/es5/polygon-utils.js +6 -4
- package/dist/es5/polygon-utils.js.map +1 -1
- package/dist/es5/polygon.js +5 -0
- package/dist/es5/polygon.js.map +1 -1
- package/dist/es5/utils.js.map +1 -1
- package/dist/esm/cut-by-grid.js +5 -5
- package/dist/esm/cut-by-grid.js.map +1 -1
- package/dist/esm/cut-by-mercator-bounds.js +5 -5
- package/dist/esm/cut-by-mercator-bounds.js.map +1 -1
- package/dist/esm/earcut.js +9 -10
- package/dist/esm/earcut.js.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lineclip.js +5 -5
- package/dist/esm/lineclip.js.map +1 -1
- package/dist/esm/polygon-utils.js +7 -4
- package/dist/esm/polygon-utils.js.map +1 -1
- package/dist/esm/polygon.js +7 -0
- package/dist/esm/polygon.js.map +1 -1
- package/dist/esm/utils.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/lineclip.d.ts +30 -0
- package/dist/lineclip.d.ts.map +1 -0
- package/dist/lineclip.js +178 -0
- package/dist/{es5/polygon-utils.d.ts → polygon-utils.d.ts} +28 -64
- package/dist/polygon-utils.d.ts.map +1 -0
- package/dist/polygon-utils.js +140 -0
- package/dist/polygon.d.ts +41 -0
- package/dist/polygon.d.ts.map +1 -0
- package/dist/polygon.js +67 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +34 -0
- package/package.json +4 -3
- package/src/{cut-by-grid.js → cut-by-grid.ts} +79 -33
- package/src/{cut-by-mercator-bounds.js → cut-by-mercator-bounds.ts} +51 -22
- package/src/{earcut.js → earcut.ts} +26 -14
- package/src/{index.js → index.ts} +0 -0
- package/src/{lineclip.js → lineclip.ts} +61 -33
- package/src/polygon-utils.ts +228 -0
- package/src/polygon.ts +98 -0
- package/src/{utils.js → utils.ts} +11 -3
- package/dist/es5/cut-by-grid.d.ts +0 -24
- package/dist/es5/cut-by-mercator-bounds.d.ts +0 -23
- package/dist/es5/earcut.d.ts +0 -9
- package/dist/es5/lineclip.d.ts +0 -25
- package/dist/es5/polygon.d.ts +0 -36
- package/dist/esm/cut-by-grid.d.ts +0 -24
- package/dist/esm/cut-by-mercator-bounds.d.ts +0 -23
- package/dist/esm/lineclip.d.ts +0 -25
- package/dist/esm/polygon-utils.d.ts +0 -117
- package/dist/esm/polygon.d.ts +0 -36
- package/src/cut-by-grid.d.ts +0 -24
- package/src/cut-by-mercator-bounds.d.ts +0 -23
- package/src/earcut.d.ts +0 -9
- package/src/lineclip.d.ts +0 -25
- package/src/polygon-utils.d.ts +0 -117
- package/src/polygon-utils.js +0 -127
- package/src/polygon.d.ts +0 -36
- package/src/polygon.js +0 -66
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
/* eslint-disable max-statements, max-depth, complexity, no-unused-expressions */
|
|
2
|
-
import {bitCode, intersect} from './lineclip';
|
|
2
|
+
import {bitCode, intersect, BoundingBox} from './lineclip';
|
|
3
3
|
import {getPointAtIndex, copy, push} from './utils';
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import type {NumericArray} from '@math.gl/core';
|
|
6
|
+
|
|
7
|
+
export type Polygon = {
|
|
8
|
+
positions: number[];
|
|
9
|
+
holeIndices?: number[];
|
|
10
|
+
edgeTypes?: number[];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function cutPolylineByGrid(
|
|
14
|
+
positions: NumericArray,
|
|
15
|
+
options?: {
|
|
16
|
+
size?: number;
|
|
17
|
+
broken?: boolean;
|
|
18
|
+
gridResolution?: number;
|
|
19
|
+
gridOffset?: [number, number];
|
|
20
|
+
startIndex?: number;
|
|
21
|
+
endIndex?: number;
|
|
22
|
+
}
|
|
23
|
+
): number[] | number[][] {
|
|
6
24
|
const {
|
|
7
25
|
size = 2,
|
|
8
26
|
broken = false,
|
|
@@ -10,15 +28,15 @@ export function cutPolylineByGrid(positions, options = {}) {
|
|
|
10
28
|
gridOffset = [0, 0],
|
|
11
29
|
startIndex = 0,
|
|
12
30
|
endIndex = positions.length
|
|
13
|
-
} = options;
|
|
31
|
+
} = options || {};
|
|
14
32
|
const numPoints = (endIndex - startIndex) / size;
|
|
15
|
-
let part = [];
|
|
16
|
-
const result = [part];
|
|
17
|
-
const a = getPointAtIndex(positions, 0, size, startIndex);
|
|
18
|
-
let b;
|
|
19
|
-
let codeB;
|
|
20
|
-
const cell = getGridCell(a, gridResolution, gridOffset, []);
|
|
21
|
-
const scratchPoint = [];
|
|
33
|
+
let part: number[] = [];
|
|
34
|
+
const result: number[][] = [part];
|
|
35
|
+
const a: number[] = getPointAtIndex(positions, 0, size, startIndex);
|
|
36
|
+
let b: number[];
|
|
37
|
+
let codeB: number;
|
|
38
|
+
const cell: BoundingBox = getGridCell(a, gridResolution, gridOffset, []);
|
|
39
|
+
const scratchPoint: number[] = [];
|
|
22
40
|
push(part, a);
|
|
23
41
|
|
|
24
42
|
for (let i = 1; i < numPoints; i++) {
|
|
@@ -57,29 +75,39 @@ export function cutPolylineByGrid(positions, options = {}) {
|
|
|
57
75
|
const TYPE_INSIDE = 0;
|
|
58
76
|
const TYPE_BORDER = 1;
|
|
59
77
|
|
|
60
|
-
function concatInPlace(arr1, arr2) {
|
|
78
|
+
function concatInPlace(arr1: number[], arr2: number[]): number[] {
|
|
61
79
|
for (let i = 0; i < arr2.length; i++) {
|
|
62
80
|
arr1.push(arr2[i]);
|
|
63
81
|
}
|
|
64
82
|
return arr1;
|
|
65
83
|
}
|
|
66
84
|
|
|
67
|
-
export function cutPolygonByGrid(
|
|
85
|
+
export function cutPolygonByGrid(
|
|
86
|
+
positions: number[],
|
|
87
|
+
holeIndices: number[] | null = null,
|
|
88
|
+
options?: {
|
|
89
|
+
size?: number;
|
|
90
|
+
gridResolution?: number;
|
|
91
|
+
gridOffset?: [number, number];
|
|
92
|
+
edgeTypes?: boolean;
|
|
93
|
+
}
|
|
94
|
+
): Polygon[] {
|
|
68
95
|
if (!positions.length) {
|
|
69
96
|
// input is empty
|
|
70
97
|
return [];
|
|
71
98
|
}
|
|
72
|
-
const {size = 2, gridResolution = 10, gridOffset = [0, 0], edgeTypes = false} = options;
|
|
73
|
-
const result = [];
|
|
74
|
-
const queue = [
|
|
99
|
+
const {size = 2, gridResolution = 10, gridOffset = [0, 0], edgeTypes = false} = options || {};
|
|
100
|
+
const result: Polygon[] = [];
|
|
101
|
+
const queue: {pos: number[]; types: number[]; holes: number[]}[] = [
|
|
75
102
|
{
|
|
76
103
|
pos: positions,
|
|
77
|
-
types: edgeTypes
|
|
104
|
+
types: edgeTypes ? (new Array(positions.length / size).fill(TYPE_BORDER) as number[]) : null,
|
|
78
105
|
holes: holeIndices || []
|
|
79
106
|
}
|
|
80
107
|
];
|
|
81
|
-
const bbox = [[], []];
|
|
82
|
-
|
|
108
|
+
const bbox: number[][] = [[], []];
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
let cell: BoundingBox = [];
|
|
83
111
|
|
|
84
112
|
// Recursively bisect polygon until every part fit in a single grid cell
|
|
85
113
|
while (queue.length) {
|
|
@@ -118,7 +146,7 @@ export function cutPolygonByGrid(positions, holeIndices, options = {}) {
|
|
|
118
146
|
}
|
|
119
147
|
} else {
|
|
120
148
|
// Polygon fits in a single cell, no more processing required
|
|
121
|
-
const polygon = {positions: pos};
|
|
149
|
+
const polygon: Polygon = {positions: pos};
|
|
122
150
|
if (edgeTypes) {
|
|
123
151
|
polygon.edgeTypes = types;
|
|
124
152
|
}
|
|
@@ -136,17 +164,25 @@ export function cutPolygonByGrid(positions, holeIndices, options = {}) {
|
|
|
136
164
|
// TYPE_BORDER - edge from the original polygon
|
|
137
165
|
// TYPE_INSIDE - inside the original polygon
|
|
138
166
|
// eslint-disable-next-line max-params
|
|
139
|
-
function bisectPolygon(
|
|
167
|
+
function bisectPolygon(
|
|
168
|
+
positions: NumericArray,
|
|
169
|
+
edgeTypes: number[] | undefined,
|
|
170
|
+
size: number,
|
|
171
|
+
startIndex: number,
|
|
172
|
+
endIndex: number,
|
|
173
|
+
bbox: BoundingBox,
|
|
174
|
+
edge: number
|
|
175
|
+
) {
|
|
140
176
|
const numPoints = (endIndex - startIndex) / size;
|
|
141
|
-
const resultLow = [];
|
|
142
|
-
const resultHigh = [];
|
|
143
|
-
const typesLow = [];
|
|
144
|
-
const typesHigh = [];
|
|
145
|
-
const scratchPoint = [];
|
|
146
|
-
|
|
147
|
-
let p;
|
|
148
|
-
let side;
|
|
149
|
-
let type;
|
|
177
|
+
const resultLow: number[] = [];
|
|
178
|
+
const resultHigh: number[] = [];
|
|
179
|
+
const typesLow: number[] = [];
|
|
180
|
+
const typesHigh: number[] = [];
|
|
181
|
+
const scratchPoint: number[] = [];
|
|
182
|
+
|
|
183
|
+
let p: number[];
|
|
184
|
+
let side: number;
|
|
185
|
+
let type: number;
|
|
150
186
|
const prev = getPointAtIndex(positions, numPoints - 1, size, startIndex);
|
|
151
187
|
let prevSide = Math.sign(edge & 8 ? prev[1] - bbox[3] : prev[0] - bbox[2]);
|
|
152
188
|
let prevType = edgeTypes && edgeTypes[numPoints - 1];
|
|
@@ -189,7 +225,12 @@ function bisectPolygon(positions, edgeTypes, size, startIndex, endIndex, bbox, e
|
|
|
189
225
|
];
|
|
190
226
|
}
|
|
191
227
|
|
|
192
|
-
function getGridCell(
|
|
228
|
+
function getGridCell(
|
|
229
|
+
p: number[],
|
|
230
|
+
gridResolution: number,
|
|
231
|
+
gridOffset: [number, number],
|
|
232
|
+
out: number[]
|
|
233
|
+
): BoundingBox {
|
|
193
234
|
const left = Math.floor((p[0] - gridOffset[0]) / gridResolution) * gridResolution + gridOffset[0];
|
|
194
235
|
const bottom =
|
|
195
236
|
Math.floor((p[1] - gridOffset[1]) / gridResolution) * gridResolution + gridOffset[1];
|
|
@@ -197,10 +238,10 @@ function getGridCell(p, gridResolution, gridOffset, out) {
|
|
|
197
238
|
out[1] = bottom;
|
|
198
239
|
out[2] = left + gridResolution;
|
|
199
240
|
out[3] = bottom + gridResolution;
|
|
200
|
-
return out;
|
|
241
|
+
return out as BoundingBox;
|
|
201
242
|
}
|
|
202
243
|
|
|
203
|
-
function moveToNeighborCell(cell, gridResolution, edge) {
|
|
244
|
+
function moveToNeighborCell(cell: number[], gridResolution: number, edge: number): void {
|
|
204
245
|
if (edge & 8) {
|
|
205
246
|
// top
|
|
206
247
|
cell[1] += gridResolution;
|
|
@@ -220,7 +261,12 @@ function moveToNeighborCell(cell, gridResolution, edge) {
|
|
|
220
261
|
}
|
|
221
262
|
}
|
|
222
263
|
|
|
223
|
-
function getBoundingBox(
|
|
264
|
+
function getBoundingBox(
|
|
265
|
+
positions: NumericArray,
|
|
266
|
+
size: number,
|
|
267
|
+
endIndex: number,
|
|
268
|
+
out: number[][]
|
|
269
|
+
): number[][] {
|
|
224
270
|
let minX = Infinity;
|
|
225
271
|
let maxX = -Infinity;
|
|
226
272
|
let minY = Infinity;
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
import {cutPolylineByGrid, cutPolygonByGrid} from './cut-by-grid';
|
|
2
2
|
import {getPointAtIndex, push} from './utils';
|
|
3
|
+
import type {Polygon} from './cut-by-grid';
|
|
4
|
+
import type {NumericArray} from '@math.gl/core';
|
|
3
5
|
|
|
4
6
|
// https://en.wikipedia.org/wiki/Web_Mercator_projection
|
|
5
7
|
const DEFAULT_MAX_LATITUDE = 85.051129;
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
export function cutPolylineByMercatorBounds(
|
|
9
|
-
|
|
9
|
+
/** https://user-images.githubusercontent.com/2059298/78465769-938b7a00-76ae-11ea-9b95-1f4c26425ab9.png */
|
|
10
|
+
export function cutPolylineByMercatorBounds(
|
|
11
|
+
positions: NumericArray,
|
|
12
|
+
options?: {
|
|
13
|
+
size?: number;
|
|
14
|
+
startIndex?: number;
|
|
15
|
+
endIndex?: number;
|
|
16
|
+
normalize?: boolean;
|
|
17
|
+
}
|
|
18
|
+
): number[][] {
|
|
19
|
+
const {size = 2, startIndex = 0, endIndex = positions.length, normalize = true} = options || {};
|
|
10
20
|
|
|
11
21
|
// Remap longitudes so that each segment takes the shorter path
|
|
12
22
|
const newPositions = positions.slice(startIndex, endIndex);
|
|
@@ -17,7 +27,7 @@ export function cutPolylineByMercatorBounds(positions, options = {}) {
|
|
|
17
27
|
broken: true,
|
|
18
28
|
gridResolution: 360,
|
|
19
29
|
gridOffset: [-180, -180]
|
|
20
|
-
});
|
|
30
|
+
}) as number[][];
|
|
21
31
|
|
|
22
32
|
if (normalize) {
|
|
23
33
|
// Each part is guaranteed to be in a single copy of the world
|
|
@@ -29,12 +39,21 @@ export function cutPolylineByMercatorBounds(positions, options = {}) {
|
|
|
29
39
|
return parts;
|
|
30
40
|
}
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
export function cutPolygonByMercatorBounds(
|
|
34
|
-
|
|
42
|
+
/** https://user-images.githubusercontent.com/2059298/78465770-94241080-76ae-11ea-809a-6a8534dac1d9.png */
|
|
43
|
+
export function cutPolygonByMercatorBounds(
|
|
44
|
+
positions: number[],
|
|
45
|
+
holeIndices: number[] | null = null,
|
|
46
|
+
options?: {
|
|
47
|
+
size?: number;
|
|
48
|
+
normalize?: boolean;
|
|
49
|
+
maxLatitude?: number;
|
|
50
|
+
edgeTypes?: boolean;
|
|
51
|
+
}
|
|
52
|
+
): Polygon[] {
|
|
53
|
+
const {size = 2, normalize = true, edgeTypes = false} = options || {};
|
|
35
54
|
holeIndices = holeIndices || [];
|
|
36
|
-
const newPositions = [];
|
|
37
|
-
const newHoleIndices = [];
|
|
55
|
+
const newPositions: number[] = [];
|
|
56
|
+
const newHoleIndices: number[] = [];
|
|
38
57
|
let srcStartIndex = 0;
|
|
39
58
|
let targetIndex = 0;
|
|
40
59
|
|
|
@@ -59,7 +78,7 @@ export function cutPolygonByMercatorBounds(positions, holeIndices, options = {})
|
|
|
59
78
|
wrapLongitudesForShortestPath(newPositions, size, targetStartIndex, targetIndex);
|
|
60
79
|
|
|
61
80
|
// Handle the case when the ring contains a pole
|
|
62
|
-
insertPoleVertices(newPositions, size, targetStartIndex, targetIndex, options
|
|
81
|
+
insertPoleVertices(newPositions, size, targetStartIndex, targetIndex, options?.maxLatitude);
|
|
63
82
|
|
|
64
83
|
srcStartIndex = srcEndIndex;
|
|
65
84
|
newHoleIndices[ringIndex] = targetIndex;
|
|
@@ -86,7 +105,12 @@ export function cutPolygonByMercatorBounds(positions, holeIndices, options = {})
|
|
|
86
105
|
/* Helpers */
|
|
87
106
|
|
|
88
107
|
// See comments for insertPoleVertices
|
|
89
|
-
function findSplitIndex(
|
|
108
|
+
function findSplitIndex(
|
|
109
|
+
positions: number[],
|
|
110
|
+
size: number,
|
|
111
|
+
startIndex: number,
|
|
112
|
+
endIndex: number
|
|
113
|
+
): number {
|
|
90
114
|
let maxLat = -1;
|
|
91
115
|
let pointIndex = -1;
|
|
92
116
|
for (let i = startIndex + 1; i < endIndex; i += size) {
|
|
@@ -109,12 +133,12 @@ function findSplitIndex(positions, size, startIndex, endIndex) {
|
|
|
109
133
|
// intersect with the ring itself. This is ensured by findSplitIndex, which returns the
|
|
110
134
|
// vertex closest to the pole.
|
|
111
135
|
function insertPoleVertices(
|
|
112
|
-
positions,
|
|
113
|
-
size,
|
|
114
|
-
startIndex,
|
|
115
|
-
endIndex,
|
|
116
|
-
maxLatitude = DEFAULT_MAX_LATITUDE
|
|
117
|
-
) {
|
|
136
|
+
positions: number[],
|
|
137
|
+
size: number,
|
|
138
|
+
startIndex: number,
|
|
139
|
+
endIndex: number,
|
|
140
|
+
maxLatitude: number = DEFAULT_MAX_LATITUDE
|
|
141
|
+
): void {
|
|
118
142
|
// Check if the ring contains a pole
|
|
119
143
|
const firstLng = positions[startIndex];
|
|
120
144
|
const lastLng = positions[endIndex - size];
|
|
@@ -134,9 +158,14 @@ function insertPoleVertices(
|
|
|
134
158
|
}
|
|
135
159
|
}
|
|
136
160
|
|
|
137
|
-
function wrapLongitudesForShortestPath(
|
|
138
|
-
|
|
139
|
-
|
|
161
|
+
function wrapLongitudesForShortestPath(
|
|
162
|
+
positions: NumericArray,
|
|
163
|
+
size: number,
|
|
164
|
+
startIndex: number,
|
|
165
|
+
endIndex: number
|
|
166
|
+
): void {
|
|
167
|
+
let prevLng: number = positions[0];
|
|
168
|
+
let lng: number;
|
|
140
169
|
for (let i = startIndex; i < endIndex; i += size) {
|
|
141
170
|
lng = positions[i];
|
|
142
171
|
const delta = lng - prevLng;
|
|
@@ -147,8 +176,8 @@ function wrapLongitudesForShortestPath(positions, size, startIndex, endIndex) {
|
|
|
147
176
|
}
|
|
148
177
|
}
|
|
149
178
|
|
|
150
|
-
function shiftLongitudesIntoRange(positions, size) {
|
|
151
|
-
let refLng;
|
|
179
|
+
function shiftLongitudesIntoRange(positions: NumericArray, size: number): void {
|
|
180
|
+
let refLng: number;
|
|
152
181
|
const pointCount = positions.length / size;
|
|
153
182
|
|
|
154
183
|
// Find a longitude that is not on the edge of a world
|
|
@@ -22,16 +22,28 @@
|
|
|
22
22
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
/* eslint-disable
|
|
25
|
+
/* eslint-disable */
|
|
26
26
|
|
|
27
27
|
import {getPolygonSignedArea} from './polygon-utils';
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Computes a triangulation of a polygon
|
|
31
|
+
* @param positions a flat array of the vertex positions that define the polygon.
|
|
32
|
+
* @param holeIndices an array of hole indices if any (e.g. [5, 8] for a 12-vertex input would mean one hole with vertices 5–7 and another with 8–11).
|
|
33
|
+
* @param dim the number of elements in each vertex. Size `2` will interpret `positions` as `[x0, y0, x1, y1, ...]` and size `3` will interpret `positions` as `[x0, y0, z0, x1, y1, z1, ...]`. Default `2`.
|
|
34
|
+
* @param areas areas of outer polygon and holes as computed by `getPolygonSignedArea()`. Can be optionally supplied to speed up triangulation
|
|
35
|
+
* @returns array of indices into the `positions` array that describes the triangulation of the polygon
|
|
36
|
+
* Adapted from https://github.com/mapbox/earcut
|
|
37
|
+
*/
|
|
38
|
+
export function earcut(
|
|
39
|
+
positions: number[],
|
|
40
|
+
holeIndices?: number[],
|
|
41
|
+
dim: number = 2,
|
|
42
|
+
areas?: number[]
|
|
43
|
+
): number[] {
|
|
32
44
|
const hasHoles = holeIndices && holeIndices.length;
|
|
33
|
-
const outerLen = hasHoles ? holeIndices[0] * dim :
|
|
34
|
-
let outerNode = linkedList(
|
|
45
|
+
const outerLen = hasHoles ? holeIndices[0] * dim : positions.length;
|
|
46
|
+
let outerNode = linkedList(positions, 0, outerLen, dim, true, areas && areas[0]);
|
|
35
47
|
const triangles = [];
|
|
36
48
|
|
|
37
49
|
if (!outerNode || outerNode.next === outerNode.prev) return triangles;
|
|
@@ -44,16 +56,16 @@ export function earcut(data, holeIndices, dim, areas) {
|
|
|
44
56
|
let x;
|
|
45
57
|
let y;
|
|
46
58
|
|
|
47
|
-
if (hasHoles) outerNode = eliminateHoles(
|
|
59
|
+
if (hasHoles) outerNode = eliminateHoles(positions, holeIndices, outerNode, dim, areas);
|
|
48
60
|
|
|
49
61
|
// if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
|
|
50
|
-
if (
|
|
51
|
-
minX = maxX =
|
|
52
|
-
minY = maxY =
|
|
62
|
+
if (positions.length > 80 * dim) {
|
|
63
|
+
minX = maxX = positions[0];
|
|
64
|
+
minY = maxY = positions[1];
|
|
53
65
|
|
|
54
66
|
for (let i = dim; i < outerLen; i += dim) {
|
|
55
|
-
x =
|
|
56
|
-
y =
|
|
67
|
+
x = positions[i];
|
|
68
|
+
y = positions[i + 1];
|
|
57
69
|
if (x < minX) minX = x;
|
|
58
70
|
if (y < minY) minY = y;
|
|
59
71
|
if (x > maxX) maxX = x;
|
|
@@ -96,7 +108,7 @@ function linkedList(data, start, end, dim, clockwise, area) {
|
|
|
96
108
|
}
|
|
97
109
|
|
|
98
110
|
// eliminate colinear or duplicate points
|
|
99
|
-
function filterPoints(start, end) {
|
|
111
|
+
function filterPoints(start, end?) {
|
|
100
112
|
if (!start) return start;
|
|
101
113
|
if (!end) end = start;
|
|
102
114
|
|
|
@@ -119,7 +131,7 @@ function filterPoints(start, end) {
|
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
// main ear slicing loop which triangulates a polygon (given as a linked list)
|
|
122
|
-
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
|
|
134
|
+
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass?) {
|
|
123
135
|
if (!ear) return;
|
|
124
136
|
|
|
125
137
|
// interlink polygon nodes in z-order
|
|
File without changes
|
|
@@ -20,22 +20,34 @@
|
|
|
20
20
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
/* eslint-disable max-statements, max-depth */
|
|
23
|
+
/* eslint-disable max-statements, max-depth, complexity */
|
|
24
24
|
|
|
25
25
|
import {push, copy, getPointAtIndex} from './utils';
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
export type BoundingBox = [number, number, number, number];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Cohen-Sutherland line clipping algorithm, adapted to efficiently
|
|
31
|
+
* handle polylines rather than just segments
|
|
32
|
+
*/
|
|
33
|
+
export function clipPolyline(
|
|
34
|
+
positions: number[],
|
|
35
|
+
bbox: BoundingBox,
|
|
36
|
+
options?: {
|
|
37
|
+
size?: number;
|
|
38
|
+
startIndex?: number;
|
|
39
|
+
endIndex?: number;
|
|
40
|
+
}
|
|
41
|
+
): number[][] {
|
|
42
|
+
const {size = 2, startIndex = 0, endIndex = positions.length} = options || {};
|
|
31
43
|
const numPoints = (endIndex - startIndex) / size;
|
|
32
|
-
const result = [];
|
|
33
|
-
let part = [];
|
|
34
|
-
let a;
|
|
35
|
-
let b;
|
|
36
|
-
let codeA = -1;
|
|
37
|
-
let codeB;
|
|
38
|
-
let lastCode;
|
|
44
|
+
const result: number[][] = [];
|
|
45
|
+
let part: number[] = [];
|
|
46
|
+
let a: number[];
|
|
47
|
+
let b: number[];
|
|
48
|
+
let codeA: number = -1;
|
|
49
|
+
let codeB: number;
|
|
50
|
+
let lastCode: number;
|
|
39
51
|
|
|
40
52
|
for (let i = 1; i < numPoints; i++) {
|
|
41
53
|
a = getPointAtIndex(positions, i - 1, size, startIndex, a);
|
|
@@ -86,17 +98,27 @@ export function clipPolyline(positions, bbox, options = {}) {
|
|
|
86
98
|
return result;
|
|
87
99
|
}
|
|
88
100
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
101
|
+
/**
|
|
102
|
+
* Sutherland-Hodgeman polygon clipping algorithm
|
|
103
|
+
* polygon must be closed (first vertex == last vertex)
|
|
104
|
+
*/
|
|
105
|
+
export function clipPolygon(
|
|
106
|
+
positions: number[],
|
|
107
|
+
bbox: BoundingBox,
|
|
108
|
+
options?: {
|
|
109
|
+
size?: number;
|
|
110
|
+
startIndex?: number;
|
|
111
|
+
endIndex?: number;
|
|
112
|
+
}
|
|
113
|
+
): number[] {
|
|
114
|
+
const {size = 2, endIndex = positions.length} = options || {};
|
|
115
|
+
let {startIndex = 0} = options || {};
|
|
94
116
|
let numPoints = (endIndex - startIndex) / size;
|
|
95
|
-
let result;
|
|
96
|
-
let p;
|
|
97
|
-
let prev;
|
|
98
|
-
let inside;
|
|
99
|
-
let prevInside;
|
|
117
|
+
let result: number[];
|
|
118
|
+
let p: number[];
|
|
119
|
+
let prev: number[];
|
|
120
|
+
let inside: boolean;
|
|
121
|
+
let prevInside: boolean;
|
|
100
122
|
|
|
101
123
|
// clip against each side of the clip rectangle
|
|
102
124
|
for (let edge = 1; edge <= 8; edge *= 2) {
|
|
@@ -128,14 +150,20 @@ export function clipPolygon(positions, bbox, options = {}) {
|
|
|
128
150
|
return result;
|
|
129
151
|
}
|
|
130
152
|
|
|
131
|
-
|
|
153
|
+
/** intersect a segment against one of the 4 lines that make up the bbox */
|
|
132
154
|
|
|
133
|
-
export function intersect(
|
|
155
|
+
export function intersect(
|
|
156
|
+
a: number[],
|
|
157
|
+
b: number[],
|
|
158
|
+
edge: number,
|
|
159
|
+
bbox: BoundingBox,
|
|
160
|
+
out: number[] = []
|
|
161
|
+
): number[] {
|
|
134
162
|
let t;
|
|
135
163
|
// Forces out[snapI] to be on the bbox edge
|
|
136
164
|
// Interpolation introduces precision issue which may cause lineclip to be
|
|
137
165
|
// stuck in an infinite loop
|
|
138
|
-
let snap;
|
|
166
|
+
let snap: number;
|
|
139
167
|
if (edge & 8) {
|
|
140
168
|
// top
|
|
141
169
|
t = (bbox[3] - a[1]) / (b[1] - a[1]);
|
|
@@ -161,14 +189,14 @@ export function intersect(a, b, edge, bbox, out = []) {
|
|
|
161
189
|
return out;
|
|
162
190
|
}
|
|
163
191
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
export function bitCode(p, bbox) {
|
|
192
|
+
/**
|
|
193
|
+
* bit code reflects the point position relative to the bbox:
|
|
194
|
+
* left mid right
|
|
195
|
+
* top 1001 1000 1010
|
|
196
|
+
* mid 0001 0000 0010
|
|
197
|
+
* bottom 0101 0100 0110
|
|
198
|
+
*/
|
|
199
|
+
export function bitCode(p: number[], bbox: BoundingBox): number {
|
|
172
200
|
let code = 0;
|
|
173
201
|
|
|
174
202
|
if (p[0] < bbox[0]) code |= 1;
|