@deck.gl/extensions 9.4.0-alpha.2 → 9.4.0-beta.3

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 (43) hide show
  1. package/dist/clip/clip-extension.d.ts +3 -0
  2. package/dist/clip/clip-extension.d.ts.map +1 -1
  3. package/dist/clip/clip-extension.js +7 -0
  4. package/dist/clip/clip-extension.js.map +1 -1
  5. package/dist/dist.dev.js +2195 -142
  6. package/dist/fill-style/fill-style-extension.d.ts +38 -6
  7. package/dist/fill-style/fill-style-extension.d.ts.map +1 -1
  8. package/dist/fill-style/fill-style-extension.js +88 -10
  9. package/dist/fill-style/fill-style-extension.js.map +1 -1
  10. package/dist/fill-style/procedural-pattern.d.ts +65 -0
  11. package/dist/fill-style/procedural-pattern.d.ts.map +1 -0
  12. package/dist/fill-style/procedural-pattern.js +135 -0
  13. package/dist/fill-style/procedural-pattern.js.map +1 -0
  14. package/dist/fill-style/shader-module.d.ts +13 -4
  15. package/dist/fill-style/shader-module.d.ts.map +1 -1
  16. package/dist/fill-style/shader-module.js +212 -21
  17. package/dist/fill-style/shader-module.js.map +1 -1
  18. package/dist/index.cjs +3484 -215
  19. package/dist/index.cjs.map +3 -4
  20. package/dist/index.d.ts +2 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/mask/shader-module.d.ts +1 -0
  23. package/dist/mask/shader-module.d.ts.map +1 -1
  24. package/dist/path-style/path-style-extension.d.ts +49 -6
  25. package/dist/path-style/path-style-extension.d.ts.map +1 -1
  26. package/dist/path-style/path-style-extension.js +237 -37
  27. package/dist/path-style/path-style-extension.js.map +1 -1
  28. package/dist/path-style/shaders.glsl.d.ts +30 -1
  29. package/dist/path-style/shaders.glsl.d.ts.map +1 -1
  30. package/dist/path-style/shaders.glsl.js +136 -21
  31. package/dist/path-style/shaders.glsl.js.map +1 -1
  32. package/dist/terrain/shader-module.d.ts +17 -2
  33. package/dist/terrain/shader-module.d.ts.map +1 -1
  34. package/dist/utils/debug.d.ts.map +1 -1
  35. package/dist.min.js +1251 -96
  36. package/package.json +6 -6
  37. package/src/clip/clip-extension.ts +15 -1
  38. package/src/fill-style/fill-style-extension.ts +135 -13
  39. package/src/fill-style/procedural-pattern.ts +203 -0
  40. package/src/fill-style/shader-module.ts +233 -23
  41. package/src/index.ts +10 -1
  42. package/src/path-style/path-style-extension.ts +350 -44
  43. package/src/path-style/shaders.glsl.ts +268 -27
@@ -4,15 +4,43 @@
4
4
 
5
5
  import type {ShaderModule} from '@luma.gl/shadertools';
6
6
  import {project, fp64LowPart} from '@deck.gl/core';
7
- import type {ProjectProps, ProjectUniforms} from '@deck.gl/core';
7
+ import type {
8
+ OrthographicViewport,
9
+ ProjectProps,
10
+ ProjectUniforms,
11
+ Unit,
12
+ Viewport
13
+ } from '@deck.gl/core';
8
14
 
9
15
  import type {Texture} from '@luma.gl/core';
10
16
 
17
+ // Common-space size of one atlas texel: the equator measures 40,000km and spans 512 common units
18
+ const FILL_UV_SCALE = 512 / 40000000;
19
+
20
+ /** Common-space size of one atlas texel, i.e. what turns the pixel size of a frame into the size
21
+ * of one tile in common space. */
22
+ function getPatternUnitScale(sizeUnits: Unit, viewport: Viewport): number {
23
+ switch (sizeUnits) {
24
+ case 'pixels':
25
+ // Following the zoom exactly would rescale the pattern on every fractional zoom change,
26
+ // which never lets the tiling settle. The pattern is re-anchored once per zoom level
27
+ // instead, and within a level stays within a factor of sqrt(2) of its nominal size.
28
+ return 2 ** -Math.round(viewport.zoom);
29
+ case 'common':
30
+ return 1;
31
+ default:
32
+ return FILL_UV_SCALE;
33
+ }
34
+ }
35
+
11
36
  const uniformBlock = /* glsl */ `\
12
37
  layout(std140) uniform fillUniforms {
13
38
  vec2 patternTextureSize;
39
+ float patternUnitScale;
14
40
  bool patternEnabled;
15
41
  bool patternMask;
42
+ bool procedural;
43
+ bool flipY;
16
44
  vec2 uvCoordinateOrigin;
17
45
  vec2 uvCoordinateOrigin64Low;
18
46
  } fill;
@@ -22,13 +50,20 @@ layout(std140) uniform fillUniforms {
22
50
  * fill pattern shader module
23
51
  */
24
52
  const patternVs = /* glsl */ `
