Skip to content

Commit

Permalink
Triangulate ourselves w/ earcut (fix with holes) (#51)
Browse files Browse the repository at this point in the history
* Custom earcut

* type ignore
  • Loading branch information
kylebarron authored Oct 21, 2023
1 parent a4467a1 commit 9a3c7d6
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 28 deletions.
3 changes: 0 additions & 3 deletions examples/multilinestring/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ const NAV_CONTROL_STYLE = {
left: 10,
};

console.log("multipoint")
console.log(arrow);

function Root() {
const onClick = (info) => {
if (info.object) {
Expand Down
3 changes: 0 additions & 3 deletions examples/multipoint/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,6 @@ const NAV_CONTROL_STYLE = {
left: 10,
};

console.log("multipoint")
console.log(arrow);

function Root() {
const onClick = (info) => {
if (info.object) {
Expand Down
3 changes: 0 additions & 3 deletions examples/multipolygon/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,6 @@ function Root() {
new GeoArrowSolidPolygonLayer({
id: "geoarrow-polygons",
data: table,
filled: true,
wireframe: true,
extruded: true,
getPolygon: table.getChild("geometry")!,
getFillColor: table.getChild("pop_colors")!,
pickable: true,
Expand Down
5 changes: 3 additions & 2 deletions examples/multipolygon/generate_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ def main():
min_pop = np.min(log_pop_est)
max_pop = np.max(log_pop_est)
normalized = (log_pop_est - min_pop) / (max_pop - min_pop)
colors = apply_continuous_cmap(normalized, PRGn_11)
colors = apply_continuous_cmap(normalized, PRGn_11, alpha=0.5)

table = table.append_column(
"pop_colors", pa.FixedSizeListArray.from_arrays(colors.flatten("C"), 3)
"pop_colors",
pa.FixedSizeListArray.from_arrays(colors.flatten("C"), colors.shape[-1]),
)

feather.write_feather(
Expand Down
23 changes: 23 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@
"peerDependencies": {
"@deck.gl/core": "^8.9.23",
"@deck.gl/layers": "^8.9.23",
"@math.gl/polygon": "^3.6.2",
"apache-arrow": "^13.0.0"
},
"devDependencies": {
"@deck.gl/core": "^8.9.23",
"@deck.gl/layers": "^8.9.23",
"@math.gl/polygon": "^3.6.2",
"@rollup/plugin-terser": "^0.4.3",
"@rollup/plugin-typescript": "^11.1.2",
"apache-arrow": "^13.0.0",
Expand Down
60 changes: 60 additions & 0 deletions src/earcut.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { earcut } from "@math.gl/polygon";
import { PolygonData } from "./types";
import { getLineStringChild, getPointChild, getPolygonChild } from "./utils";

export function earcutPolygonArray(data: PolygonData): Uint32Array {
const trianglesResults: number[][] = [];
let outputSize = 0;
for (let geomIndex = 0; geomIndex < data.length; geomIndex++) {
const triangles = earcutSinglePolygon(data, geomIndex);
trianglesResults.push(triangles);
outputSize += triangles.length;
}

const outputArray = new Uint32Array(outputSize);
let idx = 0;
for (const triangles of trianglesResults) {
for (const value of triangles) {
outputArray[idx] = value;
idx += 1;
}
}

return outputArray;
}

function earcutSinglePolygon(data: PolygonData, geomIndex: number): number[] {
const geomOffsets = data.valueOffsets;
const rings = getPolygonChild(data);
const ringOffsets = rings.valueOffsets;

const coords = getLineStringChild(rings);
const dim = coords.type.listSize;
const flatCoords = getPointChild(coords);

const ringBegin = geomOffsets[geomIndex];
const ringEnd = geomOffsets[geomIndex + 1];

const coordsBegin = ringOffsets[ringBegin];
const coordsEnd = ringOffsets[ringEnd];

const slicedFlatCoords = flatCoords.values.subarray(
coordsBegin * dim,
coordsEnd * dim
);

const initialCoordIndex = ringOffsets[ringBegin];
const holeIndices = [];
for (let holeRingIdx = ringBegin + 1; holeRingIdx < ringEnd; holeRingIdx++) {
holeIndices.push(ringOffsets[holeRingIdx] - initialCoordIndex);
}
// @ts-expect-error earcut typing is off. Should allow TypedArray but here
// only says number[]
const triangles = earcut(slicedFlatCoords, holeIndices, dim);

for (let i = 0; i < triangles.length; i++) {
triangles[i] += initialCoordIndex;
}

return triangles;
}
12 changes: 9 additions & 3 deletions src/path-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
GetPickingInfoParams,
Layer,
LayersList,
PickingInfo,
Unit,
} from "@deck.gl/core/typed";
import { PathLayer } from "@deck.gl/layers/typed";
Expand All @@ -28,7 +27,11 @@ import {
validateMultiLineStringType,
validateVectorAccessors,
} from "./utils.js";
import { LineStringVector, MultiLineStringVector } from "./types.js";
import {
GeoArrowPickingInfo,
LineStringVector,
MultiLineStringVector,
} from "./types.js";
import { EXTENSION_NAME } from "./constants.js";

const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255];
Expand Down Expand Up @@ -141,7 +144,10 @@ export class GeoArrowPathLayer<
static defaultProps = defaultProps;
static layerName = "GeoArrowPathLayer";

getPickingInfo({ info, sourceLayer }: GetPickingInfoParams): PickingInfo {
getPickingInfo({
info,
sourceLayer,
}: GetPickingInfoParams): GeoArrowPickingInfo {
const { data: table } = this.props;

// Geometry index as rendered
Expand Down
1 change: 0 additions & 1 deletion src/scatterplot-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
GetPickingInfoParams,
Layer,
LayersList,
PickingInfo,
Unit,
} from "@deck.gl/core/typed";
import { ScatterplotLayer } from "@deck.gl/layers/typed";
Expand Down
25 changes: 14 additions & 11 deletions src/solid-polygon-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
Layer,
LayersList,
GetPickingInfoParams,
PickingInfo,
} from "@deck.gl/core/typed";
import { SolidPolygonLayer } from "@deck.gl/layers/typed";
import type { SolidPolygonLayerProps } from "@deck.gl/layers/typed";
Expand Down Expand Up @@ -36,6 +35,7 @@ import {
PolygonVector,
} from "./types.js";
import { EXTENSION_NAME } from "./constants.js";
import { earcutPolygonArray } from "./earcut.js";

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

Expand Down Expand Up @@ -257,6 +257,8 @@ export class GeoArrowSolidPolygonLayer<

const resolvedRingOffsets = getPolygonResolvedOffsets(polygonData);

const earcutTriangles = earcutPolygonArray(polygonData);

const props: SolidPolygonLayerProps = {
// used for picking purposes
recordBatchIdx,
Expand All @@ -278,6 +280,7 @@ export class GeoArrowSolidPolygonLayer<
startIndices: resolvedRingOffsets,
attributes: {
getPolygon: { value: flatCoordinateArray, size: nDim },
indices: { value: earcutTriangles, size: 1 },
},
},
};
Expand Down Expand Up @@ -358,6 +361,8 @@ export class GeoArrowSolidPolygonLayer<
// const ringOffsets = ringData.valueOffsets;
const flatCoordinateArray = coordData.values;

const earcutTriangles = earcutPolygonArray(polygonData);

// NOTE: we have two different uses of offsets. One is for _rendering_
// each polygon. The other is for mapping _accessor attributes_ from one
// value per feature to one value per vertex. And for that we need to use
Expand Down Expand Up @@ -399,19 +404,18 @@ export class GeoArrowSolidPolygonLayer<
startIndices: resolvedPolygonToCoordOffsets,
attributes: {
getPolygon: { value: flatCoordinateArray, size: nDim },
instancePickingColors: {
value: encodePickingColors(
resolvedMultiPolygonToCoordOffsets,
this.encodePickingColor
),
size: 3,
},
indices: { value: earcutTriangles, size: 1 },
// instancePickingColors: {
// value: encodePickingColors(
// resolvedMultiPolygonToCoordOffsets,
// this.encodePickingColor
// ),
// size: 3,
// },
},
},
};

console.log(resolvedMultiPolygonToCoordOffsets);

assignAccessor({
props,
propName: "getElevation",
Expand Down Expand Up @@ -464,6 +468,5 @@ function encodePickingColors(
}
}

console.log(pickingColors);
return pickingColors;
}
4 changes: 2 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import * as arrow from "apache-arrow";

export type InterleavedCoord = arrow.FixedSizeList<arrow.Float64>;
export type SeparatedCoord = arrow.Struct<{
x: arrow.Float;
y: arrow.Float;
x: arrow.Float64;
y: arrow.Float64;
}>;
// TODO: support separated coords
export type Coord = InterleavedCoord; // | SeparatedCoord;
Expand Down

0 comments on commit 9a3c7d6

Please sign in to comment.