@deck.gl-community/infovis-layers 9.3.2 → 9.3.7

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.
Files changed (129) hide show
  1. package/dist/index.cjs +2998 -114
  2. package/dist/index.cjs.map +4 -4
  3. package/dist/index.d.ts +11 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +9 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/layers/animation-layer/animation-layer.d.ts +30 -0
  8. package/dist/layers/animation-layer/animation-layer.d.ts.map +1 -0
  9. package/dist/layers/animation-layer/animation-layer.js +53 -0
  10. package/dist/layers/animation-layer/animation-layer.js.map +1 -0
  11. package/dist/layers/animation-layer/animation.d.ts +86 -0
  12. package/dist/layers/animation-layer/animation.d.ts.map +1 -0
  13. package/dist/layers/animation-layer/animation.js +214 -0
  14. package/dist/layers/animation-layer/animation.js.map +1 -0
  15. package/dist/layers/block-layer/block-layer-fragment.glsl.d.ts +3 -0
  16. package/dist/layers/block-layer/block-layer-fragment.glsl.d.ts.map +1 -0
  17. package/dist/layers/block-layer/block-layer-fragment.glsl.js +41 -0
  18. package/dist/layers/block-layer/block-layer-fragment.glsl.js.map +1 -0
  19. package/dist/layers/block-layer/block-layer-uniforms.d.ts +27 -0
  20. package/dist/layers/block-layer/block-layer-uniforms.d.ts.map +1 -0
  21. package/dist/layers/block-layer/block-layer-uniforms.js +26 -0
  22. package/dist/layers/block-layer/block-layer-uniforms.js.map +1 -0
  23. package/dist/layers/block-layer/block-layer-vertex.glsl.d.ts +3 -0
  24. package/dist/layers/block-layer/block-layer-vertex.glsl.d.ts.map +1 -0
  25. package/dist/layers/block-layer/block-layer-vertex.glsl.js +67 -0
  26. package/dist/layers/block-layer/block-layer-vertex.glsl.js.map +1 -0
  27. package/dist/layers/block-layer/block-layer.d.ts +71 -0
  28. package/dist/layers/block-layer/block-layer.d.ts.map +1 -0
  29. package/dist/layers/block-layer/block-layer.js +108 -0
  30. package/dist/layers/block-layer/block-layer.js.map +1 -0
  31. package/dist/layers/fast-text-layer/fast-text-layer.d.ts +148 -0
  32. package/dist/layers/fast-text-layer/fast-text-layer.d.ts.map +1 -0
  33. package/dist/layers/fast-text-layer/fast-text-layer.js +862 -0
  34. package/dist/layers/fast-text-layer/fast-text-layer.js.map +1 -0
  35. package/dist/layers/fast-text-layer/fast-text-layer.test.d.ts +2 -0
  36. package/dist/layers/fast-text-layer/fast-text-layer.test.d.ts.map +1 -0
  37. package/dist/layers/fast-text-layer/fast-text-layer.test.js +300 -0
  38. package/dist/layers/fast-text-layer/fast-text-layer.test.js.map +1 -0
  39. package/dist/layers/fast-text-layer/fast-text-layout.d.ts +179 -0
  40. package/dist/layers/fast-text-layer/fast-text-layout.d.ts.map +1 -0
  41. package/dist/layers/fast-text-layer/fast-text-layout.js +665 -0
  42. package/dist/layers/fast-text-layer/fast-text-layout.js.map +1 -0
  43. package/dist/layers/fast-text-layer/font-atlas.d.ts +98 -0
  44. package/dist/layers/fast-text-layer/font-atlas.d.ts.map +1 -0
  45. package/dist/layers/fast-text-layer/font-atlas.js +337 -0
  46. package/dist/layers/fast-text-layer/font-atlas.js.map +1 -0
  47. package/dist/layers/fast-text-layer/index.d.ts +4 -0
  48. package/dist/layers/fast-text-layer/index.d.ts.map +1 -0
  49. package/dist/layers/fast-text-layer/index.js +4 -0
  50. package/dist/layers/fast-text-layer/index.js.map +1 -0
  51. package/dist/layers/time-delta-layer.d.ts +22 -60
  52. package/dist/layers/time-delta-layer.d.ts.map +1 -1
  53. package/dist/layers/time-delta-layer.js +31 -53
  54. package/dist/layers/time-delta-layer.js.map +1 -1
  55. package/dist/synchronized-views/synchronized-views.js +1 -1
  56. package/dist/synchronized-views/synchronized-views.js.map +1 -1
  57. package/dist/utils/format-utils.d.ts +27 -3
  58. package/dist/utils/format-utils.d.ts.map +1 -1
  59. package/dist/utils/format-utils.js +72 -40
  60. package/dist/utils/format-utils.js.map +1 -1
  61. package/dist/utils/utf8-string-view.d.ts +53 -0
  62. package/dist/utils/utf8-string-view.d.ts.map +1 -0
  63. package/dist/utils/utf8-string-view.js +169 -0
  64. package/dist/utils/utf8-string-view.js.map +1 -0
  65. package/dist/utils/utf8-string-view.test.d.ts +2 -0
  66. package/dist/utils/utf8-string-view.test.d.ts.map +1 -0
  67. package/dist/utils/utf8-string-view.test.js +88 -0
  68. package/dist/utils/utf8-string-view.test.js.map +1 -0
  69. package/dist/views/bounds-utils.d.ts +23 -0
  70. package/dist/views/bounds-utils.d.ts.map +1 -0
  71. package/dist/views/bounds-utils.js +65 -0
  72. package/dist/views/bounds-utils.js.map +1 -0
  73. package/dist/views/layer-bounds-filter.d.ts +48 -0
  74. package/dist/views/layer-bounds-filter.d.ts.map +1 -0
  75. package/dist/views/layer-bounds-filter.js +99 -0
  76. package/dist/views/layer-bounds-filter.js.map +1 -0
  77. package/dist/views/layer-filter.js +4 -4
  78. package/dist/views/layer-filter.js.map +1 -1
  79. package/dist/views/orthographic-utils.d.ts +30 -2
  80. package/dist/views/orthographic-utils.d.ts.map +1 -1
  81. package/dist/views/orthographic-utils.js +33 -14
  82. package/dist/views/orthographic-utils.js.map +1 -1
  83. package/dist/views/view-layout/build-views-from-view-layout.d.ts +37 -0
  84. package/dist/views/view-layout/build-views-from-view-layout.d.ts.map +1 -0
  85. package/dist/views/view-layout/build-views-from-view-layout.js +413 -0
  86. package/dist/views/view-layout/build-views-from-view-layout.js.map +1 -0
  87. package/dist/views/view-layout/index.d.ts +3 -0
  88. package/dist/views/view-layout/index.d.ts.map +1 -0
  89. package/dist/views/view-layout/index.js +3 -0
  90. package/dist/views/view-layout/index.js.map +1 -0
  91. package/dist/views/view-layout/view-layout-item.d.ts +74 -0
  92. package/dist/views/view-layout/view-layout-item.d.ts.map +1 -0
  93. package/dist/views/view-layout/view-layout-item.js +48 -0
  94. package/dist/views/view-layout/view-layout-item.js.map +1 -0
  95. package/dist/views/view-layout/view-layout.test.d.ts +2 -0
  96. package/dist/views/view-layout/view-layout.test.d.ts.map +1 -0
  97. package/dist/views/view-layout/view-layout.test.js +112 -0
  98. package/dist/views/view-layout/view-layout.test.js.map +1 -0
  99. package/dist/views/viewport-bounds-utils.d.ts +55 -0
  100. package/dist/views/viewport-bounds-utils.d.ts.map +1 -0
  101. package/dist/views/viewport-bounds-utils.js +70 -0
  102. package/dist/views/viewport-bounds-utils.js.map +1 -0
  103. package/package.json +15 -5
  104. package/src/index.ts +91 -2
  105. package/src/layers/animation-layer/animation-layer.ts +92 -0
  106. package/src/layers/animation-layer/animation.ts +328 -0
  107. package/src/layers/block-layer/block-layer-fragment.glsl.ts +41 -0
  108. package/src/layers/block-layer/block-layer-uniforms.ts +43 -0
  109. package/src/layers/block-layer/block-layer-vertex.glsl.ts +67 -0
  110. package/src/layers/block-layer/block-layer.ts +199 -0
  111. package/src/layers/fast-text-layer/fast-text-layer.test.ts +359 -0
  112. package/src/layers/fast-text-layer/fast-text-layer.ts +1208 -0
  113. package/src/layers/fast-text-layer/fast-text-layout.ts +1124 -0
  114. package/src/layers/fast-text-layer/font-atlas.ts +574 -0
  115. package/src/layers/fast-text-layer/index.ts +37 -0
  116. package/src/layers/time-delta-layer.ts +55 -70
  117. package/src/synchronized-views/synchronized-views.ts +1 -1
  118. package/src/utils/format-utils.ts +90 -40
  119. package/src/utils/utf8-string-view.test.ts +119 -0
  120. package/src/utils/utf8-string-view.ts +273 -0
  121. package/src/views/bounds-utils.ts +88 -0
  122. package/src/views/layer-bounds-filter.ts +160 -0
  123. package/src/views/layer-filter.ts +4 -4
  124. package/src/views/orthographic-utils.ts +65 -18
  125. package/src/views/view-layout/build-views-from-view-layout.ts +547 -0
  126. package/src/views/view-layout/index.ts +18 -0
  127. package/src/views/view-layout/view-layout-item.ts +125 -0
  128. package/src/views/view-layout/view-layout.test.ts +129 -0
  129. package/src/views/viewport-bounds-utils.ts +104 -0