53
+ uniform sampler2D fill_patternTexture;
54
+
25
55
  in vec4 fillPatternFrames;
26
56
  in float fillPatternScales;
27
57
  in vec2 fillPatternOffsets;
58
+ in vec4 fillPatternBackgroundColors;
28
59
 
29
60
  out vec2 fill_uv;
30
61
  out vec4 fill_patternBounds;
31
62
  out vec4 fill_patternPlacement;
63
+ out vec4 fill_backgroundColor;
64
+ out float fill_patternCoordinateScale;
65
+ flat out vec4 fill_patternParams0;
66
+ flat out vec4 fill_patternParams1;
32
67
  `;
33
68
 
34
69
  const vs = `
@@ -41,9 +76,136 @@ uniform sampler2D fill_patternTexture;
41
76
 
42
77
  in vec4 fill_patternBounds;
43
78
  in vec4 fill_patternPlacement;
79
+ in vec4 fill_backgroundColor;
44
80
  in vec2 fill_uv;
81
+ in float fill_patternCoordinateScale;
82
+ flat in vec4 fill_patternParams0;
83
+ flat in vec4 fill_patternParams1;
84
+
85
+ const float FILL_PATTERN_HATCH = 1.0;
86
+ const float FILL_PATTERN_CROSS_HATCH = 2.0;
87
+ const float FILL_PATTERN_DOTS = 3.0;
88
+
89
+ float fill_getPatternCoordinate(vec2 axis, float period, float offset) {
90
+ float coordinateScale = max(abs(fill_patternCoordinateScale), 1.0e-20);
91
+ vec2 coordinateOrigin = fill.uvCoordinateOrigin;
92
+ vec2 coordinateOffset = fill.uvCoordinateOrigin64Low + fill_uv;
93
+ float yDirection = fill.flipY ? 1.0 : -1.0;
94
+ coordinateOrigin.y *= yDirection;
95
+ coordinateOffset.y *= yDirection;
96
+
97
+ // Reduce the high part before adding the local offset to retain precision in large coordinates.
98
+ float highPart = mod(dot(coordinateOrigin, axis) / coordinateScale, period);
99
+ float lowPart = dot(coordinateOffset, axis) / coordinateScale;
100
+ return mod(highPart + lowPart + offset * period, period);
101
+ }
102
+
103
+ vec2 fill_getPatternCoords(
104
+ vec2 axis0,
105
+ vec2 axis1,
106
+ vec2 period,
107
+ vec2 offset
108
+ ) {
109
+ return vec2(
110
+ fill_getPatternCoordinate(axis0, period.x, offset.x),
111
+ fill_getPatternCoordinate(axis1, period.y, offset.y)
112
+ );
113
+ }
114
+
115
+ float fill_getScreenSpaceAlpha(float signedDistance) {
116
+ float distanceDerivative = fwidth(signedDistance);
117
+ if (distanceDerivative == 0.0) {
118
+ return signedDistance <= 0.0 ? 1.0 : 0.0;
119
+ }
120
+ // Normalize the SDF to screen pixels so the coverage ramp is independent of pattern units.
121
+ float distanceInPixels = signedDistance / distanceDerivative;
122
+ return 1.0 - smoothstep(-0.5, 0.5, distanceInPixels);
123
+ }
124
+
125
+ float fill_getLineAlpha(float position, float center, float period, float strokeWidth) {
126
+ float distanceToLine = abs(mod(position - center + period * 0.5, period) - period * 0.5);
127
+ return fill_getScreenSpaceAlpha(distanceToLine - strokeWidth * 0.5);
128
+ }
129
+
130
+ float fill_getHatchAlpha(float angle, float strokeWidth, vec2 gaps, bool alternating) {
131
+ vec2 lineDirection = vec2(cos(angle), sin(angle));
132
+ vec2 lineNormal = vec2(-lineDirection.y, lineDirection.x);
133
+ float period = alternating
134
+ ? strokeWidth * 2.0 + gaps.x + gaps.y
135
+ : strokeWidth + gaps.x;
136
+ float offset = dot(fill_patternPlacement.xy, lineNormal);
137
+ float position = fill_getPatternCoordinate(lineNormal, period, offset);
138
+ float alpha = fill_getLineAlpha(position, 0.0, period, strokeWidth);
139
+ if (alternating) {
140
+ alpha = max(
141
+ alpha,
142
+ fill_getLineAlpha(position, strokeWidth + gaps.x, period, strokeWidth)
143
+ );
144
+ }
145
+ return alpha;
146
+ }
147
+
148
+ float fill_getDotAlpha(float angle, float skew, float radius, float gap) {
149
+ vec2 axis0 = vec2(cos(angle), sin(angle));
150
+ float axis1Angle = angle + 1.5707963267948966 - skew;
151
+ vec2 axis1 = vec2(cos(axis1Angle), sin(axis1Angle));
152
+ float determinant = axis0.x * axis1.y - axis0.y * axis1.x;
153
+
154
+ // The reciprocal basis maps common coordinates into the skewed dot lattice.
155
+ vec2 reciprocal0 = vec2(axis1.y, -axis1.x) / determinant;
156
+ vec2 reciprocal1 = vec2(-axis0.y, axis0.x) / determinant;
157
+ float period = radius * 2.0 + gap;
158
+ vec2 latticeCoords = fill_getPatternCoords(
159
+ reciprocal0,
160
+ reciprocal1,
161
+ vec2(period),
162
+ fill_patternPlacement.xy
163
+ );
164
+ vec2 cellOffset = mod(latticeCoords + period * 0.5, period) - period * 0.5;
165
+ float distanceToDotSquared = 1.0e20;
166
+ // In a skewed lattice the closest dot is not necessarily in the independently wrapped cell.
167
+ // Include adjacent cells to keep the repeated distance field continuous at cell boundaries.
168
+ for (int x = -1; x <= 1; x++) {
169
+ for (int y = -1; y <= 1; y++) {
170
+ vec2 neighborOffset = cellOffset + vec2(float(x), float(y)) * period;
171
+ vec2 dotOffset = axis0 * neighborOffset.x + axis1 * neighborOffset.y;
172
+ distanceToDotSquared = min(distanceToDotSquared, dot(dotOffset, dotOffset));
173
+ }
174
+ }
175
+ float distanceToDot = sqrt(distanceToDotSquared);
176
+ return fill_getScreenSpaceAlpha(distanceToDot - radius);
177
+ }
178
+
179
+ float fill_getProceduralPatternAlpha() {
180
+ float patternType = fill_patternParams0.x;
181
+ float size = fill_patternParams0.y;
182
+ vec2 gaps = fill_patternParams0.zw;
183
+ vec2 angles = fill_patternParams1.xy;
184
+ float elementCount = fill_patternParams1.z;
185
+
186
+ if (patternType == FILL_PATTERN_HATCH) {
187
+ return fill_getHatchAlpha(angles.x, size, gaps, elementCount == 2.0);
188
+ }
189
+ if (patternType == FILL_PATTERN_CROSS_HATCH) {
190
+ return max(
191
+ fill_getHatchAlpha(angles.x, size, gaps, false),
192
+ fill_getHatchAlpha(angles.y, size, gaps, false)
193
+ );
194
+ }
195
+ if (patternType == FILL_PATTERN_DOTS) {
196
+ return fill_getDotAlpha(angles.x, angles.y, size, gaps.x);
197
+ }
198
+ return 1.0;
199
+ }
45
200
 
