@accelint/map-toolkit 2.0.0 → 3.0.0
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/CHANGELOG.md +50 -0
- package/README.md +8 -1
- package/catalog-info.yaml +9 -6
- package/dist/deckgl/base-map/index.d.ts +2 -2
- package/dist/deckgl/base-map/provider.d.ts +2 -2
- package/dist/deckgl/extensions/coffin-corner/coffin-corner-extension.d.ts +144 -0
- package/dist/deckgl/extensions/coffin-corner/coffin-corner-extension.js +535 -0
- package/dist/deckgl/extensions/coffin-corner/coffin-corner-extension.js.map +1 -0
- package/dist/deckgl/extensions/coffin-corner/index.d.ts +17 -0
- package/dist/deckgl/extensions/coffin-corner/index.js +19 -0
- package/dist/deckgl/extensions/coffin-corner/store.d.ts +96 -0
- package/dist/deckgl/extensions/coffin-corner/store.js +173 -0
- package/dist/deckgl/extensions/coffin-corner/store.js.map +1 -0
- package/dist/deckgl/extensions/coffin-corner/types.d.ts +76 -0
- package/dist/deckgl/extensions/coffin-corner/types.js +27 -0
- package/dist/deckgl/extensions/coffin-corner/types.js.map +1 -0
- package/dist/deckgl/extensions/coffin-corner/use-coffin-corner.d.ts +81 -0
- package/dist/deckgl/extensions/coffin-corner/use-coffin-corner.js +75 -0
- package/dist/deckgl/extensions/coffin-corner/use-coffin-corner.js.map +1 -0
- package/dist/deckgl/extensions/index.d.ts +15 -0
- package/dist/deckgl/extensions/index.js +16 -0
- package/dist/deckgl/index.d.ts +6 -1
- package/dist/deckgl/index.js +5 -1
- package/dist/deckgl/shapes/display-shape-layer/constants.js +6 -15
- package/dist/deckgl/shapes/display-shape-layer/constants.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/index.d.ts +31 -15
- package/dist/deckgl/shapes/display-shape-layer/index.js +97 -78
- package/dist/deckgl/shapes/display-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/types.d.ts +8 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/icon-config.js +3 -48
- package/dist/deckgl/shapes/display-shape-layer/utils/icon-config.js.map +1 -1
- package/dist/deckgl/shapes/display-shape-layer/utils/radius-label.js +53 -0
- package/dist/deckgl/shapes/display-shape-layer/utils/radius-label.js.map +1 -0
- package/dist/deckgl/shapes/draw-shape-layer/index.d.ts +7 -3
- package/dist/deckgl/shapes/draw-shape-layer/index.js +7 -3
- package/dist/deckgl/shapes/draw-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js +4 -2
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-circle-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js +3 -2
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-ellipse-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js +5 -2
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-line-string-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js +5 -2
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-polygon-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js +7 -2
- package/dist/deckgl/shapes/draw-shape-layer/modes/draw-rectangle-mode-with-tooltip.js.map +1 -1
- package/dist/deckgl/shapes/draw-shape-layer/types.d.ts +2 -2
- package/dist/deckgl/shapes/edit-shape-layer/index.d.ts +7 -4
- package/dist/deckgl/shapes/edit-shape-layer/index.js +22 -8
- package/dist/deckgl/shapes/edit-shape-layer/index.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js +3 -2
- package/dist/deckgl/shapes/edit-shape-layer/modes/bounding-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js +4 -2
- package/dist/deckgl/shapes/edit-shape-layer/modes/circle-transform-mode.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/store.js +15 -4
- package/dist/deckgl/shapes/edit-shape-layer/store.js.map +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/types.d.ts +4 -2
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.d.ts +1 -1
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js +7 -3
- package/dist/deckgl/shapes/edit-shape-layer/use-edit-shape.js.map +1 -1
- package/dist/deckgl/shapes/index.d.ts +3 -2
- package/dist/deckgl/shapes/index.js +2 -1
- package/dist/deckgl/shapes/shared/constants.js +1 -1
- package/dist/deckgl/shapes/shared/types.d.ts +11 -4
- package/dist/deckgl/shapes/shared/types.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/duplicate-shape.d.ts +56 -0
- package/dist/deckgl/shapes/shared/utils/duplicate-shape.js +131 -0
- package/dist/deckgl/shapes/shared/utils/duplicate-shape.js.map +1 -0
- package/dist/deckgl/shapes/shared/utils/geometry-measurements.js.map +1 -1
- package/dist/deckgl/shapes/shared/utils/layer-config.js +10 -7
- package/dist/deckgl/shapes/shared/utils/layer-config.js.map +1 -1
- package/dist/deckgl/symbol-layer/fiber.d.ts +3 -1
- package/dist/deckgl/symbol-layer/fiber.js.map +1 -1
- package/dist/shared/units.d.ts +15 -56
- package/dist/shared/units.js +1 -52
- package/dist/shared/units.js.map +1 -1
- package/dist/viewport/index.d.ts +2 -3
- package/dist/viewport/index.js +1 -2
- package/dist/viewport/types.d.ts +8 -4
- package/dist/viewport/utils.d.ts +3 -3
- package/dist/viewport/utils.js +16 -8
- package/dist/viewport/utils.js.map +1 -1
- package/dist/viewport/viewport-size.d.ts +4 -3
- package/dist/viewport/viewport-size.js +2 -2
- package/dist/viewport/viewport-size.js.map +1 -1
- package/package.json +13 -6
- package/dist/deckgl/shapes/display-shape-layer/utils/interaction.js +0 -50
- package/dist/deckgl/shapes/display-shape-layer/utils/interaction.js.map +0 -1
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import { createLoggerDomain } from "../../../shared/logger.js";
|
|
15
|
+
import { LayerExtension } from "@deck.gl/core";
|
|
16
|
+
import { IconLayer, ScatterplotLayer } from "@deck.gl/layers";
|
|
17
|
+
|
|
18
|
+
//#region src/deckgl/extensions/coffin-corner/coffin-corner-extension.ts
|
|
19
|
+
const logger = createLoggerDomain("[CoffinCornerExtension]");
|
|
20
|
+
/**
|
|
21
|
+
* Shader module defining the `highlightColor` uniform for coffin corner brackets.
|
|
22
|
+
*
|
|
23
|
+
* @property name - Module identifier used by deck.gl's shader assembly.
|
|
24
|
+
* @property fs - Fragment shader (GLSL) source declaring the uniform block.
|
|
25
|
+
* @property uniformTypes - Maps uniform names to WGSL-style type strings for deck.gl's uniform system.
|
|
26
|
+
*
|
|
27
|
+
* @remarks
|
|
28
|
+
* GLSL type reference:
|
|
29
|
+
* - `float` — single decimal number
|
|
30
|
+
* - `vec2` — 2-component vector (x, y)
|
|
31
|
+
* - `vec3` — 3-component vector (x, y, z) or (r, g, b)
|
|
32
|
+
* - `vec4` — 4-component vector (x, y, z, w) or (r, g, b, a)
|
|
33
|
+
* - `uniform` — read-only value passed from JS to the GPU each frame
|
|
34
|
+
* - `in/out` — values passed between vertex and fragment shader stages
|
|
35
|
+
*/
|
|
36
|
+
const coffinCornerModule = {
|
|
37
|
+
name: "coffinCorner",
|
|
38
|
+
fs: `\
|
|
39
|
+
uniform coffinCornerUniforms {
|
|
40
|
+
vec4 highlightColor; // RGBA color, each channel 0.0–1.0
|
|
41
|
+
} coffinCorner;
|
|
42
|
+
`,
|
|
43
|
+
uniformTypes: { highlightColor: "vec4<f32>" }
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Vertex declarations: per-instance attributes for selection/hover state,
|
|
47
|
+
* passed to the fragment shader as varyings.
|
|
48
|
+
*/
|
|
49
|
+
const VS_DECL = `\
|
|
50
|
+
in float instanceSelectedEntity;
|
|
51
|
+
in float instanceHoveredEntity;
|
|
52
|
+
out float vInstanceSelectedEntity; // v prefix is conventional for "varying"
|
|
53
|
+
out float vInstanceHoveredEntity;
|
|
54
|
+
`;
|
|
55
|
+
/** Vertex main-end: forward per-instance attributes to the fragment shader. */
|
|
56
|
+
const VS_MAIN_END = `\
|
|
57
|
+
vInstanceSelectedEntity = instanceSelectedEntity;
|
|
58
|
+
vInstanceHoveredEntity = instanceHoveredEntity;
|
|
59
|
+
`;
|
|
60
|
+
/**
|
|
61
|
+
* ScatterplotLayer vertex declarations: adds `vQuadScale` varying to
|
|
62
|
+
* communicate the quad expansion factor to the fragment shader.
|
|
63
|
+
*/
|
|
64
|
+
const SCATTERPLOT_VS_DECL = `\
|
|
65
|
+
${VS_DECL}out float vQuadScale;
|
|
66
|
+
`;
|
|
67
|
+
/**
|
|
68
|
+
* ScatterplotLayer vertex main-end: expands the quad when hovered/selected
|
|
69
|
+
* to provide room for coffin corner brackets beyond the circle edge.
|
|
70
|
+
*
|
|
71
|
+
* The expansion multiplies `unitPosition` by the scale factor so that
|
|
72
|
+
* fragment shader interpolation covers the larger area. The extra pixel
|
|
73
|
+
* offset is added to `gl_Position` in clip space.
|
|
74
|
+
*
|
|
75
|
+
* References ScatterplotLayer vertex-scope variables:
|
|
76
|
+
* `edgePadding`, `positions`, `outerRadiusPixels` (all still in scope at #main-end).
|
|
77
|
+
*/
|
|
78
|
+
const SCATTERPLOT_VS_MAIN_END = `\
|
|
79
|
+
${VS_MAIN_END}
|
|
80
|
+
// Expand the quad when hovered/selected to make room for bracket arms.
|
|
81
|
+
// Scale factor 2.0 gives brackets the same visual footprint as the circle diameter.
|
|
82
|
+
// Skip expansion in globe mode — clip-space XY manipulation causes depth conflicts
|
|
83
|
+
// with the globe surface (known deck.gl limitation, see PR #9975).
|
|
84
|
+
vQuadScale = 1.0;
|
|
85
|
+
if ((instanceSelectedEntity > 0.5 || instanceHoveredEntity > 0.5)
|
|
86
|
+
&& project.projectionMode != PROJECTION_MODE_GLOBE) {
|
|
87
|
+
vQuadScale = 2.0;
|
|
88
|
+
// Add extra offset in clip space (works for both billboard and non-billboard)
|
|
89
|
+
vec2 extraPixelOffset = (vQuadScale - 1.0) * edgePadding * positions.xy * outerRadiusPixels;
|
|
90
|
+
gl_Position.xy += project_pixel_size_to_clipspace(extraPixelOffset);
|
|
91
|
+
// Scale unitPosition so fragment interpolation covers the expanded quad
|
|
92
|
+
unitPosition *= vQuadScale;
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
/**
|
|
96
|
+
* Fragment declarations: SDF functions for bracket rendering.
|
|
97
|
+
*
|
|
98
|
+
* Signed distance functions (SDF) return negative inside the shape,
|
|
99
|
+
* zero on the edge, and positive outside. Used for anti-aliased rendering.
|
|
100
|
+
*/
|
|
101
|
+
const FS_DECL = `\
|
|
102
|
+
in float vInstanceSelectedEntity;
|
|
103
|
+
in float vInstanceHoveredEntity;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Signed distance to an axis-aligned box.
|
|
107
|
+
*
|
|
108
|
+
* @param position - point to measure distance from
|
|
109
|
+
* @param halfExtents - half-width and half-height of the box (distance from center to each edge)
|
|
110
|
+
* @returns signed distance: negative inside, zero on edge, positive outside
|
|
111
|
+
*/
|
|
112
|
+
float coffinCorner_signedDistBox(vec2 position, vec2 halfExtents) {
|
|
113
|
+
vec2 dist = abs(position) - halfExtents;
|
|
114
|
+
return length(max(dist, 0.0)) + min(max(dist.x, dist.y), 0.0);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Signed distance to an L-shaped bracket arm (horizontal + vertical box union).
|
|
119
|
+
*
|
|
120
|
+
* @param position - point to measure distance from
|
|
121
|
+
* @param length - length of each arm of the L-shape (fraction of icon size)
|
|
122
|
+
* @param width - stroke width of each arm (fraction of icon size)
|
|
123
|
+
* @returns signed distance to the nearest arm of the L-shape
|
|
124
|
+
*/
|
|
125
|
+
float coffinCorner_signedDistBracketArm(vec2 position, float length, float width) {
|
|
126
|
+
float horizontalDist = coffinCorner_signedDistBox(position - vec2(length * 0.5, 0.0), vec2(length * 0.5, width * 0.5));
|
|
127
|
+
float verticalDist = coffinCorner_signedDistBox(position - vec2(0.0, length * 0.5), vec2(width * 0.5, length * 0.5));
|
|
128
|
+
return min(horizontalDist, verticalDist);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Minimum signed distance from a point to any of the four corner brackets.
|
|
133
|
+
*
|
|
134
|
+
* @param position - point in normalized space (-0.5 to 0.5)
|
|
135
|
+
* @param armLength - length of each bracket arm in UV units
|
|
136
|
+
* @param armWidth - stroke width of each bracket arm in UV units
|
|
137
|
+
* @returns signed distance to the nearest corner bracket
|
|
138
|
+
*/
|
|
139
|
+
float coffinCorner_allCorners(vec2 position, float armLength, float armWidth) {
|
|
140
|
+
const vec2 halfSize = vec2(0.5);
|
|
141
|
+
|
|
142
|
+
vec2 topLeft = position + halfSize;
|
|
143
|
+
vec2 topRight = vec2(halfSize.x - position.x, position.y + halfSize.y);
|
|
144
|
+
vec2 bottomLeft = vec2(position.x + halfSize.x, halfSize.y - position.y);
|
|
145
|
+
vec2 bottomRight = halfSize - position;
|
|
146
|
+
|
|
147
|
+
return min(
|
|
148
|
+
min(coffinCorner_signedDistBracketArm(topLeft, armLength, armWidth),
|
|
149
|
+
coffinCorner_signedDistBracketArm(topRight, armLength, armWidth)),
|
|
150
|
+
min(coffinCorner_signedDistBracketArm(bottomLeft, armLength, armWidth),
|
|
151
|
+
coffinCorner_signedDistBracketArm(bottomRight, armLength, armWidth))
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Composite bracket visuals over a base color.
|
|
157
|
+
* Applies: hover fill → base color → black stroke → colored bracket fill.
|
|
158
|
+
* Returns the composited color — caller is responsible for writing to fragColor.
|
|
159
|
+
*
|
|
160
|
+
* @param baseColor - the layer's rendered color for this pixel (icon texture or circle fill)
|
|
161
|
+
* @param isHovered - whether the entity is hovered
|
|
162
|
+
* @param isSelected - whether the entity is selected
|
|
163
|
+
* @param strokeAlpha - alpha for the dilated black outline
|
|
164
|
+
* @param fillAlpha - alpha for the bracket fill shape
|
|
165
|
+
* @returns composited RGBA color
|
|
166
|
+
*/
|
|
167
|
+
vec4 coffinCorner_composite(
|
|
168
|
+
vec4 baseColor,
|
|
169
|
+
bool isHovered,
|
|
170
|
+
bool isSelected,
|
|
171
|
+
float strokeAlpha,
|
|
172
|
+
float fillAlpha
|
|
173
|
+
) {
|
|
174
|
+
// Start with background fill (only when hovering) — white 30% opacity
|
|
175
|
+
vec4 result = isHovered
|
|
176
|
+
? vec4(1.0, 1.0, 1.0, 0.3)
|
|
177
|
+
: vec4(0.0); // no fill when just selected
|
|
178
|
+
|
|
179
|
+
// Composite base color OVER fill
|
|
180
|
+
result.rgb = baseColor.rgb * baseColor.a + result.rgb * result.a * (1.0 - baseColor.a);
|
|
181
|
+
result.a = baseColor.a + result.a * (1.0 - baseColor.a);
|
|
182
|
+
|
|
183
|
+
// Composite black stroke OVER base (dilated shape)
|
|
184
|
+
if (strokeAlpha > 0.01) {
|
|
185
|
+
vec3 strokeColor = vec3(0.0); // Black
|
|
186
|
+
result.rgb = strokeColor * strokeAlpha + result.rgb * (1.0 - strokeAlpha);
|
|
187
|
+
result.a = strokeAlpha + result.a * (1.0 - strokeAlpha);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Composite colored fill OVER stroke (normal shape)
|
|
191
|
+
if (fillAlpha > 0.01) {
|
|
192
|
+
vec4 cornerColor = isSelected
|
|
193
|
+
? coffinCorner.highlightColor
|
|
194
|
+
: vec4(1.0); // White fully opaque for hover-only
|
|
195
|
+
float blendedAlpha = fillAlpha * cornerColor.a;
|
|
196
|
+
result.rgb = cornerColor.rgb * blendedAlpha + result.rgb * (1.0 - blendedAlpha);
|
|
197
|
+
result.a = blendedAlpha + result.a * (1.0 - blendedAlpha);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return result;
|
|
201
|
+
}
|
|
202
|
+
`;
|
|
203
|
+
/**
|
|
204
|
+
* IconLayer `fs:#main-start` — samples `iconsTexture` to get the base icon color,
|
|
205
|
+
* then composites brackets over it. References IconLayer-specific uniforms/varyings:
|
|
206
|
+
* `iconsTexture`, `vTextureCoords`, `uv`.
|
|
207
|
+
*/
|
|
208
|
+
const ICON_FS_MAIN_START = `\
|
|
209
|
+
geometry.uv = uv; // uv = texture coordinate on the icon quad (ranges -1 to 1, center is 0,0)
|
|
210
|
+
bool isHovered = vInstanceHoveredEntity > 0.5;
|
|
211
|
+
bool isSelected = vInstanceSelectedEntity > 0.5;
|
|
212
|
+
|
|
213
|
+
if (isHovered || isSelected) {
|
|
214
|
+
vec2 bracketPos = uv * 0.5; // map -1..1 to -0.5..0.5
|
|
215
|
+
|
|
216
|
+
// Check if inside the icon quad (the rectangular surface the icon texture is drawn on)
|
|
217
|
+
bool insideBox = max(abs(bracketPos.x), abs(bracketPos.y)) < 0.5;
|
|
218
|
+
|
|
219
|
+
// Distance from this pixel to the nearest bracket edge — drives stroke and fill alpha
|
|
220
|
+
float cornerDist = coffinCorner_allCorners(bracketPos, 0.26, 0.07);
|
|
221
|
+
|
|
222
|
+
// Outline width (proportional to icon)
|
|
223
|
+
float strokeWidth = 0.026;
|
|
224
|
+
// Anti-alias band: ~1 screen pixel regardless of icon size
|
|
225
|
+
float antiAlias = fwidth(cornerDist);
|
|
226
|
+
|
|
227
|
+
// strokeAlpha: expanded (dilated) shape for black border
|
|
228
|
+
// fillAlpha: true SDF edge for the bracket fill color
|
|
229
|
+
float strokeAlpha = 1.0 - smoothstep(0.0, antiAlias, cornerDist + strokeWidth);
|
|
230
|
+
float fillAlpha = 1.0 - smoothstep(0.0, antiAlias, cornerDist);
|
|
231
|
+
|
|
232
|
+
if (insideBox) {
|
|
233
|
+
// Sample icon texture (iconsTexture and vTextureCoords are provided by IconLayer's shader)
|
|
234
|
+
vec4 baseColor = texture(iconsTexture, vTextureCoords);
|
|
235
|
+
|
|
236
|
+
fragColor = coffinCorner_composite(baseColor, isHovered, isSelected, strokeAlpha, fillAlpha);
|
|
237
|
+
DECKGL_FILTER_COLOR(fragColor, geometry);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
`;
|
|
242
|
+
/**
|
|
243
|
+
* ScatterplotLayer fragment declarations: adds `vQuadScale` varying
|
|
244
|
+
* (received from the vertex shader) to the shared SDF function declarations.
|
|
245
|
+
*/
|
|
246
|
+
const SCATTERPLOT_FS_DECL = `\
|
|
247
|
+
in float vQuadScale;
|
|
248
|
+
${FS_DECL}`;
|
|
249
|
+
/**
|
|
250
|
+
* ScatterplotLayer `fs:#main-start` — replicates the circle rendering logic to get
|
|
251
|
+
* the base fill/stroke color, then composites brackets over it.
|
|
252
|
+
*
|
|
253
|
+
* Uses two coordinate systems:
|
|
254
|
+
* - **Original** (`originalUnit`): `unitPosition / vQuadScale` — maps back to
|
|
255
|
+
* the pre-expansion circle space (±1) for circle fill/stroke rendering.
|
|
256
|
+
* - **Expanded** (`bracketPos`): `unitPosition / (2 * vQuadScale)` — maps the
|
|
257
|
+
* expanded quad to -0.5..0.5 for the bracket SDF.
|
|
258
|
+
*
|
|
259
|
+
* The vertex shader expands the quad by `vQuadScale` (2×) when hovered/selected,
|
|
260
|
+
* giving the brackets room to render beyond the circle edge while keeping the circle
|
|
261
|
+
* at its original visual size.
|
|
262
|
+
*
|
|
263
|
+
* References ScatterplotLayer-specific varyings/uniforms:
|
|
264
|
+
* `unitPosition`, `outerRadiusPixels`, `innerUnitRadius`, `vFillColor`, `vLineColor`,
|
|
265
|
+
* `scatterplot.antialiasing`, `scatterplot.stroked`, `scatterplot.filled`.
|
|
266
|
+
*/
|
|
267
|
+
const SCATTERPLOT_FS_MAIN_START = `\
|
|
268
|
+
geometry.uv = unitPosition;
|
|
269
|
+
bool isHovered = vInstanceHoveredEntity > 0.5;
|
|
270
|
+
bool isSelected = vInstanceSelectedEntity > 0.5;
|
|
271
|
+
|
|
272
|
+
if (isHovered || isSelected) {
|
|
273
|
+
// Bracket SDF coordinates: map the expanded quad to -0.5..0.5.
|
|
274
|
+
// Since both vertex positions and unitPosition were scaled by vQuadScale,
|
|
275
|
+
// the quad edge is at unitPosition ≈ ±(edgePadding * vQuadScale).
|
|
276
|
+
// Dividing by (2 * vQuadScale) maps ±vQuadScale to ±0.5.
|
|
277
|
+
// The circle edge (unitPosition = 1.0) maps to 1/(2*scale) ≈ 0.25 (for scale=2).
|
|
278
|
+
vec2 bracketPos = unitPosition / (2.0 * vQuadScale);
|
|
279
|
+
|
|
280
|
+
bool insideBox = max(abs(bracketPos.x), abs(bracketPos.y)) < 0.5;
|
|
281
|
+
|
|
282
|
+
// Arm proportions scaled for the expanded quad: the circle fills ~50% of the
|
|
283
|
+
// bracket box (circle edge at 0.25, bracket corner at 0.5). Shorter arms (0.15)
|
|
284
|
+
// keep L-shapes distinct instead of merging into a solid square.
|
|
285
|
+
float uvPerPx = fwidth(bracketPos.x);
|
|
286
|
+
float armLength = max(0.15, 6.0 * uvPerPx); // min 6px arm length
|
|
287
|
+
float armWidth = max(0.04, 2.0 * uvPerPx); // min 2px stroke width
|
|
288
|
+
|
|
289
|
+
float cornerDist = coffinCorner_allCorners(bracketPos, armLength, armWidth);
|
|
290
|
+
float strokeWidth = max(0.016, 1.0 * uvPerPx); // min 1px outline
|
|
291
|
+
float antiAlias = fwidth(cornerDist);
|
|
292
|
+
float strokeAlpha = 1.0 - smoothstep(0.0, antiAlias, cornerDist + strokeWidth);
|
|
293
|
+
float fillAlpha = 1.0 - smoothstep(0.0, antiAlias, cornerDist);
|
|
294
|
+
|
|
295
|
+
if (insideBox) {
|
|
296
|
+
// Circle rendering uses unitPosition directly (NOT divided by vQuadScale).
|
|
297
|
+
// Because both the vertex positions and unitPosition were scaled equally in
|
|
298
|
+
// the vertex shader, the interpolated unitPosition at the circle-edge screen
|
|
299
|
+
// position is preserved at 1.0 — identical to the original shader's math.
|
|
300
|
+
float distToCenter = length(unitPosition) * outerRadiusPixels;
|
|
301
|
+
float inCircle = scatterplot.antialiasing
|
|
302
|
+
? smoothedge(distToCenter, outerRadiusPixels)
|
|
303
|
+
: step(distToCenter, outerRadiusPixels);
|
|
304
|
+
|
|
305
|
+
vec4 baseColor = vec4(0.0);
|
|
306
|
+
if (inCircle > 0.0) {
|
|
307
|
+
if (scatterplot.stroked > 0.5) {
|
|
308
|
+
float isLine = scatterplot.antialiasing
|
|
309
|
+
? smoothedge(innerUnitRadius * outerRadiusPixels, distToCenter)
|
|
310
|
+
: step(innerUnitRadius * outerRadiusPixels, distToCenter);
|
|
311
|
+
if (scatterplot.filled > 0.5) {
|
|
312
|
+
baseColor = mix(vFillColor, vLineColor, isLine);
|
|
313
|
+
} else {
|
|
314
|
+
baseColor = vec4(vLineColor.rgb, vLineColor.a * isLine);
|
|
315
|
+
}
|
|
316
|
+
} else if (scatterplot.filled > 0.5) {
|
|
317
|
+
baseColor = vFillColor;
|
|
318
|
+
}
|
|
319
|
+
baseColor.a *= inCircle;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
fragColor = coffinCorner_composite(baseColor, isHovered, isSelected, strokeAlpha, fillAlpha);
|
|
323
|
+
DECKGL_FILTER_COLOR(fragColor, geometry);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
`;
|
|
328
|
+
/**
|
|
329
|
+
* Shader injection config for IconLayer.
|
|
330
|
+
*
|
|
331
|
+
* deck.gl inject keys follow the pattern `<stage>:<hook>`:
|
|
332
|
+
* - `vs:#decl` — vertex shader, top-level declarations (before main)
|
|
333
|
+
* - `vs:#main-end` — vertex shader, end of main()
|
|
334
|
+
* - `fs:#decl` — fragment shader, top-level declarations (before main)
|
|
335
|
+
* - `fs:#main-start` — fragment shader, start of main()
|
|
336
|
+
*/
|
|
337
|
+
const ICON_SHADERS = {
|
|
338
|
+
modules: [coffinCornerModule],
|
|
339
|
+
inject: {
|
|
340
|
+
"vs:#decl": VS_DECL,
|
|
341
|
+
"vs:#main-end": VS_MAIN_END,
|
|
342
|
+
"fs:#decl": FS_DECL,
|
|
343
|
+
"fs:#main-start": ICON_FS_MAIN_START
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
/**
|
|
347
|
+
* Shader injection config for ScatterplotLayer.
|
|
348
|
+
* Uses scatterplot-specific vertex injections for quad expansion and
|
|
349
|
+
* scatterplot-specific fragment declarations for the `vQuadScale` varying.
|
|
350
|
+
*/
|
|
351
|
+
const SCATTERPLOT_SHADERS = {
|
|
352
|
+
modules: [coffinCornerModule],
|
|
353
|
+
inject: {
|
|
354
|
+
"vs:#decl": SCATTERPLOT_VS_DECL,
|
|
355
|
+
"vs:#main-end": SCATTERPLOT_VS_MAIN_END,
|
|
356
|
+
"fs:#decl": SCATTERPLOT_FS_DECL,
|
|
357
|
+
"fs:#main-start": SCATTERPLOT_FS_MAIN_START
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
/** Default bracket fill color when selected: #39B7FA fully opaque. */
|
|
361
|
+
const DEFAULT_SELECTED_CORNER_FILL = [
|
|
362
|
+
57,
|
|
363
|
+
183,
|
|
364
|
+
250,
|
|
365
|
+
255
|
|
366
|
+
];
|
|
367
|
+
/** Layer types supported by this extension. */
|
|
368
|
+
const SUPPORTED_LAYERS = [IconLayer, ScatterplotLayer];
|
|
369
|
+
/**
|
|
370
|
+
* deck.gl layer extension that renders bracket-like "coffin corner" indicators
|
|
371
|
+
* around hovered and selected map entities.
|
|
372
|
+
*
|
|
373
|
+
* Driven by explicit `hoveredEntityId` and `selectedEntityId` props rather than
|
|
374
|
+
* deck.gl's built-in autoHighlight. Data objects are identified via the
|
|
375
|
+
* `getEntityId` accessor (defaults to `item => item.id`).
|
|
376
|
+
*
|
|
377
|
+
* **Supported layer types:**
|
|
378
|
+
* - **IconLayer** (and subclasses like SymbolLayer) — samples `iconsTexture`
|
|
379
|
+
* to re-composite the icon beneath the brackets.
|
|
380
|
+
* - **ScatterplotLayer** — replicates the circle fill/stroke logic to composite
|
|
381
|
+
* the circle beneath the brackets.
|
|
382
|
+
*
|
|
383
|
+
* Using this extension on unsupported layer types will produce shader compilation
|
|
384
|
+
* errors due to missing layer-specific uniforms/varyings.
|
|
385
|
+
*
|
|
386
|
+
* The host layer must set `pickable` to enable picking events.
|
|
387
|
+
*
|
|
388
|
+
* @see CoffinCornerExtensionProps for the full list of extension props.
|
|
389
|
+
*
|
|
390
|
+
* @example Fiber renderer JSX (IconLayer / SymbolLayer)
|
|
391
|
+
* ```tsx
|
|
392
|
+
* <symbolLayer
|
|
393
|
+
* {...props}
|
|
394
|
+
* pickable
|
|
395
|
+
* extensions={[new CoffinCornerExtension()]}
|
|
396
|
+
* selectedEntityId={selectedId}
|
|
397
|
+
* hoveredEntityId={hoveredId}
|
|
398
|
+
* />
|
|
399
|
+
* ```
|
|
400
|
+
*
|
|
401
|
+
* @example ScatterplotLayer
|
|
402
|
+
* ```typescript
|
|
403
|
+
* new ScatterplotLayer({
|
|
404
|
+
* extensions: [new CoffinCornerExtension()],
|
|
405
|
+
* getEntityId: (d) => d.id,
|
|
406
|
+
* selectedEntityId: selectedId,
|
|
407
|
+
* hoveredEntityId: hoveredId,
|
|
408
|
+
* })
|
|
409
|
+
* ```
|
|
410
|
+
*
|
|
411
|
+
* @example Custom entity ID accessor (e.g. GeoJSON features)
|
|
412
|
+
* ```typescript
|
|
413
|
+
* new IconLayer({
|
|
414
|
+
* extensions: [new CoffinCornerExtension()],
|
|
415
|
+
* getEntityId: (d) => d.properties?.shapeId,
|
|
416
|
+
* selectedEntityId: selectedShapeId,
|
|
417
|
+
* hoveredEntityId: hoveredShapeId,
|
|
418
|
+
* selectedCoffinCornerColor: [255, 0, 0, 255],
|
|
419
|
+
* })
|
|
420
|
+
* ```
|
|
421
|
+
*/
|
|
422
|
+
var CoffinCornerExtension = class CoffinCornerExtension extends LayerExtension {
|
|
423
|
+
static componentName = "CoffinCornerExtension";
|
|
424
|
+
static defaultProps = {
|
|
425
|
+
selectedEntityId: {
|
|
426
|
+
type: "value",
|
|
427
|
+
value: void 0
|
|
428
|
+
},
|
|
429
|
+
hoveredEntityId: {
|
|
430
|
+
type: "value",
|
|
431
|
+
value: void 0
|
|
432
|
+
},
|
|
433
|
+
selectedCoffinCornerColor: {
|
|
434
|
+
type: "color",
|
|
435
|
+
value: DEFAULT_SELECTED_CORNER_FILL
|
|
436
|
+
},
|
|
437
|
+
getEntityId: {
|
|
438
|
+
type: "accessor",
|
|
439
|
+
value: (item) => item.id
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
/** Returns true if the host layer is a supported type. */
|
|
443
|
+
static isSupportedLayer(layer) {
|
|
444
|
+
return SUPPORTED_LAYERS.some((LayerType) => layer instanceof LayerType);
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Initializes selection and hover entity state maps and registers
|
|
448
|
+
* `instanceSelectedEntity` / `instanceHoveredEntity` GPU attributes.
|
|
449
|
+
* No-op on unsupported layer types (e.g. PathLayer, SolidPolygonLayer).
|
|
450
|
+
*/
|
|
451
|
+
initializeState() {
|
|
452
|
+
if (!CoffinCornerExtension.isSupportedLayer(this)) {
|
|
453
|
+
logger.warn(`CoffinCornerExtension supports IconLayer and ScatterplotLayer (and subclasses). Received: ${this.constructor.layerName}`);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
this.state.selectedEntities = /* @__PURE__ */ new Map();
|
|
457
|
+
this.state.hoveredEntities = /* @__PURE__ */ new Map();
|
|
458
|
+
const attributeManager = this.getAttributeManager();
|
|
459
|
+
if (!attributeManager) return;
|
|
460
|
+
const makeUpdateCallback = (stateKey) => (attribute, { data }) => {
|
|
461
|
+
const entities = this.state[stateKey];
|
|
462
|
+
const getId = this.props.getEntityId ?? ((item) => item.id);
|
|
463
|
+
const items = data ?? [];
|
|
464
|
+
const value = attribute.value;
|
|
465
|
+
for (let i = 0; i < items.length; i++) value[i] = entities.get(getId(items[i])) ?? 0;
|
|
466
|
+
};
|
|
467
|
+
attributeManager.addInstanced({
|
|
468
|
+
instanceSelectedEntity: {
|
|
469
|
+
size: 1,
|
|
470
|
+
update: makeUpdateCallback("selectedEntities")
|
|
471
|
+
},
|
|
472
|
+
instanceHoveredEntity: {
|
|
473
|
+
size: 1,
|
|
474
|
+
update: makeUpdateCallback("hoveredEntities")
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Syncs `selectedEntityId` and `hoveredEntityId` prop changes into the
|
|
480
|
+
* entity state maps and invalidates the corresponding GPU attributes.
|
|
481
|
+
* No-op on unsupported layer types.
|
|
482
|
+
*
|
|
483
|
+
* @param params - The deck.gl update parameters containing current and previous props.
|
|
484
|
+
*/
|
|
485
|
+
updateState(params) {
|
|
486
|
+
if (!CoffinCornerExtension.isSupportedLayer(this)) return;
|
|
487
|
+
const attributeManager = this.getAttributeManager();
|
|
488
|
+
const newSelectedId = params.props.selectedEntityId;
|
|
489
|
+
const oldSelectedId = params.oldProps.selectedEntityId;
|
|
490
|
+
if (newSelectedId !== oldSelectedId) {
|
|
491
|
+
const { selectedEntities } = this.state;
|
|
492
|
+
if (oldSelectedId != null) selectedEntities.delete(oldSelectedId);
|
|
493
|
+
if (newSelectedId != null) selectedEntities.set(newSelectedId, 1);
|
|
494
|
+
attributeManager?.invalidate("instanceSelectedEntity");
|
|
495
|
+
}
|
|
496
|
+
const newHoveredId = params.props.hoveredEntityId;
|
|
497
|
+
const oldHoveredId = params.oldProps.hoveredEntityId;
|
|
498
|
+
if (newHoveredId !== oldHoveredId) {
|
|
499
|
+
const { hoveredEntities } = this.state;
|
|
500
|
+
if (oldHoveredId != null) hoveredEntities.delete(oldHoveredId);
|
|
501
|
+
if (newHoveredId != null) hoveredEntities.set(newHoveredId, 1);
|
|
502
|
+
attributeManager?.invalidate("instanceHoveredEntity");
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Pushes the normalized `selectedCoffinCornerColor` to the shader's `highlightColor` uniform
|
|
507
|
+
* each frame. No-op on unsupported layer types.
|
|
508
|
+
*/
|
|
509
|
+
draw() {
|
|
510
|
+
if (!CoffinCornerExtension.isSupportedLayer(this)) return;
|
|
511
|
+
const color = this.props.selectedCoffinCornerColor ?? DEFAULT_SELECTED_CORNER_FILL;
|
|
512
|
+
this.setShaderModuleProps({ coffinCorner: { highlightColor: [
|
|
513
|
+
color[0] / 255,
|
|
514
|
+
color[1] / 255,
|
|
515
|
+
color[2] / 255,
|
|
516
|
+
color[3] / 255
|
|
517
|
+
] } });
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* Returns the appropriate shader injection config based on the host layer type.
|
|
521
|
+
* IconLayer gets texture-sampling shaders; ScatterplotLayer gets circle-replicating shaders.
|
|
522
|
+
* Returns null for unsupported layer types to skip shader injection.
|
|
523
|
+
*
|
|
524
|
+
* @returns The vertex/fragment shader injection config and uniform module, or null.
|
|
525
|
+
*/
|
|
526
|
+
getShaders(_extensions) {
|
|
527
|
+
if (this instanceof ScatterplotLayer) return SCATTERPLOT_SHADERS;
|
|
528
|
+
if (this instanceof IconLayer) return ICON_SHADERS;
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
//#endregion
|
|
534
|
+
export { CoffinCornerExtension };
|
|
535
|
+
//# sourceMappingURL=coffin-corner-extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coffin-corner-extension.js","names":["coffinCornerModule: {\n name: string;\n fs: string;\n uniformTypes: Record<string, string>;\n}","DEFAULT_SELECTED_CORNER_FILL: Rgba255Tuple"],"sources":["../../../../src/deckgl/extensions/coffin-corner/coffin-corner-extension.ts"],"sourcesContent":["/*\n * Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport { LayerExtension } from '@deck.gl/core';\nimport { IconLayer, ScatterplotLayer } from '@deck.gl/layers';\nimport { createLoggerDomain } from '@/shared/logger';\nimport type { Rgba255Tuple } from '@accelint/predicates';\nimport type { Layer, UpdateParameters } from '@deck.gl/core';\nimport type { CoffinCornerExtensionProps, EntityId } from './types';\n\nconst logger = createLoggerDomain('[CoffinCornerExtension]');\n\n/** Layer shape with coffin-corner selection and hover state maps. */\ntype CoffinCornerLayer = Layer & {\n state: {\n selectedEntities: Map<EntityId, number>;\n hoveredEntities: Map<EntityId, number>;\n };\n};\n\n/**\n * Shader module defining the `highlightColor` uniform for coffin corner brackets.\n *\n * @property name - Module identifier used by deck.gl's shader assembly.\n * @property fs - Fragment shader (GLSL) source declaring the uniform block.\n * @property uniformTypes - Maps uniform names to WGSL-style type strings for deck.gl's uniform system.\n *\n * @remarks\n * GLSL type reference:\n * - `float` — single decimal number\n * - `vec2` — 2-component vector (x, y)\n * - `vec3` — 3-component vector (x, y, z) or (r, g, b)\n * - `vec4` — 4-component vector (x, y, z, w) or (r, g, b, a)\n * - `uniform` — read-only value passed from JS to the GPU each frame\n * - `in/out` — values passed between vertex and fragment shader stages\n */\nconst coffinCornerModule: {\n name: string;\n fs: string;\n uniformTypes: Record<string, string>;\n} = {\n name: 'coffinCorner',\n fs: /* glsl */ `\\\nuniform coffinCornerUniforms {\n vec4 highlightColor; // RGBA color, each channel 0.0–1.0\n} coffinCorner;\n`,\n uniformTypes: {\n highlightColor: 'vec4<f32>', // WGSL-style type: 4-component vector of 32-bit floats (deck.gl convention)\n },\n};\n\n// -- Shared shader injections (IconLayer and ScatterplotLayer) --\n\n/**\n * Vertex declarations: per-instance attributes for selection/hover state,\n * passed to the fragment shader as varyings.\n */\nconst VS_DECL = /* glsl */ `\\\nin float instanceSelectedEntity;\nin float instanceHoveredEntity;\nout float vInstanceSelectedEntity; // v prefix is conventional for \"varying\"\nout float vInstanceHoveredEntity;\n`;\n\n/** Vertex main-end: forward per-instance attributes to the fragment shader. */\nconst VS_MAIN_END = /* glsl */ `\\\nvInstanceSelectedEntity = instanceSelectedEntity;\nvInstanceHoveredEntity = instanceHoveredEntity;\n`;\n\n// -- ScatterplotLayer-specific vertex shader injections --\n\n/**\n * ScatterplotLayer vertex declarations: adds `vQuadScale` varying to\n * communicate the quad expansion factor to the fragment shader.\n */\nconst SCATTERPLOT_VS_DECL = /* glsl */ `\\\n${VS_DECL}out float vQuadScale;\n`;\n\n/**\n * ScatterplotLayer vertex main-end: expands the quad when hovered/selected\n * to provide room for coffin corner brackets beyond the circle edge.\n *\n * The expansion multiplies `unitPosition` by the scale factor so that\n * fragment shader interpolation covers the larger area. The extra pixel\n * offset is added to `gl_Position` in clip space.\n *\n * References ScatterplotLayer vertex-scope variables:\n * `edgePadding`, `positions`, `outerRadiusPixels` (all still in scope at #main-end).\n */\nconst SCATTERPLOT_VS_MAIN_END = /* glsl */ `\\\n${VS_MAIN_END}\n// Expand the quad when hovered/selected to make room for bracket arms.\n// Scale factor 2.0 gives brackets the same visual footprint as the circle diameter.\n// Skip expansion in globe mode — clip-space XY manipulation causes depth conflicts\n// with the globe surface (known deck.gl limitation, see PR #9975).\nvQuadScale = 1.0;\nif ((instanceSelectedEntity > 0.5 || instanceHoveredEntity > 0.5)\n && project.projectionMode != PROJECTION_MODE_GLOBE) {\n vQuadScale = 2.0;\n // Add extra offset in clip space (works for both billboard and non-billboard)\n vec2 extraPixelOffset = (vQuadScale - 1.0) * edgePadding * positions.xy * outerRadiusPixels;\n gl_Position.xy += project_pixel_size_to_clipspace(extraPixelOffset);\n // Scale unitPosition so fragment interpolation covers the expanded quad\n unitPosition *= vQuadScale;\n}\n`;\n\n/**\n * Fragment declarations: SDF functions for bracket rendering.\n *\n * Signed distance functions (SDF) return negative inside the shape,\n * zero on the edge, and positive outside. Used for anti-aliased rendering.\n */\nconst FS_DECL = /* glsl */ `\\\nin float vInstanceSelectedEntity;\nin float vInstanceHoveredEntity;\n\n/**\n * Signed distance to an axis-aligned box.\n *\n * @param position - point to measure distance from\n * @param halfExtents - half-width and half-height of the box (distance from center to each edge)\n * @returns signed distance: negative inside, zero on edge, positive outside\n */\nfloat coffinCorner_signedDistBox(vec2 position, vec2 halfExtents) {\n vec2 dist = abs(position) - halfExtents;\n return length(max(dist, 0.0)) + min(max(dist.x, dist.y), 0.0);\n}\n\n/**\n * Signed distance to an L-shaped bracket arm (horizontal + vertical box union).\n *\n * @param position - point to measure distance from\n * @param length - length of each arm of the L-shape (fraction of icon size)\n * @param width - stroke width of each arm (fraction of icon size)\n * @returns signed distance to the nearest arm of the L-shape\n */\nfloat coffinCorner_signedDistBracketArm(vec2 position, float length, float width) {\n float horizontalDist = coffinCorner_signedDistBox(position - vec2(length * 0.5, 0.0), vec2(length * 0.5, width * 0.5));\n float verticalDist = coffinCorner_signedDistBox(position - vec2(0.0, length * 0.5), vec2(width * 0.5, length * 0.5));\n return min(horizontalDist, verticalDist);\n}\n\n/**\n * Minimum signed distance from a point to any of the four corner brackets.\n *\n * @param position - point in normalized space (-0.5 to 0.5)\n * @param armLength - length of each bracket arm in UV units\n * @param armWidth - stroke width of each bracket arm in UV units\n * @returns signed distance to the nearest corner bracket\n */\nfloat coffinCorner_allCorners(vec2 position, float armLength, float armWidth) {\n const vec2 halfSize = vec2(0.5);\n\n vec2 topLeft = position + halfSize;\n vec2 topRight = vec2(halfSize.x - position.x, position.y + halfSize.y);\n vec2 bottomLeft = vec2(position.x + halfSize.x, halfSize.y - position.y);\n vec2 bottomRight = halfSize - position;\n\n return min(\n min(coffinCorner_signedDistBracketArm(topLeft, armLength, armWidth),\n coffinCorner_signedDistBracketArm(topRight, armLength, armWidth)),\n min(coffinCorner_signedDistBracketArm(bottomLeft, armLength, armWidth),\n coffinCorner_signedDistBracketArm(bottomRight, armLength, armWidth))\n );\n}\n\n/**\n * Composite bracket visuals over a base color.\n * Applies: hover fill → base color → black stroke → colored bracket fill.\n * Returns the composited color — caller is responsible for writing to fragColor.\n *\n * @param baseColor - the layer's rendered color for this pixel (icon texture or circle fill)\n * @param isHovered - whether the entity is hovered\n * @param isSelected - whether the entity is selected\n * @param strokeAlpha - alpha for the dilated black outline\n * @param fillAlpha - alpha for the bracket fill shape\n * @returns composited RGBA color\n */\nvec4 coffinCorner_composite(\n vec4 baseColor,\n bool isHovered,\n bool isSelected,\n float strokeAlpha,\n float fillAlpha\n) {\n // Start with background fill (only when hovering) — white 30% opacity\n vec4 result = isHovered\n ? vec4(1.0, 1.0, 1.0, 0.3)\n : vec4(0.0); // no fill when just selected\n\n // Composite base color OVER fill\n result.rgb = baseColor.rgb * baseColor.a + result.rgb * result.a * (1.0 - baseColor.a);\n result.a = baseColor.a + result.a * (1.0 - baseColor.a);\n\n // Composite black stroke OVER base (dilated shape)\n if (strokeAlpha > 0.01) {\n vec3 strokeColor = vec3(0.0); // Black\n result.rgb = strokeColor * strokeAlpha + result.rgb * (1.0 - strokeAlpha);\n result.a = strokeAlpha + result.a * (1.0 - strokeAlpha);\n }\n\n // Composite colored fill OVER stroke (normal shape)\n if (fillAlpha > 0.01) {\n vec4 cornerColor = isSelected\n ? coffinCorner.highlightColor\n : vec4(1.0); // White fully opaque for hover-only\n float blendedAlpha = fillAlpha * cornerColor.a;\n result.rgb = cornerColor.rgb * blendedAlpha + result.rgb * (1.0 - blendedAlpha);\n result.a = blendedAlpha + result.a * (1.0 - blendedAlpha);\n }\n\n return result;\n}\n`;\n\n// -- Layer-specific fragment shader main-start injections --\n\n/**\n * IconLayer `fs:#main-start` — samples `iconsTexture` to get the base icon color,\n * then composites brackets over it. References IconLayer-specific uniforms/varyings:\n * `iconsTexture`, `vTextureCoords`, `uv`.\n */\nconst ICON_FS_MAIN_START = /* glsl */ `\\\n geometry.uv = uv; // uv = texture coordinate on the icon quad (ranges -1 to 1, center is 0,0)\n bool isHovered = vInstanceHoveredEntity > 0.5;\n bool isSelected = vInstanceSelectedEntity > 0.5;\n\n if (isHovered || isSelected) {\n vec2 bracketPos = uv * 0.5; // map -1..1 to -0.5..0.5\n\n // Check if inside the icon quad (the rectangular surface the icon texture is drawn on)\n bool insideBox = max(abs(bracketPos.x), abs(bracketPos.y)) < 0.5;\n\n // Distance from this pixel to the nearest bracket edge — drives stroke and fill alpha\n float cornerDist = coffinCorner_allCorners(bracketPos, 0.26, 0.07);\n\n // Outline width (proportional to icon)\n float strokeWidth = 0.026;\n // Anti-alias band: ~1 screen pixel regardless of icon size\n float antiAlias = fwidth(cornerDist);\n\n // strokeAlpha: expanded (dilated) shape for black border\n // fillAlpha: true SDF edge for the bracket fill color\n float strokeAlpha = 1.0 - smoothstep(0.0, antiAlias, cornerDist + strokeWidth);\n float fillAlpha = 1.0 - smoothstep(0.0, antiAlias, cornerDist);\n\n if (insideBox) {\n // Sample icon texture (iconsTexture and vTextureCoords are provided by IconLayer's shader)\n vec4 baseColor = texture(iconsTexture, vTextureCoords);\n\n fragColor = coffinCorner_composite(baseColor, isHovered, isSelected, strokeAlpha, fillAlpha);\n DECKGL_FILTER_COLOR(fragColor, geometry);\n return;\n }\n }\n`;\n\n/**\n * ScatterplotLayer fragment declarations: adds `vQuadScale` varying\n * (received from the vertex shader) to the shared SDF function declarations.\n */\nconst SCATTERPLOT_FS_DECL = /* glsl */ `\\\nin float vQuadScale;\n${FS_DECL}`;\n\n/**\n * ScatterplotLayer `fs:#main-start` — replicates the circle rendering logic to get\n * the base fill/stroke color, then composites brackets over it.\n *\n * Uses two coordinate systems:\n * - **Original** (`originalUnit`): `unitPosition / vQuadScale` — maps back to\n * the pre-expansion circle space (±1) for circle fill/stroke rendering.\n * - **Expanded** (`bracketPos`): `unitPosition / (2 * vQuadScale)` — maps the\n * expanded quad to -0.5..0.5 for the bracket SDF.\n *\n * The vertex shader expands the quad by `vQuadScale` (2×) when hovered/selected,\n * giving the brackets room to render beyond the circle edge while keeping the circle\n * at its original visual size.\n *\n * References ScatterplotLayer-specific varyings/uniforms:\n * `unitPosition`, `outerRadiusPixels`, `innerUnitRadius`, `vFillColor`, `vLineColor`,\n * `scatterplot.antialiasing`, `scatterplot.stroked`, `scatterplot.filled`.\n */\nconst SCATTERPLOT_FS_MAIN_START = /* glsl */ `\\\n geometry.uv = unitPosition;\n bool isHovered = vInstanceHoveredEntity > 0.5;\n bool isSelected = vInstanceSelectedEntity > 0.5;\n\n if (isHovered || isSelected) {\n // Bracket SDF coordinates: map the expanded quad to -0.5..0.5.\n // Since both vertex positions and unitPosition were scaled by vQuadScale,\n // the quad edge is at unitPosition ≈ ±(edgePadding * vQuadScale).\n // Dividing by (2 * vQuadScale) maps ±vQuadScale to ±0.5.\n // The circle edge (unitPosition = 1.0) maps to 1/(2*scale) ≈ 0.25 (for scale=2).\n vec2 bracketPos = unitPosition / (2.0 * vQuadScale);\n\n bool insideBox = max(abs(bracketPos.x), abs(bracketPos.y)) < 0.5;\n\n // Arm proportions scaled for the expanded quad: the circle fills ~50% of the\n // bracket box (circle edge at 0.25, bracket corner at 0.5). Shorter arms (0.15)\n // keep L-shapes distinct instead of merging into a solid square.\n float uvPerPx = fwidth(bracketPos.x);\n float armLength = max(0.15, 6.0 * uvPerPx); // min 6px arm length\n float armWidth = max(0.04, 2.0 * uvPerPx); // min 2px stroke width\n\n float cornerDist = coffinCorner_allCorners(bracketPos, armLength, armWidth);\n float strokeWidth = max(0.016, 1.0 * uvPerPx); // min 1px outline\n float antiAlias = fwidth(cornerDist);\n float strokeAlpha = 1.0 - smoothstep(0.0, antiAlias, cornerDist + strokeWidth);\n float fillAlpha = 1.0 - smoothstep(0.0, antiAlias, cornerDist);\n\n if (insideBox) {\n // Circle rendering uses unitPosition directly (NOT divided by vQuadScale).\n // Because both the vertex positions and unitPosition were scaled equally in\n // the vertex shader, the interpolated unitPosition at the circle-edge screen\n // position is preserved at 1.0 — identical to the original shader's math.\n float distToCenter = length(unitPosition) * outerRadiusPixels;\n float inCircle = scatterplot.antialiasing\n ? smoothedge(distToCenter, outerRadiusPixels)\n : step(distToCenter, outerRadiusPixels);\n\n vec4 baseColor = vec4(0.0);\n if (inCircle > 0.0) {\n if (scatterplot.stroked > 0.5) {\n float isLine = scatterplot.antialiasing\n ? smoothedge(innerUnitRadius * outerRadiusPixels, distToCenter)\n : step(innerUnitRadius * outerRadiusPixels, distToCenter);\n if (scatterplot.filled > 0.5) {\n baseColor = mix(vFillColor, vLineColor, isLine);\n } else {\n baseColor = vec4(vLineColor.rgb, vLineColor.a * isLine);\n }\n } else if (scatterplot.filled > 0.5) {\n baseColor = vFillColor;\n }\n baseColor.a *= inCircle;\n }\n\n fragColor = coffinCorner_composite(baseColor, isHovered, isSelected, strokeAlpha, fillAlpha);\n DECKGL_FILTER_COLOR(fragColor, geometry);\n return;\n }\n }\n`;\n\n// -- Shader configs --\n\n/**\n * Shader injection config for IconLayer.\n *\n * deck.gl inject keys follow the pattern `<stage>:<hook>`:\n * - `vs:#decl` — vertex shader, top-level declarations (before main)\n * - `vs:#main-end` — vertex shader, end of main()\n * - `fs:#decl` — fragment shader, top-level declarations (before main)\n * - `fs:#main-start` — fragment shader, start of main()\n */\nconst ICON_SHADERS = {\n modules: [coffinCornerModule],\n inject: {\n 'vs:#decl': VS_DECL,\n 'vs:#main-end': VS_MAIN_END,\n 'fs:#decl': FS_DECL,\n 'fs:#main-start': ICON_FS_MAIN_START,\n },\n};\n\n/**\n * Shader injection config for ScatterplotLayer.\n * Uses scatterplot-specific vertex injections for quad expansion and\n * scatterplot-specific fragment declarations for the `vQuadScale` varying.\n */\nconst SCATTERPLOT_SHADERS = {\n modules: [coffinCornerModule],\n inject: {\n 'vs:#decl': SCATTERPLOT_VS_DECL,\n 'vs:#main-end': SCATTERPLOT_VS_MAIN_END,\n 'fs:#decl': SCATTERPLOT_FS_DECL,\n 'fs:#main-start': SCATTERPLOT_FS_MAIN_START,\n },\n};\n\n/** Default bracket fill color when selected: #39B7FA fully opaque. */\nconst DEFAULT_SELECTED_CORNER_FILL: Rgba255Tuple = [57, 183, 250, 255];\n\n/** Layer types supported by this extension. */\nconst SUPPORTED_LAYERS = [IconLayer, ScatterplotLayer];\n\n// -- Extension class --\n\n/**\n * deck.gl layer extension that renders bracket-like \"coffin corner\" indicators\n * around hovered and selected map entities.\n *\n * Driven by explicit `hoveredEntityId` and `selectedEntityId` props rather than\n * deck.gl's built-in autoHighlight. Data objects are identified via the\n * `getEntityId` accessor (defaults to `item => item.id`).\n *\n * **Supported layer types:**\n * - **IconLayer** (and subclasses like SymbolLayer) — samples `iconsTexture`\n * to re-composite the icon beneath the brackets.\n * - **ScatterplotLayer** — replicates the circle fill/stroke logic to composite\n * the circle beneath the brackets.\n *\n * Using this extension on unsupported layer types will produce shader compilation\n * errors due to missing layer-specific uniforms/varyings.\n *\n * The host layer must set `pickable` to enable picking events.\n *\n * @see CoffinCornerExtensionProps for the full list of extension props.\n *\n * @example Fiber renderer JSX (IconLayer / SymbolLayer)\n * ```tsx\n * <symbolLayer\n * {...props}\n * pickable\n * extensions={[new CoffinCornerExtension()]}\n * selectedEntityId={selectedId}\n * hoveredEntityId={hoveredId}\n * />\n * ```\n *\n * @example ScatterplotLayer\n * ```typescript\n * new ScatterplotLayer({\n * extensions: [new CoffinCornerExtension()],\n * getEntityId: (d) => d.id,\n * selectedEntityId: selectedId,\n * hoveredEntityId: hoveredId,\n * })\n * ```\n *\n * @example Custom entity ID accessor (e.g. GeoJSON features)\n * ```typescript\n * new IconLayer({\n * extensions: [new CoffinCornerExtension()],\n * getEntityId: (d) => d.properties?.shapeId,\n * selectedEntityId: selectedShapeId,\n * hoveredEntityId: hoveredShapeId,\n * selectedCoffinCornerColor: [255, 0, 0, 255],\n * })\n * ```\n */\nexport class CoffinCornerExtension extends LayerExtension {\n static override componentName = 'CoffinCornerExtension';\n\n static override defaultProps = {\n selectedEntityId: { type: 'value', value: undefined },\n hoveredEntityId: { type: 'value', value: undefined },\n selectedCoffinCornerColor: {\n type: 'color',\n value: DEFAULT_SELECTED_CORNER_FILL,\n },\n getEntityId: {\n type: 'accessor',\n value: (item: { id: EntityId }) => item.id,\n },\n };\n\n /** Returns true if the host layer is a supported type. */\n private static isSupportedLayer(layer: Layer): boolean {\n return SUPPORTED_LAYERS.some((LayerType) => layer instanceof LayerType);\n }\n\n /**\n * Initializes selection and hover entity state maps and registers\n * `instanceSelectedEntity` / `instanceHoveredEntity` GPU attributes.\n * No-op on unsupported layer types (e.g. PathLayer, SolidPolygonLayer).\n */\n override initializeState(this: CoffinCornerLayer) {\n if (!CoffinCornerExtension.isSupportedLayer(this)) {\n logger.warn(\n `CoffinCornerExtension supports IconLayer and ScatterplotLayer (and subclasses). Received: ${(this.constructor as typeof Layer).layerName}`,\n );\n return;\n }\n\n this.state.selectedEntities = new Map<EntityId, number>();\n this.state.hoveredEntities = new Map<EntityId, number>();\n\n const attributeManager = this.getAttributeManager();\n if (!attributeManager) {\n return;\n }\n\n const makeUpdateCallback =\n (stateKey: 'selectedEntities' | 'hoveredEntities') =>\n (\n attribute: { value: unknown },\n { data }: { data: unknown[] | undefined },\n ) => {\n const entities = this.state[stateKey];\n const getId =\n (this.props as unknown as CoffinCornerExtensionProps).getEntityId ??\n // biome-ignore lint/suspicious/noExplicitAny: Default accessor assumes item.id exists.\n ((item: any) => item.id as EntityId);\n const items = data ?? [];\n const value = attribute.value as Float32Array;\n\n for (let i = 0; i < items.length; i++) {\n value[i] = entities.get(getId(items[i])) ?? 0;\n }\n };\n\n attributeManager.addInstanced({\n instanceSelectedEntity: {\n size: 1,\n update: makeUpdateCallback('selectedEntities'),\n },\n instanceHoveredEntity: {\n size: 1,\n update: makeUpdateCallback('hoveredEntities'),\n },\n });\n }\n\n /**\n * Syncs `selectedEntityId` and `hoveredEntityId` prop changes into the\n * entity state maps and invalidates the corresponding GPU attributes.\n * No-op on unsupported layer types.\n *\n * @param params - The deck.gl update parameters containing current and previous props.\n */\n override updateState(\n this: CoffinCornerLayer,\n params: UpdateParameters<Layer<CoffinCornerExtensionProps>>,\n ) {\n if (!CoffinCornerExtension.isSupportedLayer(this)) {\n return;\n }\n\n const attributeManager = this.getAttributeManager();\n\n // Selection state\n const newSelectedId = params.props.selectedEntityId;\n const oldSelectedId = params.oldProps.selectedEntityId;\n if (newSelectedId !== oldSelectedId) {\n const { selectedEntities } = this.state;\n if (oldSelectedId != null) {\n selectedEntities.delete(oldSelectedId);\n }\n if (newSelectedId != null) {\n selectedEntities.set(newSelectedId, 1);\n }\n attributeManager?.invalidate('instanceSelectedEntity');\n }\n\n // Hover state\n const newHoveredId = params.props.hoveredEntityId;\n const oldHoveredId = params.oldProps.hoveredEntityId;\n if (newHoveredId !== oldHoveredId) {\n const { hoveredEntities } = this.state;\n if (oldHoveredId != null) {\n hoveredEntities.delete(oldHoveredId);\n }\n if (newHoveredId != null) {\n hoveredEntities.set(newHoveredId, 1);\n }\n attributeManager?.invalidate('instanceHoveredEntity');\n }\n }\n\n /**\n * Pushes the normalized `selectedCoffinCornerColor` to the shader's `highlightColor` uniform\n * each frame. No-op on unsupported layer types.\n */\n override draw(this: CoffinCornerLayer) {\n if (!CoffinCornerExtension.isSupportedLayer(this)) {\n return;\n }\n\n const color =\n (this.props as unknown as CoffinCornerExtensionProps)\n .selectedCoffinCornerColor ?? DEFAULT_SELECTED_CORNER_FILL;\n\n this.setShaderModuleProps({\n coffinCorner: {\n highlightColor: [\n color[0] / 255,\n color[1] / 255,\n color[2] / 255,\n color[3] / 255,\n ],\n },\n });\n }\n\n /**\n * Returns the appropriate shader injection config based on the host layer type.\n * IconLayer gets texture-sampling shaders; ScatterplotLayer gets circle-replicating shaders.\n * Returns null for unsupported layer types to skip shader injection.\n *\n * @returns The vertex/fragment shader injection config and uniform module, or null.\n */\n override getShaders(this: CoffinCornerLayer, _extensions: this) {\n if (this instanceof ScatterplotLayer) {\n return SCATTERPLOT_SHADERS;\n }\n if (this instanceof IconLayer) {\n return ICON_SHADERS;\n }\n return null;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { CoffinCornerDeselectedEvent, CoffinCornerEvent, CoffinCornerEventType, CoffinCornerEvents, CoffinCornerExtensionProps, CoffinCornerHoveredEvent, CoffinCornerSelectedEvent, EntityId } from "./types.js";
|
|
14
|
+
import { CoffinCornerExtension } from "./coffin-corner-extension.js";
|
|
15
|
+
import { clearSelection, coffinCornerStore, getHoveredEntityId, getSelectedEntityId } from "./store.js";
|
|
16
|
+
import { UseCoffinCornerOptions, UseCoffinCornerReturn, useCoffinCorner } from "./use-coffin-corner.js";
|
|
17
|
+
export { type CoffinCornerDeselectedEvent, type CoffinCornerEvent, type CoffinCornerEventType, CoffinCornerEvents, CoffinCornerExtension, type CoffinCornerExtensionProps, type CoffinCornerHoveredEvent, type CoffinCornerSelectedEvent, type EntityId, type UseCoffinCornerOptions, type UseCoffinCornerReturn, clearSelection, coffinCornerStore, getHoveredEntityId, getSelectedEntityId, useCoffinCorner };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 Hypergiant Galactic Systems Inc. All rights reserved.
|
|
3
|
+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
* you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
+
* of the License at https://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
+
*
|
|
7
|
+
* Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
+
* OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
+
* governing permissions and limitations under the License.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
import { CoffinCornerExtension } from "./coffin-corner-extension.js";
|
|
15
|
+
import { CoffinCornerEvents } from "./types.js";
|
|
16
|
+
import { clearSelection, coffinCornerStore, getHoveredEntityId, getSelectedEntityId } from "./store.js";
|
|
17
|
+
import { useCoffinCorner } from "./use-coffin-corner.js";
|
|
18
|
+
|
|
19
|
+
export { CoffinCornerEvents, CoffinCornerExtension, clearSelection, coffinCornerStore, getHoveredEntityId, getSelectedEntityId, useCoffinCorner };
|