@@ -0,0 +1,328 @@
1
+ // deck.gl-community
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ /** A simple animation engine for deck.gl layers. */
6
+ import {type Layer} from '@deck.gl/core';
7
+
8
+ /** One timed set of layer prop changes in an animation. */
9
+ export type AnimationFrame<LayerT extends Layer> = {
10
+ /** Child layer prop values to transition to. */
11
+ props: Partial<LayerT['props']>;
12
+ /** Duration in milliseconds used to execute the transition. */
13
+ duration: number;
14
+ /** Pause in milliseconds before this frame starts. @defaultValue 0 */
15
+ delay?: number;
16
+ /** Easing function that defines the transition curve. @defaultValue linear */
17
+ easing?: (t: number) => number;
18
+ };
19
+
20
+ /** A schedulable group of animation frames. */
21
+ export type AnimationFramesGroup<LayerT extends Layer> = {
22
+ /**
23
+ * How to schedule the child frames.
24
+ * `sequence`: each frame starts after the previous frame ends.
25
+ * `concurrence`: all frames start together.
26
+ * `stagger`: frames start one-by-one at a fixed interval.
27
+ */
28
+ type: 'sequence' | 'concurrence' | 'stagger';
29
+ /**
30
+ * Optional delay in milliseconds.
31
+ * In `sequence` mode, delay refers to the wait after a step ends and before starting the next step.
32
+ * In `concurrence` mode, delay refers to the wait before starting all steps together.
33
+ * In `stagger` mode, delay refers to the interval between starting steps
34
+ */
35
+ delay?: number;
36
+
37
+ /** Frames or nested frame groups scheduled by this group. */
38
+ frames: (AnimationFramesGroup<LayerT> | AnimationFrame<LayerT>)[];
39
+ };
40
+
41
+ /** Repeat and schedule options used to resolve an animation timeline. */
42
+ export type AnimationExtensionOptions<LayerT extends Layer> = {
43
+ /** Frames scheduled by the animation. */
44
+ frames: AnimationFramesGroup<LayerT>;
45
+ /** Number of additional animation iterations after the first pass. @defaultValue 0 */
46
+ repeat?: number;
47
+ /** Whether repeated frames restart or alternate in reverse. @defaultValue 'loop' */
48
+ repeatType?: 'loop' | 'reverse';
49
+ /** Delay in milliseconds before each repeated iteration. @defaultValue 0 */
50
+ repeatDelay?: number;
51
+ };
52
+
53
+ type ResolvedAnimationFrame<LayerT extends Layer> = {
54
+ /** Assigned at start time */
55
+ from: Partial<LayerT['props']> | undefined;
56
+ /** Provided by user */
57
+ to: Partial<LayerT['props']>;
58
+ /** Calculated start time of this frame if the whole animation starts at 0 */
59
+ start: number;
60
+ /** Calculated end time of this frame if the whole animation starts at 0 */
61
+ end: number;
62
+ /** Easing function */
63
+ easing: (t: number) => number;
64
+ };
65
+
66
+ /** Runtime state for one resolved layer animation. */
67
+ export type AnimationState<LayerT extends Layer> = {
68
+ /** Timeline timestamp at which the current iteration started. */
69
+ start: number;
70
+ /** Timeline timestamp at which the current iteration ends. */
71
+ end: number;
72
+ /** Whether the end state has not yet been fully committed. */
73
+ inProgress: boolean;
74
+ /** Number of completed animation iterations. */
75
+ iterations: number;
76
+ /** Resolved frames executed during the current iteration. */
77
+ frames: ResolvedAnimationFrame<LayerT>[];
78
+ };
79
+
80
+ const LINEAR_EASING = (t: number) => t;
81
+
82
+ type ResolvedFramesResult<LayerT extends Layer> = {
83
+ frames: ResolvedAnimationFrame<LayerT>[];
84
+ duration: number;
85
+ };
86
+
87
+ function resolveFramesGroup<LayerT extends Layer = Layer>(
88
+ group: AnimationFramesGroup<LayerT> | AnimationFrame<LayerT>,
89
+ baseStart: number
90
+ ): ResolvedFramesResult<LayerT> {
91
+ if ('props' in group) {
92
+ const delay = group.delay ?? 0;
93
+ const start = baseStart + delay;
94
+ const end = start + group.duration;
95
+ return {
96
+ frames: [
97
+ {
98
+ from: undefined,
99
+ to: group.props,
100
+ start,
101
+ end,
102
+ easing: group.easing ?? LINEAR_EASING
103
+ }
104
+ ],
105
+ duration: delay + group.duration
106
+ };
107
+ }
108
+
109
+ const delay = group.delay ?? 0;
110
+ const frames: ResolvedAnimationFrame<LayerT>[] = [];
111
+
112
+ if (group.type === 'sequence') {
113
+ let current = baseStart;
114
+ for (let i = 0; i < group.frames.length; i += 1) {
115
+ const child = group.frames[i]!;
116
+ const childResult = resolveFramesGroup(child, current);
117
+ frames.push(...childResult.frames);
118
+ current += childResult.duration;
119
+ if (i < group.frames.length - 1) current += delay;
120
+ }
121
+ return {frames, duration: Math.max(0, current - baseStart)};
122
+ }
123
+
124
+ if (group.type === 'concurrence') {
125
+ const start = baseStart + delay;
126
+ let maxDuration = 0;
127
+ for (const child of group.frames) {
128
+ const childResult = resolveFramesGroup(child, start);
129
+ frames.push(...childResult.frames);
130
+ if (childResult.duration > maxDuration) maxDuration = childResult.duration;
131
+ }
132
+ return {frames, duration: delay + maxDuration};
133
+ }
134
+
135
+ let maxEnd = baseStart;
136
+ for (let i = 0; i < group.frames.length; i += 1) {
137
+ const child = group.frames[i]!;
138
+ const childResult = resolveFramesGroup(child, baseStart + i * delay);
139
+ frames.push(...childResult.frames);
140
+ const childEnd = baseStart + i * delay + childResult.duration;
141
+ if (childEnd > maxEnd) maxEnd = childEnd;
142
+ }
143
+
144
+ frames.sort((f1, f2) => f1.end - f2.end);
145
+
146
+ return {frames, duration: Math.max(0, maxEnd - baseStart)};
147
+ }
148
+
149
+ function getFramesDuration<LayerT extends Layer = Layer>(
150
+ frames: ResolvedAnimationFrame<LayerT>[]
151
+ ): number {
152
+ let maxEnd = 0;
153
+ for (const frame of frames) {
154
+ if (frame.end > maxEnd) maxEnd = frame.end;
155
+ }
156
+ return maxEnd;
157
+ }
158
+
159
+ function reverseResolvedFrames<LayerT extends Layer = Layer>(
160
+ frames: ResolvedAnimationFrame<LayerT>[]
161
+ ): ResolvedAnimationFrame<LayerT>[] {
162
+ const duration = getFramesDuration(frames);
163
+ const reversed: ResolvedAnimationFrame<LayerT>[] = [];
164
+ for (let i = frames.length - 1; i >= 0; i -= 1) {
165
+ const frame = frames[i]!;
166
+ reversed.push({
167
+ from: undefined,
168
+ to: frame.from ?? frame.to,
169
+ start: duration - frame.end,
170
+ end: duration - frame.start,
171
+ easing: frame.easing
172
+ });
173
+ }
174
+ return reversed;
175
+ }
176
+
177
+ /**
178
+ * Resolves the animation iteration that should be active at a timeline timestamp.
179
+ * @param time - Current timeline timestamp in milliseconds.
180
+ * @param opts - Animation frame and repeat configuration.
181
+ * @param prevState - Previous animation state, when one has already been resolved.
182
+ * @returns The current or next resolved animation state.
183
+ */
184
+ export function resolveAnimationFrames<LayerT extends Layer = Layer>(
185
+ time: number,
186
+ opts: AnimationExtensionOptions<LayerT>,
187
+ prevState?: AnimationState<LayerT>
188
+ ): AnimationState<LayerT> {
189
+ const {frames, repeat = 0, repeatType = 'loop', repeatDelay = 0} = opts;
190
+ if (!prevState) {
191
+ const resolved = resolveFramesGroup(frames, 0);
192
+ return {
193
+ start: time,
194
+ end: time + resolved.duration,
195
+ inProgress: true,
196
+ iterations: 0,
197
+ frames: resolved.frames
198
+ };
199
+ }
200
+
201
+ const state = prevState;
202
+ if (time < state.end) {
203
+ return state;
204
+ }
205
+
206
+ const iterationEnd = state.end;
207
+ const iterations = state.iterations + 1;
208
+ if (iterations > repeat) {
209
+ return state;
210
+ }
211
+ const start = iterationEnd + repeatDelay;
212
+ if (repeatType === 'reverse' && iterations % 2 === 1) {
213
+ const duration = state.end - state.start;
214
+ return {
215
+ start,
216
+ end: start + duration,
217
+ iterations,
218
+ frames: reverseResolvedFrames(state.frames),
219
+ inProgress: true
220
+ };
221
+ } else {
222
+ const resolved = resolveFramesGroup(frames, 0);
223
+ return {
224
+ start,
225
+ end: start + resolved.duration,
226
+ inProgress: true,
227
+ iterations,
228
+ frames: resolved.frames
229
+ };
230
+ }
231
+ }
232
+
233
+ function interpolateProp<PropT extends number | number[] | string | Function | object>(
234
+ from: PropT | undefined,
235
+ to: PropT | undefined,
236
+ ratio: number
237
+ ): PropT | undefined {
238
+ if (from === undefined || to === undefined) return to;
239
+ if (typeof to === 'string' || typeof to === 'function') return to;
240
+ if (typeof to === 'number' && typeof from === 'number') {
241
+ return (from + (to - from) * ratio) as PropT;
242
+ }
243
+ if (Array.isArray(to) && Array.isArray(from)) {
244
+ const len = to.length;
245
+ const out = new Array(len);
246
+ for (let i = 0; i < len; i += 1) {
247
+ const toValue = to[i];
248
+ const fromValue = from[i];
249
+ out[i] =
250
+ typeof toValue === 'number' && typeof fromValue === 'number'
251
+ ? fromValue + (toValue - fromValue) * ratio
252
+ : toValue;
253
+ }
254
+ return out as PropT;
255
+ }
256
+ return to;
257
+ }
258
+
259
+ /**
260
+ * Resolves animated child layer props for a point inside one animation iteration.
261
+ * @param layer - Base child layer whose props provide the interpolation starting values.
262
+ * @param frames - Resolved frames for the active animation iteration.
263
+ * @param timeSinceStart - Milliseconds elapsed since the iteration started.
264
+ * @returns Child layer props that should be applied at the requested time.
265
+ */
266
+ export function resolveProps<LayerT extends Layer = Layer>(
267
+ layer: LayerT,
268
+ frames: ResolvedAnimationFrame<LayerT>[],
269
+ timeSinceStart: number
270
+ ): Partial<LayerT['props']> {
271
+ if (timeSinceStart < 0 || frames.length === 0) return {} as LayerT['props'];
272
+
273
+ const baseProps = layer.props as LayerT['props'];
274
+ const nextProps: Partial<LayerT['props']> = {};
275
+ let transitions = baseProps.transitions ? {...baseProps.transitions} : undefined;
276
+ const updateTriggers = {...baseProps.updateTriggers};
277
+
278
+ const getPropValue = (key: keyof LayerT['props']) =>
279
+ Object.prototype.hasOwnProperty.call(nextProps, key) ? nextProps[key] : baseProps[key];
280
+
281
+ for (const frame of frames) {
282
+ if (timeSinceStart < frame.start) continue;
283
+
284
+ const duration = frame.end - frame.start;
285
+ if (duration <= 0) {
286
+ Object.assign(nextProps, frame.to);
287
+ continue;
288
+ }
289
+
290
+ if (frame.from === undefined) {
291
+ const from: Partial<LayerT['props']> = {};
292
+ for (const key of Object.keys(frame.to) as (keyof LayerT['props'])[]) {
293
+ from[key] = getPropValue(key);
294
+ }
295
+ frame.from = from;
296
+ }
297
+
298
+ if (timeSinceStart >= frame.end) {
299
+ Object.assign(nextProps, frame.to);
300
+ continue;
301
+ }
302
+
303
+ const t = frame.easing((timeSinceStart - frame.start) / duration);
304
+ for (const key of Object.keys(frame.to) as (keyof LayerT['props'])[]) {
305
+ const toValue = frame.to[key];
306
+ const fromValue = frame.from[key];
307
+ if (
308
+ String(key).startsWith('get') &&
309
+ (typeof fromValue === 'function' || typeof toValue === 'function')
310
+ ) {
311
+ nextProps[key] = toValue as any;
312
+ updateTriggers[key as string] = {...updateTriggers[key as string], animation: toValue};
313
+ transitions ??= {};
314
+ transitions[key as string] = {
315
+ duration: frame.end - frame.start,
316
+ easing: frame.easing
317
+ };
318
+ } else {
319
+ const value = interpolateProp(fromValue as any, toValue, t);
320
+ if (value !== undefined) {
321
+ nextProps[key] = value;
322
+ }
323
+ }
324
+ }
325
+ }
326
+
327
+ return nextProps;
328
+ }
@@ -0,0 +1,41 @@
1
+ // deck.gl-community
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ export default `\
6
+ #version 300 es
7
+ #define SHADER_NAME block-layer-fragment-shader
8
+
9
+ precision highp float;
10
+
11
+ in vec2 unitPosition;
12
+ flat in vec4 vFillColor;
13
+ flat in vec4 vLineColor;
14
+ flat in float lineWidth;
15
+ flat in vec2 size;
16
+
17
+ out vec4 fragColor;
18
+
19
+ void main(void) {
20
+ if (lineWidth > 0.0) {
21
+ vec2 relPosition = unitPosition * size;
22
+ float distToBorder =
23
+ min(
24
+ min(
25
+ min(relPosition.x, relPosition.y),
26
+ size.x - relPosition.x
27
+ ),
28
+ size.y - relPosition.y
29
+ );
30
+ if (distToBorder <= lineWidth) {
31
+ fragColor = vLineColor;
32
+ } else {
33
+ fragColor = vFillColor;
34
+ }
35
+ } else {
36
+ fragColor = vFillColor;
37
+ }
38
+
39
+ DECKGL_FILTER_COLOR(fragColor, geometry);
40
+ }
41
+ `;
@@ -0,0 +1,43 @@
1
+ // deck.gl-community
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import type {ShaderModule} from '@luma.gl/shadertools';
6
+
7
+ const glslUniformBlock = `\
8
+ uniform blockUniforms {
9
+ highp int sizeUnits;
10
+ highp float widthMinPixels;
11
+ highp float heightMinPixels;
12
+ highp float sizeMaxPixels;
13
+ highp int lineWidthUnits;
14
+ } block;
15
+ `;
16
+
17
+ /** Shader uniform values used by {@link BlockLayer}. */
18
+ export type BlockProps = {
19
+ /** Numeric deck.gl unit for block width and height. */
20
+ sizeUnits: number;
21
+ /** Minimum rendered block width in pixels. */
22
+ widthMinPixels: number;
23
+ /** Minimum rendered block height in pixels. */
24
+ heightMinPixels: number;
25
+ /** Maximum rendered block width or height in pixels. */
26
+ sizeMaxPixels: number;
27
+ /** Numeric deck.gl unit for block outline width. */
28
+ lineWidthUnits: number;
29
+ };
30
+
31
+ /** Shader module that defines uniforms consumed by {@link BlockLayer}. */
32
+ export const blockUniforms = {
33
+ name: 'block',
34
+ vs: glslUniformBlock,
35
+ fs: glslUniformBlock,
36
+ uniformTypes: {
37
+ sizeUnits: 'i32',
38
+ widthMinPixels: 'f32',
39
+ heightMinPixels: 'f32',
40
+ sizeMaxPixels: 'f32',
41
+ lineWidthUnits: 'i32'
42
+ }
43
+ } as const satisfies ShaderModule<BlockProps>;
@@ -0,0 +1,67 @@
1
+ // deck.gl-community
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ export default `\
6
+ #version 300 es
7
+ #define SHADER_NAME block-layer-vertex-shader
8
+
9
+ in vec3 positions;
10
+ in vec3 instancePositions;
11
+ in vec3 instancePositions64Low;
12
+ in vec2 instanceSizes;
13
+ in float instanceLineWidths;
14
+ in vec4 instanceFillColors;
15
+ in vec4 instanceLineColors;
16
+ in vec3 instancePickingColors;
17
+
18
+ out vec2 unitPosition;
19
+ flat out vec4 vFillColor;
20
+ flat out vec4 vLineColor;
21
+ flat out float lineWidth;
22
+ flat out vec2 size;
23
+
24
+ // This needs to be added back to the project module
25
+ vec2 project_size_to_pixel(vec2 size, int unit) {
26
+ if (unit == UNIT_PIXELS) return size;
27
+ if (unit == UNIT_COMMON && project.projectionMode != PROJECTION_MODE_IDENTITY) {
28
+ return size * project.scale;
29
+ }
30
+ return project_size(size) * project.scale;
31
+ }
32
+
33
+ float clamp_signed_size(float size, float minPixels, float maxPixels) {
34
+ float clampedMagnitude = clamp(abs(size), minPixels, maxPixels);
35
+ if (size < 0.0) {
36
+ return -clampedMagnitude;
37
+ }
38
+ return clampedMagnitude;
39
+ }
40
+
41
+ void main(void) {
42
+ geometry.worldPosition = instancePositions;
43
+ geometry.pickingColor = instancePickingColors;
44
+ geometry.uv = positions.xy;
45
+
46
+ vec2 pixelSize = project_size_to_pixel(instanceSizes, block.sizeUnits);
47
+ pixelSize.x = clamp_signed_size(pixelSize.x, block.widthMinPixels, block.sizeMaxPixels);
48
+ pixelSize.y = clamp_signed_size(pixelSize.y, block.heightMinPixels, block.sizeMaxPixels);
49
+ unitPosition = positions.xy;
50
+ size = pixelSize;
51
+ lineWidth = project_size_to_pixel(vec2(instanceLineWidths, 0.0), block.lineWidthUnits).x;
52
+
53
+ // Find the center of the point and add the current vertex
54
+ vec3 offset = vec3(unitPosition * project_pixel_size(pixelSize), 0.0);
55
+ DECKGL_FILTER_SIZE(offset, geometry);
56
+
57
+ gl_Position = project_position_to_clipspace(instancePositions, instancePositions64Low, offset, geometry.position);
58
+ DECKGL_FILTER_GL_POSITION(gl_Position, geometry);
59
+
60
+ // Apply opacity to instance color, or return instance picking color
61
+ vFillColor = vec4(instanceFillColors.rgb, instanceFillColors.a * layer.opacity);
62
+ DECKGL_FILTER_COLOR(vFillColor, geometry);
63
+
64
+ vLineColor = vec4(instanceLineColors.rgb, instanceLineColors.a * layer.opacity);
65
+ DECKGL_FILTER_COLOR(vLineColor, geometry);
66
+ }
67
+ `;
@@ -0,0 +1,199 @@
1
+ // deck.gl-community
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {
6
+ Accessor,
7
+ AccessorFunction,
8
+ color,
9
+ Color,
10
+ DefaultProps,
11
+ Layer,
12
+ LayerDataSource,
13
+ LayerProps,
14
+ picking,
15
+ Position,
16
+ project32,
17
+ UNIT,
18
+ Unit,
19
+ UpdateParameters
20
+ } from '@deck.gl/core';
21
+ import {Geometry, Model} from '@luma.gl/engine';
22
+
23
+ import fs from './block-layer-fragment.glsl';
24
+ import {BlockProps, blockUniforms} from './block-layer-uniforms';
25
+ import vs from './block-layer-vertex.glsl';
26
+
27
+ const DEFAULT_COLOR: [number, number, number, number] = [0, 0, 0, 255];
28
+
29
+ const defaultProps: DefaultProps<BlockLayerProps> = {
30
+ sizeUnits: 'meters',
31
+ widthMinPixels: {type: 'number', min: 0, value: 0},
32
+ heightMinPixels: {type: 'number', min: 0, value: 0},
33
+ sizeMaxPixels: {type: 'number', min: 0, value: Number.MAX_SAFE_INTEGER},
34
+
35
+ lineWidthUnits: 'pixels',
36
+
37
+ getPosition: {type: 'accessor', value: (x: any) => x.position},
38
+ getSize: {type: 'accessor', value: [10, 10]},
39
+ getLineWidth: {type: 'accessor', value: 1},
40
+ getFillColor: {type: 'accessor', value: DEFAULT_COLOR},
41
+ getLineColor: {type: 'accessor', value: DEFAULT_COLOR}
42
+ };
43
+
44
+ /** Properties supported by {@link BlockLayer}. */
45
+ export type BlockLayerProps<DataT = unknown> = _BlockLayerProps<DataT> & LayerProps;
46
+
47
+ /** Properties added by BlockLayer. */
48
+ type _BlockLayerProps<DataT> = {
49
+ /** Data objects rendered as rectangular blocks. */
50
+ data: LayerDataSource<DataT>;
51
+ /**
52
+ * The units of the block size, one of `'meters'`, `'common'`, and `'pixels'`.
53
+ * @defaultValue 'meters'
54
+ */
55
+ sizeUnits?: Unit;
56
+
57
+ /**
58
+ * The minimum width in pixels. This prop can be used to prevent the block from getting too small when zoomed out.
59
+ * @defaultValue 0
60
+ */
61
+ widthMinPixels?: number;
62
+ /**
63
+ * The minimum height in pixels. This prop can be used to prevent the block from getting too small when zoomed out.
64
+ * @defaultValue 0
65
+ */
66
+ heightMinPixels?: number;
67
+ /**
68
+ * The maximum width or height in pixels. This prop can be used to prevent the block from getting too big when zoomed in.
69
+ * @defaultValue Number.MAX_SAFE_INTEGER
70
+ */
71
+ sizeMaxPixels?: number;
72
+
73
+ /**
74
+ * The units of the stroke width, one of `'meters'`, `'common'`, and `'pixels'`.
75
+ * @defaultValue 'pixels'
76
+ */
77
+ lineWidthUnits?: Unit;
78
+
79
+ /**
80
+ * The outline width of each object.
81
+ * @defaultValue 1
82
+ */
83
+ getLineWidth?: Accessor<DataT, number>;
84
+
85
+ /**
86
+ * Method called to retrieve the position of each object.
87
+ * @defaultValue object => object.position
88
+ */
89
+ getPosition?: AccessorFunction<DataT, Position>;
90
+ /** Width and height of each block. @defaultValue [10, 10] */
91
+ getSize?: Accessor<DataT, [number, number]>;
92
+ /**
93
+ * The rgba color is in the format of `[r, g, b, [a]]`
94
+ * @defaultValue [0, 0, 0, 255]
95
+ */
96
+ getLineColor?: Accessor<DataT, Color>;
97
+ /**
98
+ * The rgba color is in the format of `[r, g, b, [a]]`
99
+ * @defaultValue [0, 0, 0, 255]
100
+ */
101
+ getFillColor?: Accessor<DataT, Color>;
102
+ };
103
+
104
+ /** Renders axis-aligned rectangular blocks with fill and outline colors. */
105
+ export class BlockLayer<DataT = any, ExtraPropsT extends {} = {}> extends Layer<
106
+ ExtraPropsT & Required<_BlockLayerProps<DataT>>
107
+ > {
108
+ static override layerName = 'BlockLayer';
109
+ static override defaultProps = defaultProps;
110
+
111
+ override state: {
112
+ model?: Model;
113
+ } = {};
114
+
115
+ override getShaders() {
116
+ return super.getShaders({
117
+ vs,
118
+ fs,
119
+ modules: [project32, color, picking, blockUniforms]
120
+ });
121
+ }
122
+
123
+ initializeState() {
124
+ this.getAttributeManager()!.addInstanced({
125
+ instancePositions: {
126
+ size: 3,
127
+ type: 'float64',
128
+ fp64: this.use64bitPositions(),
129
+ transition: true,
130
+ accessor: 'getPosition'
131
+ },
132
+ instanceSizes: {
133
+ size: 2,
134
+ transition: true,
135
+ accessor: 'getSize'
136
+ },
137
+ instanceLineWidths: {
138
+ size: 1,
139
+ transition: true,
140
+ accessor: 'getLineWidth'
141
+ },
142
+ instanceLineColors: {
143
+ size: this.props.colorFormat.length,
144
+ type: 'unorm8',
145
+ transition: true,
146
+ accessor: 'getLineColor',
147
+ defaultValue: DEFAULT_COLOR
148
+ },
149
+ instanceFillColors: {
150
+ size: this.props.colorFormat.length,
151
+ type: 'unorm8',
152
+ transition: true,
153
+ accessor: 'getFillColor',
154
+ defaultValue: DEFAULT_COLOR
155
+ }
156
+ });
157
+ }
158
+
159
+ override updateState(params: UpdateParameters<this>): void {
160
+ const {changeFlags} = params;
161
+ super.updateState(params);
162
+ if (changeFlags.extensionsChanged) {
163
+ this.state.model?.destroy();
164
+ this.state.model = this._getModel();
165
+ this.getAttributeManager()!.invalidateAll();
166
+ }
167
+ }
168
+
169
+ override draw() {
170
+ const {sizeUnits, widthMinPixels, heightMinPixels, sizeMaxPixels, lineWidthUnits} = this.props;
171
+ const model = this.state.model!;
172
+ const blockProps: BlockProps = {
173
+ sizeUnits: UNIT[sizeUnits],
174
+ widthMinPixels,
175
+ heightMinPixels,
176
+ sizeMaxPixels,
177
+ lineWidthUnits: UNIT[lineWidthUnits]
178
+ };
179
+ model.shaderInputs.setProps({block: blockProps});
180
+ model.draw(this.context.renderPass);
181
+ }
182
+
183
+ protected _getModel(): Model {
184
+ // a square
185
+ const positions = [0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0];
186
+ return new Model(this.context.device, {
187
+ ...this.getShaders(),
188
+ id: this.props.id,
189
+ bufferLayout: this.getAttributeManager()!.getBufferLayouts(),
190
+ geometry: new Geometry({
191
+ topology: 'triangle-strip',
192
+ attributes: {
193
+ positions: new Float32Array(positions)
194
+ }
195
+ }),
196
+ isInstanced: true
197
+ });
198
+ }
199
+ }