46
- const float FILL_UV_SCALE = 512.0 / 40000000.0;
201
+ // Draw the pattern over the background fill (Porter-Duff source-over)
202
+ vec4 fill_blendOverBackground(vec4 pattern, vec4 background) {
203
+ if (background.a == 0.0 || layer.opacity == 0.0) return pattern;
204
+ float patternAlpha = pattern.a / layer.opacity;
205
+ float blendedAlpha = patternAlpha + background.a * (1.0 - patternAlpha);
206
+ vec3 blendedRGB = mix(background.rgb, pattern.rgb, patternAlpha / blendedAlpha);
207
+ return vec4(blendedRGB, blendedAlpha * layer.opacity);
208
+ }
47
209
  `;
48
210
 
49
211
  const fs = `
@@ -53,30 +215,56 @@ ${patternFs}
53
215
 
54
216
  const inject = {
55
217
  'vs:DECKGL_FILTER_GL_POSITION': /* glsl */ `
56
- fill_uv = geometry.position.xy;
57
- `,
58
-
59
- 'vs:DECKGL_FILTER_COLOR': /* glsl */ `
60
218
  if (fill.patternEnabled) {
61
- fill_patternBounds = fillPatternFrames / vec4(fill.patternTextureSize, fill.patternTextureSize);
62
219
  fill_patternPlacement.xy = fillPatternOffsets;
63
- fill_patternPlacement.zw = fillPatternScales * fillPatternFrames.zw;
220
+ fill_backgroundColor = fillPatternBackgroundColors;
221
+ if (fill.procedural) {
222
+ fill_uv = geometry.position.xy;
223
+ fill_patternCoordinateScale = fill.patternUnitScale * fillPatternScales;
224
+ int texelIndex = int(fillPatternFrames.x) * 4;
225
+ fill_patternParams0 = vec4(
226
+ texelFetch(fill_patternTexture, ivec2(texelIndex, 0), 0).rg,
227
+ texelFetch(fill_patternTexture, ivec2(texelIndex + 1, 0), 0).rg
228
+ );
229
+ fill_patternParams1 = vec4(
230
+ texelFetch(fill_patternTexture, ivec2(texelIndex + 2, 0), 0).rg,
231
+ texelFetch(fill_patternTexture, ivec2(texelIndex + 3, 0), 0).rg
232
+ );
233
+ } else {
234
+ fill_patternBounds = fillPatternFrames / vec4(fill.patternTextureSize, fill.patternTextureSize);
235
+ vec2 patternFrameCommon = fill.patternUnitScale * fillPatternScales * fillPatternFrames.zw;
236
+ // Reduce the coordinate origin to within one tile before adding the vertex position. The
237
+ // origin is large in common space, and fp32 cannot carry the sum at full precision.
238
+ vec2 origin = mod(fill.uvCoordinateOrigin, patternFrameCommon) + fill.uvCoordinateOrigin64Low;
239
+ fill_uv = (origin + geometry.position.xy) / patternFrameCommon;
240
+ // Pattern atlases use top-left coordinates, so reverse common-space Y in bottom-left views.
241
+ fill_uv.y *= fill.flipY ? 1.0 : -1.0;
242
+ fill_uv += fillPatternOffsets;
243
+ }
64
244
  }
65
245
  `,
66
246
 
67
247
  'fs:DECKGL_FILTER_COLOR': /* glsl */ `
68
248
  if (fill.patternEnabled) {
69
- vec2 scale = FILL_UV_SCALE * fill_patternPlacement.zw;
70
- vec2 patternUV = mod(mod(fill.uvCoordinateOrigin, scale) + fill.uvCoordinateOrigin64Low + fill_uv, scale) / scale;
71
- patternUV = mod(fill_patternPlacement.xy + patternUV, 1.0);
249
+ if (fill.procedural) {
250
+ color.a *= fill_getProceduralPatternAlpha();
251
+ } else {
252
+ vec2 patternUV = fract(fill_uv);
253
+ vec2 texCoords = fill_patternBounds.xy + fill_patternBounds.zw * patternUV;
72
254
 
73
- vec2 texCoords = fill_patternBounds.xy + fill_patternBounds.zw * patternUV;
255
+ // Tiling is emulated by wrapping the coordinate, so texCoords jumps from the end of the
256
+ // frame back to its start once per tile, leading to the wrong mip level being selected,
257
+ // which leads to artifacts at pattern edges. fill_uv is continuous across the primitive,
258
+ // meaning that the correct mip level is always selected
259
+ vec4 grad = fill_patternBounds.zwzw * vec4(dFdx(fill_uv), dFdy(fill_uv));
260
+ vec4 patternColor = textureGrad(fill_patternTexture, texCoords, grad.xy, grad.zw);
74
261
 
75
- vec4 patternColor = texture(fill_patternTexture, texCoords);
76
- color.a *= patternColor.a;
77
- if (!fill.patternMask) {
78
- color.rgb = patternColor.rgb;
262
+ color.a *= patternColor.a;
263
+ if (!fill.patternMask) {
264
+ color.rgb = patternColor.rgb;
265
+ }
79
266
  }
267
+ color = fill_blendOverBackground(color, fill_backgroundColor);
80
268
  }
81
269
  `
82
270
  };
