Skip to content

Commit 9a3c7d6

Browse files
authored
Triangulate ourselves w/ earcut (fix with holes) (#51)
* Custom earcut * type ignore
1 parent a4467a1 commit 9a3c7d6

File tree

11 files changed

+113
-28
lines changed

11 files changed

+113
-28
lines changed

examples/multilinestring/app.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ const NAV_CONTROL_STYLE = {
2424
left: 10,
2525
};
2626

27-
console.log("multipoint")
28-
console.log(arrow);
29-
3027
function Root() {
3128
const onClick = (info) => {
3229
if (info.object) {

examples/multipoint/app.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@ const NAV_CONTROL_STYLE = {
2424
left: 10,
2525
};
2626

27-
console.log("multipoint")
28-
console.log(arrow);
29-
3027
function Root() {
3128
const onClick = (info) => {
3229
if (info.object) {

examples/multipolygon/app.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@ function Root() {
5454
new GeoArrowSolidPolygonLayer({
5555
id: "geoarrow-polygons",
5656
data: table,
57-
filled: true,
58-
wireframe: true,
59-
extruded: true,
6057
getPolygon: table.getChild("geometry")!,
6158
getFillColor: table.getChild("pop_colors")!,
6259
pickable: true,

examples/multipolygon/generate_data.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ def main():
2727
min_pop = np.min(log_pop_est)
2828
max_pop = np.max(log_pop_est)
2929
normalized = (log_pop_est - min_pop) / (max_pop - min_pop)
30-
colors = apply_continuous_cmap(normalized, PRGn_11)
30+
colors = apply_continuous_cmap(normalized, PRGn_11, alpha=0.5)
3131

3232
table = table.append_column(
33-
"pop_colors", pa.FixedSizeListArray.from_arrays(colors.flatten("C"), 3)
33+
"pop_colors",
34+
pa.FixedSizeListArray.from_arrays(colors.flatten("C"), colors.shape[-1]),
3435
)
3536

3637
feather.write_feather(

package-lock.json

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,13 @@
3939
"peerDependencies": {
4040
"@deck.gl/core": "^8.9.23",
4141
"@deck.gl/layers": "^8.9.23",
42+
"@math.gl/polygon": "^3.6.2",
4243
"apache-arrow": "^13.0.0"
4344
},
4445
"devDependencies": {
4546
"@deck.gl/core": "^8.9.23",
4647
"@deck.gl/layers": "^8.9.23",
48+
"@math.gl/polygon": "^3.6.2",
4749
"@rollup/plugin-terser": "^0.4.3",
4850
"@rollup/plugin-typescript": "^11.1.2",
4951
"apache-arrow": "^13.0.0",

src/earcut.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { earcut } from "@math.gl/polygon";
2+
import { PolygonData } from "./types";
3+
import { getLineStringChild, getPointChild, getPolygonChild } from "./utils";
4+
5+
export function earcutPolygonArray(data: PolygonData): Uint32Array {
6+
const trianglesResults: number[][] = [];
7+
let outputSize = 0;
8+
for (let geomIndex = 0; geomIndex < data.length; geomIndex++) {
9+
const triangles = earcutSinglePolygon(data, geomIndex);
10+
trianglesResults.push(triangles);
11+
outputSize += triangles.length;
12+
}
13+
14+
const outputArray = new Uint32Array(outputSize);
15+
let idx = 0;
16+
for (const triangles of trianglesResults) {
17+
for (const value of triangles) {
18+
outputArray[idx] = value;
19+
idx += 1;
20+
}
21+
}
22+
23+
return outputArray;
24+
}
25+
26+
function earcutSinglePolygon(data: PolygonData, geomIndex: number): number[] {
27+
const geomOffsets = data.valueOffsets;
28+
const rings = getPolygonChild(data);
29+
const ringOffsets = rings.valueOffsets;
30+
31+
const coords = getLineStringChild(rings);
32+
const dim = coords.type.listSize;
33+
const flatCoords = getPointChild(coords);
34+
35+
const ringBegin = geomOffsets[geomIndex];
36+
const ringEnd = geomOffsets[geomIndex + 1];
37+
38+
const coordsBegin = ringOffsets[ringBegin];
39+
const coordsEnd = ringOffsets[ringEnd];
40+
41+
const slicedFlatCoords = flatCoords.values.subarray(
42+
coordsBegin * dim,
43+
coordsEnd * dim
44+
);
45+
46+
const initialCoordIndex = ringOffsets[ringBegin];
47+
const holeIndices = [];
48+
for (let holeRingIdx = ringBegin + 1; holeRingIdx < ringEnd; holeRingIdx++) {
49+
holeIndices.push(ringOffsets[holeRingIdx] - initialCoordIndex);
50+
}
51+
// @ts-expect-error earcut typing is off. Should allow TypedArray but here
52+
// only says number[]
53+
const triangles = earcut(slicedFlatCoords, holeIndices, dim);
54+
55+
for (let i = 0; i < triangles.length; i++) {
56+
triangles[i] += initialCoordIndex;
57+
}
58+
59+
return triangles;
60+
}

src/path-layer.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
GetPickingInfoParams,
88
Layer,
99
LayersList,
10-
PickingInfo,
1110
Unit,
1211
} from "@deck.gl/core/typed";
1312
import { PathLayer } from "@deck.gl/layers/typed";
@@ -28,7 +27,11 @@ import {
2827
validateMultiLineStringType,
2928
validateVectorAccessors,
3029
} from "./utils.js";
31-
import { LineStringVector, MultiLineStringVector } from "./types.js";
30+
import {
31+
GeoArrowPickingInfo,
32+
LineStringVector,
33+
MultiLineStringVector,
34+
} from "./types.js";
3235
import { EXTENSION_NAME } from "./constants.js";
3336

3437
const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255];
@@ -141,7 +144,10 @@ export class GeoArrowPathLayer<
141144
static defaultProps = defaultProps;
142145
static layerName = "GeoArrowPathLayer";
143146

144-
getPickingInfo({ info, sourceLayer }: GetPickingInfoParams): PickingInfo {
147+
getPickingInfo({
148+
info,
149+
sourceLayer,
150+
}: GetPickingInfoParams): GeoArrowPickingInfo {
145151
const { data: table } = this.props;
146152

147153
// Geometry index as rendered

src/scatterplot-layer.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
GetPickingInfoParams,
88
Layer,
99
LayersList,
10-
PickingInfo,
1110
Unit,
1211
} from "@deck.gl/core/typed";
1312
import { ScatterplotLayer } from "@deck.gl/layers/typed";

src/solid-polygon-layer.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
Layer,
99
LayersList,
1010
GetPickingInfoParams,
11-
PickingInfo,
1211
} from "@deck.gl/core/typed";
1312
import { SolidPolygonLayer } from "@deck.gl/layers/typed";
1413
import type { SolidPolygonLayerProps } from "@deck.gl/layers/typed";
@@ -36,6 +35,7 @@ import {
3635
PolygonVector,
3736
} from "./types.js";
3837
import { EXTENSION_NAME } from "./constants.js";
38+
import { earcutPolygonArray } from "./earcut.js";
3939

4040
const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255];
4141

@@ -257,6 +257,8 @@ export class GeoArrowSolidPolygonLayer<
257257

258258
const resolvedRingOffsets = getPolygonResolvedOffsets(polygonData);
259259

260+
const earcutTriangles = earcutPolygonArray(polygonData);
261+
260262
const props: SolidPolygonLayerProps = {
261263
// used for picking purposes
262264
recordBatchIdx,
@@ -278,6 +280,7 @@ export class GeoArrowSolidPolygonLayer<
278280
startIndices: resolvedRingOffsets,
279281
attributes: {
280282
getPolygon: { value: flatCoordinateArray, size: nDim },
283+
indices: { value: earcutTriangles, size: 1 },
281284
},
282285
},
283286
};
@@ -358,6 +361,8 @@ export class GeoArrowSolidPolygonLayer<
358361
// const ringOffsets = ringData.valueOffsets;
359362
const flatCoordinateArray = coordData.values;
360363

364+
const earcutTriangles = earcutPolygonArray(polygonData);
365+
361366
// NOTE: we have two different uses of offsets. One is for _rendering_
362367
// each polygon. The other is for mapping _accessor attributes_ from one
363368
// value per feature to one value per vertex. And for that we need to use
@@ -399,19 +404,18 @@ export class GeoArrowSolidPolygonLayer<
399404
startIndices: resolvedPolygonToCoordOffsets,
400405
attributes: {
401406
getPolygon: { value: flatCoordinateArray, size: nDim },
402-
instancePickingColors: {
403-
value: encodePickingColors(
404-
resolvedMultiPolygonToCoordOffsets,
405-
this.encodePickingColor
406-
),
407-
size: 3,
408-
},
407+
indices: { value: earcutTriangles, size: 1 },
408+
// instancePickingColors: {
409+
// value: encodePickingColors(
410+
// resolvedMultiPolygonToCoordOffsets,
411+
// this.encodePickingColor
412+
// ),
413+
// size: 3,
414+
// },
409415
},
410416
},
411417
};
412418

413-
console.log(resolvedMultiPolygonToCoordOffsets);
414-
415419
assignAccessor({
416420
props,
417421
propName: "getElevation",
@@ -464,6 +468,5 @@ function encodePickingColors(
464468
}
465469
}
466470

467-
console.log(pickingColors);
468471
return pickingColors;
469472
}

0 commit comments

Comments
 (0)