137 lines
3.7 KiB
TypeScript
137 lines
3.7 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
import { LayerExtension } from '@deck.gl/core';
|
|
import { ENUM_PALETTE } from './consts';
|
|
|
|
/**
|
|
* LayerExtension that turns polygon fills into pie charts.
|
|
*
|
|
* Follows the canonical deck.gl v9 extension pattern:
|
|
* - defaultProps with type:'accessor' enables getSubLayerProps() to wrap
|
|
* accessors via getSubLayerAccessor(), which unwraps __source.object to
|
|
* access the original data item through CompositeLayer sublayer chains.
|
|
* - stepMode:'dynamic' handles per-instance counting automatically.
|
|
* - isEnabled() restricts to SolidPolygonLayer (fill) sublayers only.
|
|
*
|
|
* Accepts an optional custom palette in the constructor for per-feature color overrides.
|
|
*/
|
|
|
|
function paletteToGlsl(palette: [number, number, number][]): string {
|
|
return palette
|
|
.map(
|
|
(c) =>
|
|
`vec3(${(c[0] / 255).toFixed(4)}, ${(c[1] / 255).toFixed(4)}, ${(c[2] / 255).toFixed(4)})`
|
|
)
|
|
.join(',\n ');
|
|
}
|
|
|
|
export class PieHexExtension extends LayerExtension {
|
|
static extensionName = 'PieHexExtension';
|
|
static defaultProps = {
|
|
getCenter: { type: 'accessor', value: [0, 0] },
|
|
getRatios0: { type: 'accessor', value: [1, 0, 0, 0] },
|
|
getRatios1: { type: 'accessor', value: [0, 0, 0, 0] },
|
|
getRatios2: { type: 'accessor', value: [0, 0] },
|
|
};
|
|
|
|
private paletteGlsl: string;
|
|
|
|
constructor(palette?: [number, number, number][]) {
|
|
super();
|
|
this.paletteGlsl = paletteToGlsl(palette ?? ENUM_PALETTE);
|
|
}
|
|
|
|
isEnabled(layer: any): boolean {
|
|
return layer.id.endsWith('-fill');
|
|
}
|
|
|
|
getShaders(this: any, extension: any): any {
|
|
if (!extension.isEnabled(this)) return null;
|
|
return {
|
|
modules: [
|
|
{
|
|
name: 'pieHex',
|
|
inject: {
|
|
'vs:#decl': `\
|
|
in vec2 instancePieCenter;
|
|
in vec4 instanceRatios0;
|
|
in vec4 instanceRatios1;
|
|
in vec2 instanceRatios2;
|
|
out vec2 vPieCenter;
|
|
out vec2 vPieFragPos;
|
|
out vec4 vRatios0;
|
|
out vec4 vRatios1;
|
|
out vec2 vRatios2;`,
|
|
'vs:#main-end': `\
|
|
vPieCenter = project_position(vec3(instancePieCenter, 0.0)).xy;
|
|
vPieFragPos = geometry.position.xy;
|
|
vRatios0 = instanceRatios0;
|
|
vRatios1 = instanceRatios1;
|
|
vRatios2 = instanceRatios2;`,
|
|
'fs:#decl': `\
|
|
in vec2 vPieCenter;
|
|
in vec2 vPieFragPos;
|
|
in vec4 vRatios0;
|
|
in vec4 vRatios1;
|
|
in vec2 vRatios2;
|
|
const vec3 pieColors[10] = vec3[10](
|
|
${extension.paletteGlsl}
|
|
);`,
|
|
'fs:DECKGL_FILTER_COLOR': `\
|
|
{
|
|
vec2 delta = vPieFragPos - vPieCenter;
|
|
float angle = atan(delta.x, -delta.y) / (2.0 * 3.14159265) + 0.5;
|
|
|
|
float ratios[10];
|
|
ratios[0] = vRatios0.x; ratios[1] = vRatios0.y;
|
|
ratios[2] = vRatios0.z; ratios[3] = vRatios0.w;
|
|
ratios[4] = vRatios1.x; ratios[5] = vRatios1.y;
|
|
ratios[6] = vRatios1.z; ratios[7] = vRatios1.w;
|
|
ratios[8] = vRatios2.x; ratios[9] = vRatios2.y;
|
|
|
|
float cumulative = 0.0;
|
|
vec3 sliceColor = pieColors[0];
|
|
for (int i = 0; i < 10; i++) {
|
|
cumulative += ratios[i];
|
|
if (angle < cumulative) {
|
|
sliceColor = pieColors[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
color = vec4(sliceColor, 1.0);
|
|
}`,
|
|
},
|
|
uniformTypes: {},
|
|
},
|
|
],
|
|
};
|
|
}
|
|
|
|
initializeState(this: any, _context: any, extension: any): void {
|
|
if (!extension.isEnabled(this)) return;
|
|
const am = this.getAttributeManager();
|
|
if (!am) return;
|
|
am.add({
|
|
instancePieCenter: {
|
|
size: 2,
|
|
stepMode: 'dynamic',
|
|
accessor: 'getCenter',
|
|
},
|
|
instanceRatios0: {
|
|
size: 4,
|
|
stepMode: 'dynamic',
|
|
accessor: 'getRatios0',
|
|
},
|
|
instanceRatios1: {
|
|
size: 4,
|
|
stepMode: 'dynamic',
|
|
accessor: 'getRatios1',
|
|
},
|
|
instanceRatios2: {
|
|
size: 2,
|
|
stepMode: 'dynamic',
|
|
accessor: 'getRatios2',
|
|
},
|
|
});
|
|
}
|
|
}
|