@@ -85,13 +273,19 @@ export type FillStyleModuleProps = {
85
273
  project: ProjectProps;
86
274
  fillPatternEnabled?: boolean;
87
275
  fillPatternMask?: boolean;
276
+ procedural?: boolean;
88
277
  fillPatternTexture: Texture;
278
+ fillPatternSizeUnits?: Unit;
279
+ fillPatternCommonFrame?: [number, number] | null;
89
280
  };
90
281
 
91
282
  type FillStyleModuleUniforms = {
92
283
  patternTextureSize?: [number, number];
284
+ patternUnitScale?: number;
93
285
  patternEnabled?: boolean;
94
286
  patternMask?: boolean;
287
+ procedural?: boolean;
288
+ flipY?: boolean;
95
289
  uvCoordinateOrigin?: [number, number];
96
290
  uvCoordinateOrigin64Low?: [number, number];
97
291
  };
@@ -114,19 +308,32 @@ function getPatternUniforms(
114
308
  uniforms.patternTextureSize = [fillPatternTexture.width, fillPatternTexture.height];
115
309
  }
116
310
  if ('project' in opts) {
117
- const {fillPatternMask = true, fillPatternEnabled = true} = opts;
311
+ const {
312
+ fillPatternMask = true,
313
+ fillPatternEnabled = true,
314
+ fillPatternSizeUnits = 'meters',
315
+ fillPatternCommonFrame = null,
316
+ procedural = false
317
+ } = opts;
118
318
  const projectUniforms = project.getUniforms(opts.project) as ProjectUniforms;
119
319
  const {commonOrigin: coordinateOriginCommon} = projectUniforms;
320
+ const unitScale = getPatternUnitScale(fillPatternSizeUnits, opts.project.viewport);
120
321
 
121
- const coordinateOriginCommon64Low: [number, number] = [
122
- fp64LowPart(coordinateOriginCommon[0]),
123
- fp64LowPart(coordinateOriginCommon[1])
124
- ];
322
+ // Improve the precision of the uv mapping by removing an integer multiple of the
323
+ // pattern frames. This results in the same result, without wobbling at high zooms
324
+ const origin: [number, number] = [coordinateOriginCommon[0], coordinateOriginCommon[1]];
325
+ if (fillPatternCommonFrame) {
326
+ origin[0] %= unitScale * fillPatternCommonFrame[0];
327
+ origin[1] %= unitScale * fillPatternCommonFrame[1];
328
+ }
125
329
 
126
- uniforms.uvCoordinateOrigin = coordinateOriginCommon.slice(0, 2) as [number, number];
127
- uniforms.uvCoordinateOrigin64Low = coordinateOriginCommon64Low;
330
+ uniforms.uvCoordinateOrigin = origin;
331
+ uniforms.uvCoordinateOrigin64Low = [fp64LowPart(origin[0]), fp64LowPart(origin[1])];
332
+ uniforms.patternUnitScale = unitScale;
128
333
  uniforms.patternMask = fillPatternMask;
129
334
  uniforms.patternEnabled = fillPatternEnabled;
335
+ uniforms.procedural = procedural;
336
+ uniforms.flipY = (opts.project.viewport as OrthographicViewport)?.flipY ?? false;
130
337
  }
131
338
  return uniforms;
132
339
  }
@@ -140,8 +347,11 @@ export const patternShaders = {
140
347
  getUniforms: getPatternUniforms,
141
348
  uniformTypes: {
142
349
  patternTextureSize: 'vec2<f32>',
350
+ patternUnitScale: 'f32',
143
351
  patternEnabled: 'i32',
144
352
  patternMask: 'i32',
353
+ procedural: 'i32',
354
+ flipY: 'i32',
145
355
  uvCoordinateOrigin: 'vec2<f32>',
146
356
  uvCoordinateOrigin64Low: 'vec2<f32>'
147
357
  }
package/src/index.ts CHANGED
@@ -23,12 +23,21 @@ export type {
23
23
  } from './data-filter/data-filter-extension';
24
24
  export type {
25
25
  PathStyleExtensionProps,
26
- PathStyleExtensionOptions
26
+ PathStyleExtensionOptions,
27
+ DashMode,
28
+ DashUnits
27
29
  } from './path-style/path-style-extension';
28
30
  export type {
29
31
  FillStyleExtensionProps,
30
32
  FillStyleExtensionOptions
31
33
  } from './fill-style/fill-style-extension';
34
+ export type {
35
+ HatchPatternConfig,
36
+ CrossHatchPatternConfig,
37
+ DotPatternConfig,
38
+ ProceduralPatternConfig,
39
+ ProceduralPatternMapping
40
+ } from './fill-style/procedural-pattern';
32
41
  export type {ClipExtensionProps} from './clip/clip-extension';
33
42
  export type {CollisionFilterExtensionProps} from './collision-filter/collision-filter-extension';
34
43
  export type {MaskExtensionProps} from './mask/mask-extension';