@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
package/dist/index.cjs CHANGED
@@ -15,11 +15,11 @@ var __copyProps = (to, from, except, desc) => {
15
15
  }
16
16
  return to;
17
17
  };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
19
19
 
20
20
  // dist/index.js
21
- var dist_exports = {};
22
- __export(dist_exports, {
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
23
  BrushingExtension: () => brushing_extension_default,
24
24
  ClipExtension: () => clip_extension_default,
25
25
  CollisionFilterExtension: () => collision_filter_extension_default,
@@ -31,13 +31,2642 @@ __export(dist_exports, {
31
31
  _TerrainExtension: () => terrain_extension_default,
32
32
  project64: () => project64_default
33
33
  });
34
- module.exports = __toCommonJS(dist_exports);
34
+ module.exports = __toCommonJS(index_exports);
35
35
 
36
- // dist/brushing/brushing-extension.js
37
- var import_core2 = require("@deck.gl/core");
36
+ // ../core/src/utils/log.ts
37
+ var import_log = require("@probe.gl/log");
38
+ var defaultLogger = new import_log.Log({ id: "deck" });
39
+ var log_default = defaultLogger;
40
+
41
+ // ../core/src/shaderlib/index.ts
42
+ var import_shadertools2 = require("@luma.gl/shadertools");
43
+
44
+ // ../core/src/shaderlib/misc/geometry.ts
45
+ var source = (
46
+ /* wgsl */
47
+ `const SMOOTH_EDGE_RADIUS: f32 = 0.5;
48
+
49
+ struct VertexGeometry {
50
+ position: vec4<f32>,
51
+ worldPosition: vec3<f32>,
52
+ worldPositionAlt: vec3<f32>,
53
+ normal: vec3<f32>,
54
+ uv: vec2<f32>,
55
+ pickingColor: vec3<f32>,
56
+ };
57
+
58
+ var<private> geometry_: VertexGeometry = VertexGeometry(
59
+ vec4<f32>(0.0, 0.0, 1.0, 0.0),
60
+ vec3<f32>(0.0, 0.0, 0.0),
61
+ vec3<f32>(0.0, 0.0, 0.0),
62
+ vec3<f32>(0.0, 0.0, 0.0),
63
+ vec2<f32>(0.0, 0.0),
64
+ vec3<f32>(0.0, 0.0, 0.0)
65
+ );
66
+
67
+ struct FragmentGeometry {
68
+ uv: vec2<f32>,
69
+ };
70
+
71
+ var<private> fragmentGeometry: FragmentGeometry;
72
+
73
+ fn smoothedge(edge: f32, x: f32) -> f32 {
74
+ return smoothstep(edge - SMOOTH_EDGE_RADIUS, edge + SMOOTH_EDGE_RADIUS, x);
75
+ }
76
+ `
77
+ );
78
+ var defines = "#define SMOOTH_EDGE_RADIUS 0.5";
79
+ var vs = (
80
+ /* glsl */
81
+ `${defines}
82
+
83
+ struct VertexGeometry {
84
+ vec4 position;
85
+ vec3 worldPosition;
86
+ vec3 worldPositionAlt;
87
+ vec3 normal;
88
+ vec2 uv;
89
+ vec3 pickingColor;
90
+ } geometry = VertexGeometry(
91
+ vec4(0.0, 0.0, 1.0, 0.0),
92
+ vec3(0.0),
93
+ vec3(0.0),
94
+ vec3(0.0),
95
+ vec2(0.0),
96
+ vec3(0.0)
97
+ );
98
+ `
99
+ );
100
+ var fs = (
101
+ /* glsl */
102
+ `${defines}
103
+
104
+ struct FragmentGeometry {
105
+ vec2 uv;
106
+ };
107
+ FragmentGeometry geometry;
108
+
109
+ float smoothedge(float edge, float x) {
110
+ return smoothstep(edge - SMOOTH_EDGE_RADIUS, edge + SMOOTH_EDGE_RADIUS, x);
111
+ }
112
+ `
113
+ );
114
+ var geometry_default = {
115
+ name: "geometry",
116
+ source,
117
+ vs,
118
+ fs
119
+ };
120
+
121
+ // ../core/src/shaderlib/project/project.ts
122
+ var import_shadertools = require("@luma.gl/shadertools");
123
+
124
+ // ../core/src/shaderlib/project/viewport-uniforms.ts
125
+ var import_core = require("@math.gl/core");
126
+
127
+ // ../core/src/lib/constants.ts
128
+ var import_mjolnir = require("mjolnir.js");
129
+ var COORDINATE_SYSTEM = {
130
+ /**
131
+ * `LNGLAT` if rendering into a geospatial viewport, `CARTESIAN` otherwise
132
+ */
133
+ DEFAULT: "default",
134
+ /**
135
+ * Positions are interpreted as [longitude, latitude, elevation]
136
+ * longitude/latitude are in degrees, elevation is in meters.
137
+ * Dimensions are in meters.
138
+ */
139
+ LNGLAT: "lnglat",
140
+ /**
141
+ * Positions are interpreted as [x, y, z] in meter offsets from the coordinate origin.
142
+ * Dimensions are in meters.
143
+ */
144
+ METER_OFFSETS: "meter-offsets",
145
+ /**
146
+ * Positions are interpreted as [deltaLng, deltaLat, elevation] from the coordinate origin.
147
+ * deltaLng/deltaLat are in degrees, elevation is in meters.
148
+ * Dimensions are in meters.
149
+ */
150
+ LNGLAT_OFFSETS: "lnglat-offsets",
151
+ /**
152
+ * Positions and dimensions are in the common units of the viewport.
153
+ */
154
+ CARTESIAN: "cartesian"
155
+ };
156
+ Object.defineProperty(COORDINATE_SYSTEM, "IDENTITY", {
157
+ get: () => {
158
+ log_default.deprecated("COORDINATE_SYSTEM.IDENTITY", "COORDINATE_SYSTEM.CARTESIAN")();
159
+ return COORDINATE_SYSTEM.CARTESIAN;
160
+ }
161
+ });
162
+ var PROJECTION_MODE = {
163
+ /**
164
+ * Render geospatial data in Web Mercator projection
165
+ */
166
+ WEB_MERCATOR: 1,
167
+ /**
168
+ * Render geospatial data as a 3D globe
169
+ */
170
+ GLOBE: 2,
171
+ /**
172
+ * (Internal use only) Web Mercator projection at high zoom
173
+ */
174
+ WEB_MERCATOR_AUTO_OFFSET: 4,
175
+ /**
176
+ * No transformation
177
+ */
178
+ IDENTITY: 0
179
+ };
180
+ var UNIT = {
181
+ common: 0,
182
+ meters: 1,
183
+ pixels: 2
184
+ };
185
+
186
+ // ../core/src/utils/memoize.ts
187
+ function isEqual(a, b) {
188
+ if (a === b) {
189
+ return true;
190
+ }
191
+ if (Array.isArray(a)) {
192
+ const len = a.length;
193
+ if (!b || b.length !== len) {
194
+ return false;
195
+ }
196
+ for (let i = 0; i < len; i++) {
197
+ if (a[i] !== b[i]) {
198
+ return false;
199
+ }
200
+ }
201
+ return true;
202
+ }
203
+ return false;
204
+ }
205
+ function memoize(compute) {
206
+ let cachedArgs = {};
207
+ let cachedResult;
208
+ return (args) => {
209
+ for (const key in args) {
210
+ if (!isEqual(args[key], cachedArgs[key])) {
211
+ cachedResult = compute(args);
212
+ cachedArgs = args;
213
+ break;
214
+ }
215
+ }
216
+ return cachedResult;
217
+ };
218
+ }
219
+
220
+ // ../core/src/shaderlib/project/viewport-uniforms.ts
221
+ var ZERO_VECTOR = [0, 0, 0, 0];
222
+ var VECTOR_TO_POINT_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0];
223
+ var IDENTITY_MATRIX = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
224
+ var DEFAULT_PIXELS_PER_UNIT2 = [0, 0, 0];
225
+ var DEFAULT_COORDINATE_ORIGIN = [0, 0, 0];
226
+ var COORDINATE_SYSTEM_NUMBERS = {
227
+ default: -1,
228
+ cartesian: 0,
229
+ lnglat: 1,
230
+ "meter-offsets": 2,
231
+ "lnglat-offsets": 3
232
+ };
233
+ function getShaderCoordinateSystem(coordinateSystem) {
234
+ const shaderCoordinateSystem = COORDINATE_SYSTEM_NUMBERS[coordinateSystem];
235
+ if (shaderCoordinateSystem === void 0) {
236
+ throw new Error(`Invalid coordinateSystem: ${coordinateSystem}`);
237
+ }
238
+ return shaderCoordinateSystem;
239
+ }
240
+ var getMemoizedViewportUniforms = memoize(calculateViewportUniforms);
241
+ function getOffsetOrigin(viewport, coordinateSystem, coordinateOrigin = DEFAULT_COORDINATE_ORIGIN) {
242
+ if (coordinateOrigin.length < 3) {
243
+ coordinateOrigin = [coordinateOrigin[0], coordinateOrigin[1], 0];
244
+ }
245
+ let shaderCoordinateOrigin = coordinateOrigin;
246
+ let geospatialOrigin;
247
+ let offsetMode = true;
248
+ if (coordinateSystem === "lnglat-offsets" || coordinateSystem === "meter-offsets") {
249
+ geospatialOrigin = coordinateOrigin;
250
+ } else {
251
+ geospatialOrigin = viewport.isGeospatial ? (
252
+ // @ts-expect-error longitude and latitude are not defined on the base Viewport, but is expected on geospatial viewports
253
+ [Math.fround(viewport.longitude), Math.fround(viewport.latitude), 0]
254
+ ) : null;
255
+ }
256
+ switch (viewport.projectionMode) {
257
+ case PROJECTION_MODE.WEB_MERCATOR:
258
+ if (coordinateSystem === "lnglat" || coordinateSystem === "cartesian") {
259
+ geospatialOrigin = [0, 0, 0];
260
+ offsetMode = false;
261
+ }
262
+ break;
263
+ case PROJECTION_MODE.WEB_MERCATOR_AUTO_OFFSET:
264
+ if (coordinateSystem === "lnglat") {
265
+ shaderCoordinateOrigin = geospatialOrigin;
266
+ } else if (coordinateSystem === "cartesian") {
267
+ shaderCoordinateOrigin = [
268
+ Math.fround(viewport.center[0]),
269
+ Math.fround(viewport.center[1]),
270
+ 0
271
+ ];
272
+ geospatialOrigin = viewport.unprojectPosition(shaderCoordinateOrigin);
273
+ shaderCoordinateOrigin[0] -= coordinateOrigin[0];
274
+ shaderCoordinateOrigin[1] -= coordinateOrigin[1];
275
+ shaderCoordinateOrigin[2] -= coordinateOrigin[2];
276
+ }
277
+ break;
278
+ case PROJECTION_MODE.IDENTITY:
279
+ shaderCoordinateOrigin = viewport.position.map(Math.fround);
280
+ shaderCoordinateOrigin[2] = shaderCoordinateOrigin[2] || 0;
281
+ break;
282
+ case PROJECTION_MODE.GLOBE:
283
+ offsetMode = false;
284
+ geospatialOrigin = null;
285
+ break;
286
+ default:
287
+ offsetMode = false;
288
+ }
289
+ return { geospatialOrigin, shaderCoordinateOrigin, offsetMode };
290
+ }
291
+ function calculateMatrixAndOffset(viewport, coordinateSystem, coordinateOrigin) {
292
+ const { viewMatrixUncentered, projectionMatrix } = viewport;
293
+ let { viewMatrix: viewMatrix2, viewProjectionMatrix } = viewport;
294
+ let projectionCenter = ZERO_VECTOR;
295
+ let originCommon = ZERO_VECTOR;
296
+ let cameraPosCommon = viewport.cameraPosition;
297
+ const { geospatialOrigin, shaderCoordinateOrigin, offsetMode } = getOffsetOrigin(
298
+ viewport,
299
+ coordinateSystem,
300
+ coordinateOrigin
301
+ );
302
+ if (offsetMode) {
303
+ originCommon = viewport.projectPosition(geospatialOrigin || shaderCoordinateOrigin);
304
+ cameraPosCommon = [
305
+ cameraPosCommon[0] - originCommon[0],
306
+ cameraPosCommon[1] - originCommon[1],
307
+ cameraPosCommon[2] - originCommon[2]
308
+ ];
309
+ originCommon[3] = 1;
310
+ projectionCenter = import_core.vec4.transformMat4([], originCommon, viewProjectionMatrix);
311
+ viewMatrix2 = viewMatrixUncentered || viewMatrix2;
312
+ viewProjectionMatrix = import_core.mat4.multiply([], projectionMatrix, viewMatrix2);
313
+ viewProjectionMatrix = import_core.mat4.multiply([], viewProjectionMatrix, VECTOR_TO_POINT_MATRIX);
314
+ }
315
+ return {
316
+ viewMatrix: viewMatrix2,
317
+ viewProjectionMatrix,
318
+ projectionCenter,
319
+ originCommon,
320
+ cameraPosCommon,
321
+ shaderCoordinateOrigin,
322
+ geospatialOrigin
323
+ };
324
+ }
325
+ function getUniformsFromViewport({
326
+ viewport,
327
+ devicePixelRatio = 1,
328
+ modelMatrix = null,
329
+ // Match Layer.defaultProps
330
+ coordinateSystem = "default",
331
+ coordinateOrigin = DEFAULT_COORDINATE_ORIGIN,
332
+ autoWrapLongitude = false
333
+ }) {
334
+ if (coordinateSystem === "default") {
335
+ coordinateSystem = viewport.isGeospatial ? "lnglat" : "cartesian";
336
+ }
337
+ const uniforms = getMemoizedViewportUniforms({
338
+ viewport,
339
+ devicePixelRatio,
340
+ coordinateSystem,
341
+ coordinateOrigin
342
+ });
343
+ uniforms.wrapLongitude = autoWrapLongitude;
344
+ uniforms.modelMatrix = modelMatrix || IDENTITY_MATRIX;
345
+ return uniforms;
346
+ }
347
+ function calculateViewportUniforms({
348
+ viewport,
349
+ devicePixelRatio,
350
+ coordinateSystem,
351
+ coordinateOrigin
352
+ }) {
353
+ const {
354
+ projectionCenter,
355
+ viewProjectionMatrix,
356
+ originCommon,
357
+ cameraPosCommon,
358
+ shaderCoordinateOrigin,
359
+ geospatialOrigin
360
+ } = calculateMatrixAndOffset(viewport, coordinateSystem, coordinateOrigin);
361
+ const distanceScales = viewport.getDistanceScales();
362
+ const viewportSize = [
363
+ viewport.width * devicePixelRatio,
364
+ viewport.height * devicePixelRatio
365
+ ];
366
+ const focalDistance = import_core.vec4.transformMat4([], [0, 0, -viewport.focalDistance, 1], viewport.projectionMatrix)[3] || 1;
367
+ const uniforms = {
368
+ // Projection mode values
369
+ coordinateSystem: getShaderCoordinateSystem(coordinateSystem),
370
+ projectionMode: viewport.projectionMode,
371
+ coordinateOrigin: shaderCoordinateOrigin,
372
+ commonOrigin: originCommon.slice(0, 3),
373
+ center: projectionCenter,
374
+ // Backward compatibility
375
+ // TODO: remove in v9
376
+ // @ts-expect-error _pseudoMeters is only defined on WebMercator viewport
377
+ pseudoMeters: Boolean(viewport._pseudoMeters),
378
+ // Screen size
379
+ viewportSize,
380
+ devicePixelRatio,
381
+ focalDistance,
382
+ commonUnitsPerMeter: distanceScales.unitsPerMeter,
383
+ commonUnitsPerWorldUnit: distanceScales.unitsPerMeter,
384
+ commonUnitsPerWorldUnit2: DEFAULT_PIXELS_PER_UNIT2,
385
+ scale: viewport.scale,
386
+ // This is the mercator scale (2 ** zoom)
387
+ wrapLongitude: false,
388
+ viewProjectionMatrix,
389
+ modelMatrix: IDENTITY_MATRIX,
390
+ // This is for lighting calculations
391
+ cameraPosition: cameraPosCommon
392
+ };
393
+ if (geospatialOrigin) {
394
+ const distanceScalesAtOrigin = viewport.getDistanceScales(geospatialOrigin);
395
+ switch (coordinateSystem) {
396
+ case "meter-offsets":
397
+ uniforms.commonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerMeter;
398
+ uniforms.commonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerMeter2;
399
+ break;
400
+ case "lnglat":
401
+ case "lnglat-offsets":
402
+ if (!viewport._pseudoMeters) {
403
+ uniforms.commonUnitsPerMeter = distanceScalesAtOrigin.unitsPerMeter;
404
+ }
405
+ uniforms.commonUnitsPerWorldUnit = distanceScalesAtOrigin.unitsPerDegree;
406
+ uniforms.commonUnitsPerWorldUnit2 = distanceScalesAtOrigin.unitsPerDegree2;
407
+ break;
408
+ // a.k.a "preprojected" positions
409
+ case "cartesian":
410
+ uniforms.commonUnitsPerWorldUnit = [1, 1, distanceScalesAtOrigin.unitsPerMeter[2]];
411
+ uniforms.commonUnitsPerWorldUnit2 = [0, 0, distanceScalesAtOrigin.unitsPerMeter2[2]];
412
+ break;
413
+ default:
414
+ break;
415
+ }
416
+ }
417
+ if (viewport.projectionMode === PROJECTION_MODE.GLOBE && coordinateSystem === "meter-offsets") {
418
+ const EARTH_RADIUS2 = 6370972;
419
+ const GLOBE_RADIUS2 = 256;
420
+ const lambda = coordinateOrigin[0] * Math.PI / 180;
421
+ const phi = coordinateOrigin[1] * Math.PI / 180;
422
+ const cosPhi = Math.cos(phi);
423
+ const D = ((coordinateOrigin[2] || 0) / EARTH_RADIUS2 + 1) * GLOBE_RADIUS2;
424
+ uniforms.commonOrigin = [
425
+ Math.sin(lambda) * cosPhi * D,
426
+ -Math.cos(lambda) * cosPhi * D,
427
+ Math.sin(phi) * D
428
+ ];
429
+ }
430
+ return uniforms;
431
+ }
432
+
433
+ // ../core/src/shaderlib/project/project.wgsl.ts
434
+ var SHADER_COORDINATE_SYSTEMS = [
435
+ "default",
436
+ "lnglat",
437
+ "meter-offsets",
438
+ "lnglat-offsets",
439
+ "cartesian"
440
+ ];
441
+ var COORDINATE_SYSTEM_WGSL_CONSTANTS = SHADER_COORDINATE_SYSTEMS.map(
442
+ (coordinateSystem) => `const COORDINATE_SYSTEM_${coordinateSystem.toUpperCase().replaceAll("-", "_")}: i32 = ${getShaderCoordinateSystem(coordinateSystem)};`
443
+ ).join("");
444
+ var PROJECTION_MODE_WGSL_CONSTANTS = Object.keys(PROJECTION_MODE).map((key) => `const PROJECTION_MODE_${key}: i32 = ${PROJECTION_MODE[key]};`).join("");
445
+ var UNIT_WGSL_CONSTANTS = Object.keys(UNIT).map((key) => `const UNIT_${key.toUpperCase()}: i32 = ${UNIT[key]};`).join("");
446
+ var projectWGSLHeader = (
447
+ /* wgsl */
448
+ `${COORDINATE_SYSTEM_WGSL_CONSTANTS}
449
+ ${PROJECTION_MODE_WGSL_CONSTANTS}
450
+ ${UNIT_WGSL_CONSTANTS}
451
+
452
+ const TILE_SIZE: f32 = 512.0;
453
+ const PI: f32 = 3.1415926536;
454
+ const WORLD_SCALE: f32 = TILE_SIZE / (PI * 2.0);
455
+ const ZERO_64_LOW: vec3<f32> = vec3<f32>(0.0, 0.0, 0.0);
456
+ const EARTH_RADIUS: f32 = 6370972.0; // meters
457
+ const GLOBE_RADIUS: f32 = 256.0;
458
+
459
+ // -----------------------------------------------------------------------------
460
+ // Uniform block (converted from GLSL uniform block)
461
+ // -----------------------------------------------------------------------------
462
+ struct ProjectUniforms {
463
+ wrapLongitude: i32,
464
+ coordinateSystem: i32,
465
+ commonUnitsPerMeter: vec3<f32>,
466
+ projectionMode: i32,
467
+ scale: f32,
468
+ commonUnitsPerWorldUnit: vec3<f32>,
469
+ commonUnitsPerWorldUnit2: vec3<f32>,
470
+ center: vec4<f32>,
471
+ modelMatrix: mat4x4<f32>,
472
+ viewProjectionMatrix: mat4x4<f32>,
473
+ viewportSize: vec2<f32>,
474
+ devicePixelRatio: f32,
475
+ focalDistance: f32,
476
+ cameraPosition: vec3<f32>,
477
+ coordinateOrigin: vec3<f32>,
478
+ commonOrigin: vec3<f32>,
479
+ pseudoMeters: i32,
480
+ };
481
+
482
+ @group(0) @binding(auto)
483
+ var<uniform> project: ProjectUniforms;
484
+
485
+ // -----------------------------------------------------------------------------
486
+ // Geometry data shared across the project helpers.
487
+ // The active layer shader is responsible for populating this private module
488
+ // state before calling the project functions below.
489
+ // -----------------------------------------------------------------------------
490
+
491
+ // Structure to carry additional geometry data used by deck.gl filters.
492
+ struct Geometry {
493
+ worldPosition: vec3<f32>,
494
+ worldPositionAlt: vec3<f32>,
495
+ position: vec4<f32>,
496
+ normal: vec3<f32>,
497
+ uv: vec2<f32>,
498
+ pickingColor: vec3<f32>,
499
+ };
500
+
501
+ var<private> geometry: Geometry;
502
+ `
503
+ );
504
+ var projectWGSL = (
505
+ /* wgsl */
506
+ `${projectWGSLHeader}
507
+
508
+ // -----------------------------------------------------------------------------
509
+ // Functions
510
+ // -----------------------------------------------------------------------------
511
+
512
+ // Returns an adjustment factor for commonUnitsPerMeter
513
+ fn _project_size_at_latitude(lat: f32) -> f32 {
514
+ let y = clamp(lat, -89.9, 89.9);
515
+ return 1.0 / cos(radians(y));
516
+ }
517
+
518
+ // Overloaded version: scales a value in meters at a given latitude.
519
+ fn _project_size_at_latitude_m(meters: f32, lat: f32) -> f32 {
520
+ return meters * project.commonUnitsPerMeter.z * _project_size_at_latitude(lat);
521
+ }
522
+
523
+ // Computes a non-linear scale factor based on geometry.
524
+ // (Note: This function relies on "geometry" being provided.)
525
+ fn project_size() -> f32 {
526
+ if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR &&
527
+ project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT &&
528
+ project.pseudoMeters == 0) {
529
+ if (geometry.position.w == 0.0) {
530
+ return _project_size_at_latitude(geometry.worldPosition.y);
531
+ }
532
+ let y: f32 = geometry.position.y / TILE_SIZE * 2.0 - 1.0;
533
+ let y2 = y * y;
534
+ let y4 = y2 * y2;
535
+ let y6 = y4 * y2;
536
+ return 1.0 + 4.9348 * y2 + 4.0587 * y4 + 1.5642 * y6;
537
+ }
538
+ return 1.0;
539
+ }
540
+
541
+ // Overloads to scale offsets (meters to world units)
542
+ fn project_size_float(meters: f32) -> f32 {
543
+ return meters * project.commonUnitsPerMeter.z * project_size();
544
+ }
545
+
546
+ fn project_size_vec2(meters: vec2<f32>) -> vec2<f32> {
547
+ return meters * project.commonUnitsPerMeter.xy * project_size();
548
+ }
549
+
550
+ fn project_size_vec3(meters: vec3<f32>) -> vec3<f32> {
551
+ return meters * project.commonUnitsPerMeter * project_size();
552
+ }
553
+
554
+ fn project_size_vec4(meters: vec4<f32>) -> vec4<f32> {
555
+ return vec4<f32>(meters.xyz * project.commonUnitsPerMeter, meters.w);
556
+ }
557
+
558
+ // Returns a rotation matrix aligning the z\u2011axis with the given up vector.
559
+ fn project_get_orientation_matrix(up: vec3<f32>) -> mat3x3<f32> {
560
+ let uz = normalize(up);
561
+ let ux = select(
562
+ vec3<f32>(1.0, 0.0, 0.0),
563
+ normalize(vec3<f32>(uz.y, -uz.x, 0.0)),
564
+ abs(uz.z) == 1.0
565
+ );
566
+ let uy = cross(uz, ux);
567
+ return mat3x3<f32>(ux, uy, uz);
568
+ }
569
+
570
+ // Since WGSL does not support "out" parameters, we return a struct.
571
+ struct RotationResult {
572
+ needsRotation: bool,
573
+ transform: mat3x3<f32>,
574
+ };
575
+
576
+ fn project_needs_rotation(commonPosition: vec3<f32>) -> RotationResult {
577
+ if (project.projectionMode == PROJECTION_MODE_GLOBE) {
578
+ return RotationResult(true, project_get_orientation_matrix(commonPosition));
579
+ } else {
580
+ return RotationResult(false, mat3x3<f32>()); // identity alternative if needed
581
+ };
582
+ }
583
+
584
+ // Projects a normal vector from the current coordinate system to world space.
585
+ fn project_normal(vector: vec3<f32>) -> vec3<f32> {
586
+ let normal_modelspace = project.modelMatrix * vec4<f32>(vector, 0.0);
587
+ var n = normalize(normal_modelspace.xyz * project.commonUnitsPerMeter);
588
+ let rotResult = project_needs_rotation(geometry.position.xyz);
589
+ if (rotResult.needsRotation) {
590
+ n = rotResult.transform * n;
591
+ }
592
+ return n;
593
+ }
594
+
595
+ // Applies a scale offset based on y-offset (dy)
596
+ fn project_offset_(offset: vec4<f32>) -> vec4<f32> {
597
+ let dy: f32 = offset.y;
598
+ let commonUnitsPerWorldUnit = project.commonUnitsPerWorldUnit + project.commonUnitsPerWorldUnit2 * dy;
599
+ return vec4<f32>(offset.xyz * commonUnitsPerWorldUnit, offset.w);
600
+ }
601
+
602
+ // Projects lng/lat coordinates to a unit tile [0,1]
603
+ fn project_mercator_(lnglat: vec2<f32>) -> vec2<f32> {
604
+ var x = lnglat.x;
605
+ if (project.wrapLongitude != 0) {
606
+ x = ((x + 180.0) % 360.0) - 180.0;
607
+ }
608
+ let y = clamp(lnglat.y, -89.9, 89.9);
609
+ return vec2<f32>(
610
+ radians(x) + PI,
611
+ PI + log(tan_fp32(PI * 0.25 + radians(y) * 0.5))
612
+ ) * WORLD_SCALE;
613
+ }
614
+
615
+ // Projects lng/lat/z coordinates for a globe projection.
616
+ fn project_globe_(lnglatz: vec3<f32>) -> vec3<f32> {
617
+ let lambda = radians(lnglatz.x);
618
+ let phi = radians(lnglatz.y);
619
+ let cosPhi = cos(phi);
620
+ let D = (lnglatz.z / EARTH_RADIUS + 1.0) * GLOBE_RADIUS;
621
+ return vec3<f32>(
622
+ sin(lambda) * cosPhi,
623
+ -cos(lambda) * cosPhi,
624
+ sin(phi)
625
+ ) * D;
626
+ }
627
+
628
+ // Projects positions (with an optional 64-bit low part) from the input
629
+ // coordinate system to the common space.
630
+ fn project_position_vec4_f64(position: vec4<f32>, position64Low: vec3<f32>) -> vec4<f32> {
631
+ var position_world = project.modelMatrix * position;
632
+
633
+ // Work around for a Mac+NVIDIA bug:
634
+ if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR) {
635
+ if (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
636
+ return vec4<f32>(
637
+ project_mercator_(position_world.xy),
638
+ _project_size_at_latitude_m(position_world.z, position_world.y),
639
+ position_world.w
640
+ );
641
+ }
642
+ if (project.coordinateSystem == COORDINATE_SYSTEM_CARTESIAN) {
643
+ position_world = vec4f(position_world.xyz + project.coordinateOrigin, position_world.w);
644
+ }
645
+ }
646
+ if (project.projectionMode == PROJECTION_MODE_GLOBE) {
647
+ if (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
648
+ return vec4<f32>(
649
+ project_globe_(position_world.xyz),
650
+ position_world.w
651
+ );
652
+ }
653
+ if (project.coordinateSystem == COORDINATE_SYSTEM_METER_OFFSETS) {
654
+ let enuMatrix = project_get_orientation_matrix(project.commonOrigin);
655
+ let metersToCommon = GLOBE_RADIUS / EARTH_RADIUS;
656
+ let offsetCommon = (enuMatrix * vec3<f32>(-position_world.x, -position_world.y, position_world.z)) * metersToCommon;
657
+ return vec4<f32>(project.commonOrigin + offsetCommon, position_world.w);
658
+ }
659
+ }
660
+ if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET) {
661
+ if (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
662
+ if (abs(position_world.y - project.coordinateOrigin.y) > 0.25) {
663
+ return vec4<f32>(
664
+ project_mercator_(position_world.xy) - project.commonOrigin.xy,
665
+ project_size_float(position_world.z),
666
+ position_world.w
667
+ );
668
+ }
669
+ }
670
+ }
671
+ if (project.projectionMode == PROJECTION_MODE_IDENTITY ||
672
+ (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET &&
673
+ (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT ||
674
+ project.coordinateSystem == COORDINATE_SYSTEM_CARTESIAN))) {
675
+ position_world = vec4f(position_world.xyz - project.coordinateOrigin, position_world.w);
676
+ }
677
+
678
+ return project_offset_(position_world) +
679
+ project_offset_(project.modelMatrix * vec4<f32>(position64Low, 0.0));
680
+ }
681
+
682
+ // Overloaded versions for different input types.
683
+ fn project_position_vec4_f32(position: vec4<f32>) -> vec4<f32> {
684
+ return project_position_vec4_f64(position, ZERO_64_LOW);
685
+ }
686
+
687
+ fn project_position_vec3_f64(position: vec3<f32>, position64Low: vec3<f32>) -> vec3<f32> {
688
+ let projected_position = project_position_vec4_f64(vec4<f32>(position, 1.0), position64Low);
689
+ return projected_position.xyz;
690
+ }
691
+
692
+ fn project_position_vec3_f32(position: vec3<f32>) -> vec3<f32> {
693
+ let projected_position = project_position_vec4_f64(vec4<f32>(position, 1.0), ZERO_64_LOW);
694
+ return projected_position.xyz;
695
+ }
696
+
697
+ fn project_position_vec2_f32(position: vec2<f32>) -> vec2<f32> {
698
+ let projected_position = project_position_vec4_f64(vec4<f32>(position, 0.0, 1.0), ZERO_64_LOW);
699
+ return projected_position.xy;
700
+ }
701
+
702
+ // Transforms a common space position to clip space.
703
+ fn project_common_position_to_clipspace_with_projection(position: vec4<f32>, viewProjectionMatrix: mat4x4<f32>, center: vec4<f32>) -> vec4<f32> {
704
+ var clipPosition = viewProjectionMatrix * position + center;
705
+ // deck.gl projection matrices use WebGL's [-w, w] depth range; WebGPU clips z to [0, w].
706
+ clipPosition.z = (clipPosition.z + clipPosition.w) * 0.5;
707
+ return clipPosition;
708
+ }
709
+
710
+ // Uses the project viewProjectionMatrix and center.
711
+ fn project_common_position_to_clipspace(position: vec4<f32>) -> vec4<f32> {
712
+ return project_common_position_to_clipspace_with_projection(position, project.viewProjectionMatrix, project.center);
713
+ }
714
+
715
+ // Returns a clip space offset corresponding to a given number of screen pixels.
716
+ fn project_pixel_size_to_clipspace(pixels: vec2<f32>) -> vec2<f32> {
717
+ let offset = pixels / project.viewportSize * project.devicePixelRatio * 2.0;
718
+ return offset * project.focalDistance;
719
+ }
720
+
721
+ fn project_meter_size_to_pixel(meters: f32) -> f32 {
722
+ return project_size_float(meters) * project.scale;
723
+ }
724
+
725
+ fn project_unit_size_to_pixel(size: f32, unit: i32) -> f32 {
726
+ if (unit == UNIT_METERS) {
727
+ return project_meter_size_to_pixel(size);
728
+ } else if (unit == UNIT_COMMON) {
729
+ return size * project.scale;
730
+ }
731
+ // UNIT_PIXELS: no scaling applied.
732
+ return size;
733
+ }
734
+
735
+ fn project_pixel_size_float(pixels: f32) -> f32 {
736
+ return pixels / project.scale;
737
+ }
738
+
739
+ fn project_pixel_size_vec2(pixels: vec2<f32>) -> vec2<f32> {
740
+ return pixels / project.scale;
741
+ }
742
+ `
743
+ );
744
+
745
+ // ../core/src/shaderlib/project/project.glsl.ts
746
+ var SHADER_COORDINATE_SYSTEMS2 = [
747
+ "default",
748
+ "lnglat",
749
+ "meter-offsets",
750
+ "lnglat-offsets",
751
+ "cartesian"
752
+ ];
753
+ var COORDINATE_SYSTEM_GLSL_CONSTANTS = SHADER_COORDINATE_SYSTEMS2.map(
754
+ (coordinateSystem) => `const int COORDINATE_SYSTEM_${coordinateSystem.toUpperCase().replaceAll("-", "_")} = ${getShaderCoordinateSystem(coordinateSystem)};`
755
+ ).join("");
756
+ var PROJECTION_MODE_GLSL_CONSTANTS = Object.keys(PROJECTION_MODE).map((key) => `const int PROJECTION_MODE_${key} = ${PROJECTION_MODE[key]};`).join("");
757
+ var UNIT_GLSL_CONSTANTS = Object.keys(UNIT).map((key) => `const int UNIT_${key.toUpperCase()} = ${UNIT[key]};`).join("");
758
+ var projectGLSL = (
759
+ /* glsl */
760
+ `${COORDINATE_SYSTEM_GLSL_CONSTANTS}
761
+ ${PROJECTION_MODE_GLSL_CONSTANTS}
762
+ ${UNIT_GLSL_CONSTANTS}
763
+
764
+ layout(std140) uniform projectUniforms {
765
+ bool wrapLongitude;
766
+ int coordinateSystem;
767
+ vec3 commonUnitsPerMeter;
768
+ int projectionMode;
769
+ float scale;
770
+ vec3 commonUnitsPerWorldUnit;
771
+ vec3 commonUnitsPerWorldUnit2;
772
+ vec4 center;
773
+ mat4 modelMatrix;
774
+ mat4 viewProjectionMatrix;
775
+ vec2 viewportSize;
776
+ float devicePixelRatio;
777
+ float focalDistance;
778
+ vec3 cameraPosition;
779
+ vec3 coordinateOrigin;
780
+ vec3 commonOrigin;
781
+ bool pseudoMeters;
782
+ } project;
783
+
784
+
785
+ const float TILE_SIZE = 512.0;
786
+ const float PI = 3.1415926536;
787
+ const float WORLD_SCALE = TILE_SIZE / (PI * 2.0);
788
+ const vec3 ZERO_64_LOW = vec3(0.0);
789
+ const float EARTH_RADIUS = 6370972.0; // meters
790
+ const float GLOBE_RADIUS = 256.0;
791
+
792
+ // returns an adjustment factor for uCommonUnitsPerMeter
793
+ float project_size_at_latitude(float lat) {
794
+ float y = clamp(lat, -89.9, 89.9);
795
+ return 1.0 / cos(radians(y));
796
+ }
797
+
798
+ float project_size() {
799
+ if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR &&
800
+ project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT &&
801
+ project.pseudoMeters == false) {
802
+
803
+ // uCommonUnitsPerMeter in low-zoom Web Mercator is non-linear
804
+ // Adjust by 1 / cos(latitude)
805
+ // If geometry.position (vertex in common space) is populated, use it
806
+ // Otherwise use geometry.worldPosition (anchor in world space)
807
+
808
+ if (geometry.position.w == 0.0) {
809
+ return project_size_at_latitude(geometry.worldPosition.y);
810
+ }
811
+
812
+ // latitude from common y: 2.0 * (atan(exp(y / TILE_SIZE * 2.0 * PI - PI)) - PI / 4.0)
813
+ // Taylor series of 1 / cos(latitude)
814
+ // Max error < 0.003
815
+
816
+ float y = geometry.position.y / TILE_SIZE * 2.0 - 1.0;
817
+ float y2 = y * y;
818
+ float y4 = y2 * y2;
819
+ float y6 = y4 * y2;
820
+ return 1.0 + 4.9348 * y2 + 4.0587 * y4 + 1.5642 * y6;
821
+ }
822
+ return 1.0;
823
+ }
824
+
825
+ float project_size_at_latitude(float meters, float lat) {
826
+ return meters * project.commonUnitsPerMeter.z * project_size_at_latitude(lat);
827
+ }
828
+
829
+ //
830
+ // Scaling offsets - scales meters to "world distance"
831
+ // Note the scalar version of project_size is for scaling the z component only
832
+ //
833
+ float project_size(float meters) {
834
+ // For scatter relevant
835
+ return meters * project.commonUnitsPerMeter.z * project_size();
836
+ }
837
+
838
+ vec2 project_size(vec2 meters) {
839
+ return meters * project.commonUnitsPerMeter.xy * project_size();
840
+ }
841
+
842
+ vec3 project_size(vec3 meters) {
843
+ return meters * project.commonUnitsPerMeter * project_size();
844
+ }
845
+
846
+ vec4 project_size(vec4 meters) {
847
+ return vec4(meters.xyz * project.commonUnitsPerMeter, meters.w);
848
+ }
849
+
850
+ // Get rotation matrix that aligns the z axis with the given up vector
851
+ // Find 3 unit vectors ux, uy, uz that are perpendicular to each other and uz == up
852
+ mat3 project_get_orientation_matrix(vec3 up) {
853
+ vec3 uz = normalize(up);
854
+ // Tangent on XY plane
855
+ vec3 ux = abs(uz.z) == 1.0 ? vec3(1.0, 0.0, 0.0) : normalize(vec3(uz.y, -uz.x, 0));
856
+ vec3 uy = cross(uz, ux);
857
+ return mat3(ux, uy, uz);
858
+ }
859
+
860
+ bool project_needs_rotation(vec3 commonPosition, out mat3 transform) {
861
+ if (project.projectionMode == PROJECTION_MODE_GLOBE) {
862
+ transform = project_get_orientation_matrix(commonPosition);
863
+ return true;
864
+ }
865
+ return false;
866
+ }
867
+
868
+ //
869
+ // Projecting normal - transform deltas from current coordinate system to
870
+ // normals in the worldspace
871
+ //
872
+ vec3 project_normal(vec3 vector) {
873
+ // Apply model matrix
874
+ vec4 normal_modelspace = project.modelMatrix * vec4(vector, 0.0);
875
+ vec3 n = normalize(normal_modelspace.xyz * project.commonUnitsPerMeter);
876
+ mat3 rotation;
877
+ if (project_needs_rotation(geometry.position.xyz, rotation)) {
878
+ n = rotation * n;
879
+ }
880
+ return n;
881
+ }
882
+
883
+ vec4 project_offset_(vec4 offset) {
884
+ float dy = offset.y;
885
+ vec3 commonUnitsPerWorldUnit = project.commonUnitsPerWorldUnit + project.commonUnitsPerWorldUnit2 * dy;
886
+ return vec4(offset.xyz * commonUnitsPerWorldUnit, offset.w);
887
+ }
888
+
889
+ //
890
+ // Projecting positions - non-linear projection: lnglats => unit tile [0-1, 0-1]
891
+ //
892
+ vec2 project_mercator_(vec2 lnglat) {
893
+ float x = lnglat.x;
894
+ if (project.wrapLongitude) {
895
+ x = mod(x + 180., 360.0) - 180.;
896
+ }
897
+ float y = clamp(lnglat.y, -89.9, 89.9);
898
+ return vec2(
899
+ radians(x) + PI,
900
+ PI + log(tan_fp32(PI * 0.25 + radians(y) * 0.5))
901
+ ) * WORLD_SCALE;
902
+ }
903
+
904
+ vec3 project_globe_(vec3 lnglatz) {
905
+ float lambda = radians(lnglatz.x);
906
+ float phi = radians(lnglatz.y);
907
+ float cosPhi = cos(phi);
908
+ float D = (lnglatz.z / EARTH_RADIUS + 1.0) * GLOBE_RADIUS;
909
+
910
+ return vec3(
911
+ sin(lambda) * cosPhi,
912
+ -cos(lambda) * cosPhi,
913
+ sin(phi)
914
+ ) * D;
915
+ }
916
+
917
+ //
918
+ // Projects positions (defined by project.coordinateSystem) to common space (defined by project.projectionMode)
919
+ //
920
+ vec4 project_position(vec4 position, vec3 position64Low) {
921
+ vec4 position_world = project.modelMatrix * position;
922
+
923
+ // Work around for a Mac+NVIDIA bug https://github.com/visgl/deck.gl/issues/4145
924
+ if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR) {
925
+ if (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
926
+ return vec4(
927
+ project_mercator_(position_world.xy),
928
+ project_size_at_latitude(position_world.z, position_world.y),
929
+ position_world.w
930
+ );
931
+ }
932
+ if (project.coordinateSystem == COORDINATE_SYSTEM_CARTESIAN) {
933
+ position_world.xyz += project.coordinateOrigin;
934
+ }
935
+ }
936
+ if (project.projectionMode == PROJECTION_MODE_GLOBE) {
937
+ if (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
938
+ return vec4(
939
+ project_globe_(position_world.xyz),
940
+ position_world.w
941
+ );
942
+ }
943
+ if (project.coordinateSystem == COORDINATE_SYSTEM_METER_OFFSETS) {
944
+ mat3 enuMatrix = project_get_orientation_matrix(project.commonOrigin);
945
+ float metersToCommon = GLOBE_RADIUS / EARTH_RADIUS;
946
+ vec3 offsetCommon = (enuMatrix * vec3(-position_world.xy, position_world.z)) * metersToCommon;
947
+ return vec4(project.commonOrigin + offsetCommon, position_world.w);
948
+ }
949
+ }
950
+ if (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET) {
951
+ if (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT) {
952
+ if (abs(position_world.y - project.coordinateOrigin.y) > 0.25) {
953
+ // Too far from the projection center for offset mode to be accurate
954
+ // Only use high parts
955
+ return vec4(
956
+ project_mercator_(position_world.xy) - project.commonOrigin.xy,
957
+ project_size(position_world.z),
958
+ position_world.w
959
+ );
960
+ }
961
+ }
962
+ }
963
+ if (project.projectionMode == PROJECTION_MODE_IDENTITY ||
964
+ (project.projectionMode == PROJECTION_MODE_WEB_MERCATOR_AUTO_OFFSET &&
965
+ (project.coordinateSystem == COORDINATE_SYSTEM_LNGLAT ||
966
+ project.coordinateSystem == COORDINATE_SYSTEM_CARTESIAN))) {
967
+ // Subtract high part of 64 bit value. Convert remainder to float32, preserving precision.
968
+ position_world.xyz -= project.coordinateOrigin;
969
+ }
970
+
971
+ // Translation is already added to the high parts
972
+ return project_offset_(position_world) + project_offset_(project.modelMatrix * vec4(position64Low, 0.0));
973
+ }
974
+
975
+ vec4 project_position(vec4 position) {
976
+ return project_position(position, ZERO_64_LOW);
977
+ }
978
+
979
+ vec3 project_position(vec3 position, vec3 position64Low) {
980
+ vec4 projected_position = project_position(vec4(position, 1.0), position64Low);
981
+ return projected_position.xyz;
982
+ }
983
+
984
+ vec3 project_position(vec3 position) {
985
+ vec4 projected_position = project_position(vec4(position, 1.0), ZERO_64_LOW);
986
+ return projected_position.xyz;
987
+ }
988
+
989
+ vec2 project_position(vec2 position) {
990
+ vec4 projected_position = project_position(vec4(position, 0.0, 1.0), ZERO_64_LOW);
991
+ return projected_position.xy;
992
+ }
993
+
994
+ vec4 project_common_position_to_clipspace(vec4 position, mat4 viewProjectionMatrix, vec4 center) {
995
+ return viewProjectionMatrix * position + center;
996
+ }
997
+
998
+ //
999
+ // Projects from common space coordinates to clip space.
1000
+ // Uses project.viewProjectionMatrix
1001
+ //
1002
+ vec4 project_common_position_to_clipspace(vec4 position) {
1003
+ return project_common_position_to_clipspace(position, project.viewProjectionMatrix, project.center);
1004
+ }
1005
+
1006
+ // Returns a clip space offset that corresponds to a given number of screen pixels
1007
+ vec2 project_pixel_size_to_clipspace(vec2 pixels) {
1008
+ vec2 offset = pixels / project.viewportSize * project.devicePixelRatio * 2.0;
1009
+ return offset * project.focalDistance;
1010
+ }
1011
+
1012
+ float project_size_to_pixel(float meters) {
1013
+ return project_size(meters) * project.scale;
1014
+ }
1015
+ vec2 project_size_to_pixel(vec2 meters) {
1016
+ return project_size(meters) * project.scale;
1017
+ }
1018
+ float project_size_to_pixel(float size, int unit) {
1019
+ if (unit == UNIT_METERS) return project_size_to_pixel(size);
1020
+ if (unit == UNIT_COMMON) return size * project.scale;
1021
+ // UNIT_PIXELS
1022
+ return size;
1023
+ }
1024
+ float project_pixel_size(float pixels) {
1025
+ return pixels / project.scale;
1026
+ }
1027
+ vec2 project_pixel_size(vec2 pixels) {
1028
+ return pixels / project.scale;
1029
+ }
1030
+ `
1031
+ );
1032
+
1033
+ // ../core/src/shaderlib/project/project.ts
1034
+ var INITIAL_MODULE_OPTIONS = {};
1035
+ function getUniforms(opts = INITIAL_MODULE_OPTIONS) {
1036
+ if ("viewport" in opts) {
1037
+ return getUniformsFromViewport(opts);
1038
+ }
1039
+ return {};
1040
+ }
1041
+ var project_default = {
1042
+ name: "project",
1043
+ dependencies: [import_shadertools.fp32, geometry_default],
1044
+ source: projectWGSL,
1045
+ vs: projectGLSL,
1046
+ getUniforms,
1047
+ uniformTypes: {
1048
+ wrapLongitude: "f32",
1049
+ coordinateSystem: "i32",
1050
+ commonUnitsPerMeter: "vec3<f32>",
1051
+ projectionMode: "i32",
1052
+ scale: "f32",
1053
+ commonUnitsPerWorldUnit: "vec3<f32>",
1054
+ commonUnitsPerWorldUnit2: "vec3<f32>",
1055
+ center: "vec4<f32>",
1056
+ modelMatrix: "mat4x4<f32>",
1057
+ viewProjectionMatrix: "mat4x4<f32>",
1058
+ viewportSize: "vec2<f32>",
1059
+ devicePixelRatio: "f32",
1060
+ focalDistance: "f32",
1061
+ cameraPosition: "vec3<f32>",
1062
+ coordinateOrigin: "vec3<f32>",
1063
+ commonOrigin: "vec3<f32>",
1064
+ pseudoMeters: "f32"
1065
+ }
1066
+ // @ts-ignore TODO v9.1
1067
+ };
1068
+
1069
+ // ../core/src/passes/pass.ts
1070
+ var Pass = class {
1071
+ /** Create a new Pass instance */
1072
+ constructor(device, props = { id: "pass" }) {
1073
+ const { id } = props;
1074
+ this.id = id;
1075
+ this.device = device;
1076
+ this.props = { ...props };
1077
+ }
1078
+ setProps(props) {
1079
+ Object.assign(this.props, props);
1080
+ }
1081
+ render(params) {
1082
+ }
1083
+ // eslint-disable-line @typescript-eslint/no-empty-function
1084
+ cleanup() {
1085
+ }
1086
+ // eslint-disable-line @typescript-eslint/no-empty-function
1087
+ };
1088
+
1089
+ // ../core/src/passes/layers-pass.ts
1090
+ var WEBGPU_DEFAULT_DRAW_PARAMETERS = {
1091
+ depthWriteEnabled: true,
1092
+ depthCompare: "less-equal",
1093
+ blendColorOperation: "add",
1094
+ blendColorSrcFactor: "one",
1095
+ blendColorDstFactor: "one-minus-src-alpha",
1096
+ blendAlphaOperation: "add",
1097
+ blendAlphaSrcFactor: "one",
1098
+ blendAlphaDstFactor: "one-minus-src-alpha"
1099
+ };
1100
+ var LayersPass = class extends Pass {
1101
+ constructor() {
1102
+ super(...arguments);
1103
+ this._lastRenderIndex = -1;
1104
+ }
1105
+ render(options) {
1106
+ this._render(options);
1107
+ }
1108
+ _render(options) {
1109
+ const { canvasContext = this.device.canvasContext } = options;
1110
+ const framebuffer = options.target ?? canvasContext.getCurrentFramebuffer();
1111
+ const [width, height] = canvasContext.getDrawingBufferSize();
1112
+ const clearCanvas = options.clearCanvas ?? true;
1113
+ let clearColor = options.clearColor ?? (clearCanvas ? [0, 0, 0, 0] : false);
1114
+ let clearDepth = clearCanvas ? 1 : false;
1115
+ let clearStencil = clearCanvas ? 0 : false;
1116
+ const colorMask = options.colorMask ?? 15;
1117
+ const parameters2 = { viewport: [0, 0, width, height] };
1118
+ if (options.colorMask) {
1119
+ parameters2.colorMask = colorMask;
1120
+ }
1121
+ if (options.scissorRect) {
1122
+ parameters2.scissorRect = options.scissorRect;
1123
+ }
1124
+ const { shaderModuleProps, viewports, views, onViewportActive, clearStack = true } = options;
1125
+ const pass = options.pass || "unknown";
1126
+ const submitEachRenderPass = this.device.type === "webgpu";
1127
+ if (clearStack) {
1128
+ this._lastRenderIndex = -1;
1129
+ }
1130
+ const renderStats = [];
1131
+ if (!viewports.length) {
1132
+ const renderPass = this.device.beginRenderPass({
1133
+ framebuffer,
1134
+ parameters: parameters2,
1135
+ clearColor,
1136
+ clearDepth,
1137
+ clearStencil
1138
+ });
1139
+ renderPass.end();
1140
+ this.device.submit();
1141
+ return renderStats;
1142
+ }
1143
+ try {
1144
+ for (const viewport of viewports) {
1145
+ onViewportActive == null ? void 0 : onViewportActive(viewport);
1146
+ const drawLayerParams = this._getDrawLayerParams(viewport, options);
1147
+ const view = views && views[viewport.id];
1148
+ const subViewports = viewport.subViewports || [viewport];
1149
+ const renderGroups = submitEachRenderPass ? subViewports.map((subViewport) => [subViewport]) : [subViewports];
1150
+ for (const renderGroup of renderGroups) {
1151
+ const renderPass = this.device.beginRenderPass({
1152
+ framebuffer,
1153
+ parameters: parameters2,
1154
+ clearColor,
1155
+ clearDepth,
1156
+ clearStencil
1157
+ });
1158
+ try {
1159
+ for (const subViewport of renderGroup) {
1160
+ const stats = this._drawLayersInViewport(
1161
+ renderPass,
1162
+ {
1163
+ target: framebuffer,
1164
+ canvasContext,
1165
+ shaderModuleProps,
1166
+ viewport: subViewport,
1167
+ view,
1168
+ pass,
1169
+ layers: options.layers,
1170
+ isPicking: options.isPicking
1171
+ },
1172
+ drawLayerParams
1173
+ );
1174
+ renderStats.push(stats);
1175
+ }
1176
+ } finally {
1177
+ renderPass.end();
1178
+ if (submitEachRenderPass) {
1179
+ this.device.submit();
1180
+ }
1181
+ }
1182
+ clearColor = false;
1183
+ clearDepth = false;
1184
+ clearStencil = false;
1185
+ }
1186
+ }
1187
+ return renderStats;
1188
+ } finally {
1189
+ if (!submitEachRenderPass) {
1190
+ this.device.submit();
1191
+ }
1192
+ }
1193
+ }
1194
+ // When a viewport contains multiple subviewports (e.g. repeated web mercator map),
1195
+ // this is only done once for the parent viewport
1196
+ /* Resolve the parameters needed to draw each layer */
1197
+ _getDrawLayerParams(viewport, {
1198
+ layers,
1199
+ pass,
1200
+ isPicking = false,
1201
+ layerFilter,
1202
+ cullRect,
1203
+ views,
1204
+ effects,
1205
+ canvasContext = this.device.canvasContext,
1206
+ shaderModuleProps
1207
+ }, evaluateShouldDrawOnly = false) {
1208
+ var _a, _b;
1209
+ const drawLayerParams = [];
1210
+ const indexResolver = layerIndexResolver(this._lastRenderIndex + 1);
1211
+ const drawContext = {
1212
+ layer: layers[0],
1213
+ viewport,
1214
+ isPicking,
1215
+ renderPass: pass,
1216
+ cullRect
1217
+ };
1218
+ const layerFilterCache = {};
1219
+ for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) {
1220
+ const layer = layers[layerIndex];
1221
+ const shouldDrawLayer = this._shouldDrawLayer(
1222
+ layer,
1223
+ drawContext,
1224
+ layerFilter,
1225
+ layerFilterCache
1226
+ );
1227
+ const layerParam = { shouldDrawLayer };
1228
+ if (shouldDrawLayer && !evaluateShouldDrawOnly) {
1229
+ layerParam.shouldDrawLayer = true;
1230
+ layerParam.layerRenderIndex = indexResolver(layer, shouldDrawLayer);
1231
+ layerParam.shaderModuleProps = this._getShaderModuleProps(
1232
+ layer,
1233
+ effects,
1234
+ pass,
1235
+ canvasContext,
1236
+ shaderModuleProps
1237
+ );
1238
+ const defaultParams = layer.context.device.type === "webgpu" ? WEBGPU_DEFAULT_DRAW_PARAMETERS : null;
1239
+ layerParam.layerParameters = {
1240
+ ...defaultParams,
1241
+ ...(_a = layer.context.deck) == null ? void 0 : _a.props.parameters,
1242
+ ...(_b = views == null ? void 0 : views[viewport.id]) == null ? void 0 : _b.props.parameters,
1243
+ ...this.getLayerParameters(layer, layerIndex, viewport)
1244
+ };
1245
+ }
1246
+ drawLayerParams[layerIndex] = layerParam;
1247
+ }
1248
+ return drawLayerParams;
1249
+ }
1250
+ // Draws a list of layers in one viewport
1251
+ // TODO - when picking we could completely skip rendering viewports that dont
1252
+ // intersect with the picking rect
1253
+ /* eslint-disable max-depth, max-statements, complexity */
1254
+ _drawLayersInViewport(renderPass, {
1255
+ layers,
1256
+ shaderModuleProps: globalModuleParameters,
1257
+ pass,
1258
+ target,
1259
+ canvasContext,
1260
+ viewport,
1261
+ view,
1262
+ isPicking
1263
+ }, drawLayerParams) {
1264
+ const glViewport = getGLViewport(this.device, {
1265
+ canvasContext,
1266
+ shaderModuleProps: globalModuleParameters,
1267
+ target,
1268
+ viewport
1269
+ });
1270
+ if (view) {
1271
+ const { clear, clearColor, clearDepth, clearStencil } = view.props;
1272
+ if (clear) {
1273
+ let colorToUse = [0, 0, 0, 0];
1274
+ let depthToUse = 1;
1275
+ let stencilToUse = 0;
1276
+ if (Array.isArray(clearColor) && !isPicking) {
1277
+ colorToUse = [...clearColor.slice(0, 3), clearColor[3] || 255].map(
1278
+ (c) => c / 255
1279
+ );
1280
+ } else if (clearColor === false) {
1281
+ colorToUse = false;
1282
+ }
1283
+ if (clearDepth !== void 0) {
1284
+ depthToUse = clearDepth;
1285
+ }
1286
+ if (clearStencil !== void 0) {
1287
+ stencilToUse = clearStencil;
1288
+ }
1289
+ const clearRenderPass = this.device.beginRenderPass({
1290
+ framebuffer: target,
1291
+ parameters: {
1292
+ viewport: glViewport,
1293
+ scissorRect: glViewport
1294
+ },
1295
+ clearColor: colorToUse,
1296
+ clearDepth: depthToUse,
1297
+ clearStencil: stencilToUse
1298
+ });
1299
+ clearRenderPass.end();
1300
+ }
1301
+ }
1302
+ const renderStatus = {
1303
+ totalCount: layers.length,
1304
+ visibleCount: 0,
1305
+ compositeCount: 0,
1306
+ pickableCount: 0
1307
+ };
1308
+ renderPass.setParameters({ viewport: glViewport });
1309
+ for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) {
1310
+ const layer = layers[layerIndex];
1311
+ const drawLayerParameters = drawLayerParams[layerIndex];
1312
+ const { shouldDrawLayer } = drawLayerParameters;
1313
+ if (shouldDrawLayer && layer.props.pickable) {
1314
+ renderStatus.pickableCount++;
1315
+ }
1316
+ if (layer.isComposite) {
1317
+ renderStatus.compositeCount++;
1318
+ }
1319
+ if (layer.isDrawable && drawLayerParameters.shouldDrawLayer) {
1320
+ const { layerRenderIndex, shaderModuleProps, layerParameters } = drawLayerParameters;
1321
+ renderStatus.visibleCount++;
1322
+ this._lastRenderIndex = Math.max(this._lastRenderIndex, layerRenderIndex);
1323
+ if (shaderModuleProps.project) {
1324
+ shaderModuleProps.project.viewport = viewport;
1325
+ }
1326
+ layer.context.renderPass = renderPass;
1327
+ try {
1328
+ layer._drawLayer({
1329
+ renderPass,
1330
+ shaderModuleProps,
1331
+ uniforms: { layerIndex: layerRenderIndex },
1332
+ parameters: layerParameters
1333
+ });
1334
+ } catch (err) {
1335
+ layer.raiseError(err, `drawing ${layer} to ${pass}`);
1336
+ }
1337
+ }
1338
+ }
1339
+ return renderStatus;
1340
+ }
1341
+ /* eslint-enable max-depth, max-statements */
1342
+ /* Methods for subclass overrides */
1343
+ shouldDrawLayer(layer) {
1344
+ return true;
1345
+ }
1346
+ getShaderModuleProps(layer, effects, otherShaderModuleProps) {
1347
+ return null;
1348
+ }
1349
+ getLayerParameters(layer, layerIndex, viewport) {
1350
+ return layer.props.parameters;
1351
+ }
1352
+ /* Private */
1353
+ _shouldDrawLayer(layer, drawContext, layerFilter, layerFilterCache) {
1354
+ const shouldDrawLayer = layer.props.visible && this.shouldDrawLayer(layer);
1355
+ if (!shouldDrawLayer) {
1356
+ return false;
1357
+ }
1358
+ drawContext.layer = layer;
1359
+ let parent = layer.parent;
1360
+ while (parent) {
1361
+ if (!parent.props.visible || !parent.filterSubLayer(drawContext)) {
1362
+ return false;
1363
+ }
1364
+ drawContext.layer = parent;
1365
+ parent = parent.parent;
1366
+ }
1367
+ if (layerFilter) {
1368
+ const rootLayerId = drawContext.layer.id;
1369
+ if (!(rootLayerId in layerFilterCache)) {
1370
+ layerFilterCache[rootLayerId] = layerFilter(drawContext);
1371
+ }
1372
+ if (!layerFilterCache[rootLayerId]) {
1373
+ return false;
1374
+ }
1375
+ }
1376
+ layer.activateViewport(drawContext.viewport);
1377
+ return true;
1378
+ }
1379
+ _getShaderModuleProps(layer, effects, pass, canvasContext, overrides) {
1380
+ var _a, _b;
1381
+ const devicePixelRatio = canvasContext.cssToDeviceRatio();
1382
+ const layerProps = ((_a = layer.internalState) == null ? void 0 : _a.propsInTransition) || layer.props;
1383
+ const shaderModuleProps = {
1384
+ layer: layerProps,
1385
+ picking: {
1386
+ isActive: false
1387
+ },
1388
+ project: {
1389
+ viewport: layer.context.viewport,
1390
+ devicePixelRatio,
1391
+ modelMatrix: layerProps.modelMatrix,
1392
+ coordinateSystem: layerProps.coordinateSystem,
1393
+ coordinateOrigin: layerProps.coordinateOrigin,
1394
+ autoWrapLongitude: layer.wrapLongitude
1395
+ }
1396
+ };
1397
+ if (effects) {
1398
+ for (const effect of effects) {
1399
+ mergeModuleParameters(
1400
+ shaderModuleProps,
1401
+ (_b = effect.getShaderModuleProps) == null ? void 0 : _b.call(effect, layer, shaderModuleProps)
1402
+ );
1403
+ }
1404
+ }
1405
+ for (const module2 of layer.context.defaultShaderModules) {
1406
+ if (!(module2.name in shaderModuleProps)) {
1407
+ shaderModuleProps[module2.name] = {};
1408
+ }
1409
+ }
1410
+ return mergeModuleParameters(
1411
+ shaderModuleProps,
1412
+ this.getShaderModuleProps(layer, effects, shaderModuleProps),
1413
+ overrides
1414
+ );
1415
+ }
1416
+ };
1417
+ function layerIndexResolver(startIndex = 0, layerIndices = {}) {
1418
+ const resolvers = {};
1419
+ const resolveLayerIndex = (layer, isDrawn) => {
1420
+ const indexOverride = layer.props._offset;
1421
+ const layerId = layer.id;
1422
+ const parentId = layer.parent && layer.parent.id;
1423
+ let index;
1424
+ if (parentId && !(parentId in layerIndices)) {
1425
+ resolveLayerIndex(layer.parent, false);
1426
+ }
1427
+ if (parentId in resolvers) {
1428
+ const resolver = resolvers[parentId] = resolvers[parentId] || layerIndexResolver(layerIndices[parentId], layerIndices);
1429
+ index = resolver(layer, isDrawn);
1430
+ resolvers[layerId] = resolver;
1431
+ } else if (Number.isFinite(indexOverride)) {
1432
+ index = indexOverride + (layerIndices[parentId] || 0);
1433
+ resolvers[layerId] = null;
1434
+ } else {
1435
+ index = startIndex;
1436
+ }
1437
+ if (isDrawn && index >= startIndex) {
1438
+ startIndex = index + 1;
1439
+ }
1440
+ layerIndices[layerId] = index;
1441
+ return index;
1442
+ };
1443
+ return resolveLayerIndex;
1444
+ }
1445
+ function getGLViewport(device, {
1446
+ canvasContext = device.canvasContext,
1447
+ shaderModuleProps,
1448
+ target,
1449
+ viewport
1450
+ }) {
1451
+ var _a;
1452
+ const pixelRatio = ((_a = shaderModuleProps == null ? void 0 : shaderModuleProps.project) == null ? void 0 : _a.devicePixelRatio) ?? canvasContext.cssToDeviceRatio();
1453
+ const [, drawingBufferHeight] = canvasContext.getDrawingBufferSize();
1454
+ const height = target ? target.height : drawingBufferHeight;
1455
+ const dimensions = viewport;
1456
+ return [
1457
+ dimensions.x * pixelRatio,
1458
+ height - (dimensions.y + dimensions.height) * pixelRatio,
1459
+ dimensions.width * pixelRatio,
1460
+ dimensions.height * pixelRatio
1461
+ ];
1462
+ }
1463
+ function mergeModuleParameters(target, ...sources) {
1464
+ for (const source2 of sources) {
1465
+ if (source2) {
1466
+ for (const key in source2) {
1467
+ if (target[key]) {
1468
+ Object.assign(target[key], source2[key]);
1469
+ } else {
1470
+ target[key] = source2[key];
1471
+ }
1472
+ }
1473
+ }
1474
+ }
1475
+ return target;
1476
+ }
1477
+
1478
+ // ../core/src/utils/math-utils.ts
1479
+ var import_core2 = require("@math.gl/core");
1480
+ function createMat4() {
1481
+ return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
1482
+ }
1483
+ function mod(value, divisor) {
1484
+ const modulus = value % divisor;
1485
+ return modulus < 0 ? divisor + modulus : modulus;
1486
+ }
1487
+ function getCameraPosition(viewMatrixInverse) {
1488
+ return [viewMatrixInverse[12], viewMatrixInverse[13], viewMatrixInverse[14]];
1489
+ }
1490
+ function getFrustumPlanes(viewProjectionMatrix) {
1491
+ return {
1492
+ left: getFrustumPlane(
1493
+ viewProjectionMatrix[3] + viewProjectionMatrix[0],
1494
+ viewProjectionMatrix[7] + viewProjectionMatrix[4],
1495
+ viewProjectionMatrix[11] + viewProjectionMatrix[8],
1496
+ viewProjectionMatrix[15] + viewProjectionMatrix[12]
1497
+ ),
1498
+ right: getFrustumPlane(
1499
+ viewProjectionMatrix[3] - viewProjectionMatrix[0],
1500
+ viewProjectionMatrix[7] - viewProjectionMatrix[4],
1501
+ viewProjectionMatrix[11] - viewProjectionMatrix[8],
1502
+ viewProjectionMatrix[15] - viewProjectionMatrix[12]
1503
+ ),
1504
+ bottom: getFrustumPlane(
1505
+ viewProjectionMatrix[3] + viewProjectionMatrix[1],
1506
+ viewProjectionMatrix[7] + viewProjectionMatrix[5],
1507
+ viewProjectionMatrix[11] + viewProjectionMatrix[9],
1508
+ viewProjectionMatrix[15] + viewProjectionMatrix[13]
1509
+ ),
1510
+ top: getFrustumPlane(
1511
+ viewProjectionMatrix[3] - viewProjectionMatrix[1],
1512
+ viewProjectionMatrix[7] - viewProjectionMatrix[5],
1513
+ viewProjectionMatrix[11] - viewProjectionMatrix[9],
1514
+ viewProjectionMatrix[15] - viewProjectionMatrix[13]
1515
+ ),
1516
+ near: getFrustumPlane(
1517
+ viewProjectionMatrix[3] + viewProjectionMatrix[2],
1518
+ viewProjectionMatrix[7] + viewProjectionMatrix[6],
1519
+ viewProjectionMatrix[11] + viewProjectionMatrix[10],
1520
+ viewProjectionMatrix[15] + viewProjectionMatrix[14]
1521
+ ),
1522
+ far: getFrustumPlane(
1523
+ viewProjectionMatrix[3] - viewProjectionMatrix[2],
1524
+ viewProjectionMatrix[7] - viewProjectionMatrix[6],
1525
+ viewProjectionMatrix[11] - viewProjectionMatrix[10],
1526
+ viewProjectionMatrix[15] - viewProjectionMatrix[14]
1527
+ )
1528
+ };
1529
+ }
1530
+ var scratchVector = new import_core2.Vector3();
1531
+ function getFrustumPlane(a, b, c, d) {
1532
+ scratchVector.set(a, b, c);
1533
+ const L = scratchVector.len();
1534
+ return { distance: d / L, normal: new import_core2.Vector3(-a / L, -b / L, -c / L) };
1535
+ }
1536
+ function fp64LowPart(x) {
1537
+ return x - Math.fround(x);
1538
+ }
1539
+
1540
+ // ../core/src/viewports/viewport.ts
1541
+ var import_core3 = require("@math.gl/core");
1542
+ var import_web_mercator = require("@math.gl/web-mercator");
1543
+ var DEGREES_TO_RADIANS = Math.PI / 180;
1544
+ var IDENTITY = createMat4();
1545
+ var ZERO_VECTOR2 = [0, 0, 0];
1546
+ var DEFAULT_DISTANCE_SCALES = {
1547
+ unitsPerMeter: [1, 1, 1],
1548
+ metersPerUnit: [1, 1, 1]
1549
+ };
1550
+ function createProjectionMatrix({
1551
+ width,
1552
+ height,
1553
+ orthographic,
1554
+ fovyRadians,
1555
+ focalDistance,
1556
+ padding,
1557
+ near,
1558
+ far
1559
+ }) {
1560
+ const aspect = width / height;
1561
+ const matrix = orthographic ? new import_core3.Matrix4().orthographic({ fovy: fovyRadians, aspect, focalDistance, near, far }) : new import_core3.Matrix4().perspective({ fovy: fovyRadians, aspect, near, far });
1562
+ if (padding) {
1563
+ const { left = 0, right = 0, top = 0, bottom = 0 } = padding;
1564
+ const offsetX = (0, import_core3.clamp)((left + width - right) / 2, 0, width) - width / 2;
1565
+ const offsetY = (0, import_core3.clamp)((top + height - bottom) / 2, 0, height) - height / 2;
1566
+ matrix[8] -= offsetX * 2 / width;
1567
+ matrix[9] += offsetY * 2 / height;
1568
+ }
1569
+ return matrix;
1570
+ }
1571
+ var _Viewport = class _Viewport {
1572
+ // eslint-disable-next-line complexity
1573
+ constructor(opts = {}) {
1574
+ this._frustumPlanes = {};
1575
+ this.id = opts.id || this.constructor.displayName || "viewport";
1576
+ this.x = opts.x || 0;
1577
+ this.y = opts.y || 0;
1578
+ this.width = opts.width || 1;
1579
+ this.height = opts.height || 1;
1580
+ this.zoom = opts.zoom || 0;
1581
+ this.padding = opts.padding;
1582
+ this.distanceScales = opts.distanceScales || DEFAULT_DISTANCE_SCALES;
1583
+ this.focalDistance = opts.focalDistance || 1;
1584
+ this.position = opts.position || ZERO_VECTOR2;
1585
+ this.modelMatrix = opts.modelMatrix || null;
1586
+ const { longitude, latitude } = opts;
1587
+ this.isGeospatial = Number.isFinite(latitude) && Number.isFinite(longitude);
1588
+ this._initProps(opts);
1589
+ this._initMatrices(opts);
1590
+ this.equals = this.equals.bind(this);
1591
+ this.project = this.project.bind(this);
1592
+ this.unproject = this.unproject.bind(this);
1593
+ this.projectPosition = this.projectPosition.bind(this);
1594
+ this.unprojectPosition = this.unprojectPosition.bind(this);
1595
+ this.projectFlat = this.projectFlat.bind(this);
1596
+ this.unprojectFlat = this.unprojectFlat.bind(this);
1597
+ }
1598
+ get subViewports() {
1599
+ return null;
1600
+ }
1601
+ get metersPerPixel() {
1602
+ return this.distanceScales.metersPerUnit[2] / this.scale;
1603
+ }
1604
+ get projectionMode() {
1605
+ if (this.isGeospatial) {
1606
+ return this.zoom < 12 ? PROJECTION_MODE.WEB_MERCATOR : PROJECTION_MODE.WEB_MERCATOR_AUTO_OFFSET;
1607
+ }
1608
+ return PROJECTION_MODE.IDENTITY;
1609
+ }
1610
+ // Two viewports are equal if their dimensions, common-space scale, and
1611
+ // view and projection matrices are (approximately) equal.
1612
+ equals(viewport) {
1613
+ if (!(viewport instanceof _Viewport)) {
1614
+ return false;
1615
+ }
1616
+ if (this === viewport) {
1617
+ return true;
1618
+ }
1619
+ return viewport.width === this.width && viewport.height === this.height && viewport.scale === this.scale && viewport.projectionMode === this.projectionMode && viewport.resolution === this.resolution && (0, import_core3.equals)(viewport.distanceScales.unitsPerMeter, this.distanceScales.unitsPerMeter) && (0, import_core3.equals)(viewport.projectionMatrix, this.projectionMatrix) && (0, import_core3.equals)(viewport.viewMatrix, this.viewMatrix);
1620
+ }
1621
+ /**
1622
+ * Projects xyz (possibly latitude and longitude) to pixel coordinates in window
1623
+ * using viewport projection parameters
1624
+ * - [longitude, latitude] to [x, y]
1625
+ * - [longitude, latitude, Z] => [x, y, z]
1626
+ * Note: By default, returns top-left coordinates for canvas/SVG type render
1627
+ *
1628
+ * @param {Array} lngLatZ - [lng, lat] or [lng, lat, Z]
1629
+ * @param {Object} opts - options
1630
+ * @param {Object} opts.topLeft=true - Whether projected coords are top left
1631
+ * @return {Array} - [x, y] or [x, y, z] in top left coords
1632
+ */
1633
+ project(xyz, { topLeft = true } = {}) {
1634
+ const worldPosition = this.projectPosition(xyz);
1635
+ const coord = (0, import_web_mercator.worldToPixels)(worldPosition, this.pixelProjectionMatrix);
1636
+ const [x, y] = coord;
1637
+ const y2 = topLeft ? y : this.height - y;
1638
+ return xyz.length === 2 ? [x, y2] : [x, y2, coord[2]];
1639
+ }
1640
+ /**
1641
+ * Unproject pixel coordinates on screen onto world coordinates,
1642
+ * (possibly [lon, lat]) on map.
1643
+ * - [x, y] => [lng, lat]
1644
+ * - [x, y, z] => [lng, lat, Z]
1645
+ * @param {Array} xyz -
1646
+ * @param {Object} opts - options
1647
+ * @param {Object} opts.topLeft=true - Whether origin is top left
1648
+ * @return {Array|null} - [lng, lat, Z] or [X, Y, Z]
1649
+ */
1650
+ unproject(xyz, { topLeft = true, targetZ } = {}) {
1651
+ const [x, y, z] = xyz;
1652
+ const y2 = topLeft ? y : this.height - y;
1653
+ const targetZWorld = targetZ && targetZ * this.distanceScales.unitsPerMeter[2];
1654
+ const coord = (0, import_web_mercator.pixelsToWorld)([x, y2, z], this.pixelUnprojectionMatrix, targetZWorld);
1655
+ const [X, Y, Z] = this.unprojectPosition(coord);
1656
+ if (Number.isFinite(z)) {
1657
+ return [X, Y, Z];
1658
+ }
1659
+ return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
1660
+ }
1661
+ // NON_LINEAR PROJECTION HOOKS
1662
+ // Used for web meractor projection
1663
+ projectPosition(xyz) {
1664
+ const [X, Y] = this.projectFlat(xyz);
1665
+ const Z = (xyz[2] || 0) * this.distanceScales.unitsPerMeter[2];
1666
+ return [X, Y, Z];
1667
+ }
1668
+ unprojectPosition(xyz) {
1669
+ const [X, Y] = this.unprojectFlat(xyz);
1670
+ const Z = (xyz[2] || 0) * this.distanceScales.metersPerUnit[2];
1671
+ return [X, Y, Z];
1672
+ }
1673
+ /**
1674
+ * Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile.
1675
+ * Performs the nonlinear part of the web mercator projection.
1676
+ * Remaining projection is done with 4x4 matrices which also handles
1677
+ * perspective.
1678
+ * @param {Array} lngLat - [lng, lat] coordinates
1679
+ * Specifies a point on the sphere to project onto the map.
1680
+ * @return {Array} [x,y] coordinates.
1681
+ */
1682
+ projectFlat(xyz) {
1683
+ if (this.isGeospatial) {
1684
+ const result = (0, import_web_mercator.lngLatToWorld)(xyz);
1685
+ result[1] = (0, import_core3.clamp)(result[1], -318, 830);
1686
+ return result;
1687
+ }
1688
+ return xyz;
1689
+ }
1690
+ /**
1691
+ * Unproject world point [x,y] on map onto {lat, lon} on sphere
1692
+ * @param {object|Vector} xy - object with {x,y} members
1693
+ * representing point on projected map plane
1694
+ * @return {GeoCoordinates} - object with {lat,lon} of point on sphere.
1695
+ * Has toArray method if you need a GeoJSON Array.
1696
+ * Per cartographic tradition, lat and lon are specified as degrees.
1697
+ */
1698
+ unprojectFlat(xyz) {
1699
+ if (this.isGeospatial) {
1700
+ return (0, import_web_mercator.worldToLngLat)(xyz);
1701
+ }
1702
+ return xyz;
1703
+ }
1704
+ /**
1705
+ * Get bounds of the current viewport
1706
+ * @return {Array} - [minX, minY, maxX, maxY]
1707
+ */
1708
+ getBounds(options = {}) {
1709
+ const unprojectOption = { targetZ: options.z || 0 };
1710
+ const topLeft = this.unproject([0, 0], unprojectOption);
1711
+ const topRight = this.unproject([this.width, 0], unprojectOption);
1712
+ const bottomLeft = this.unproject([0, this.height], unprojectOption);
1713
+ const bottomRight = this.unproject([this.width, this.height], unprojectOption);
1714
+ return [
1715
+ Math.min(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]),
1716
+ Math.min(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1]),
1717
+ Math.max(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]),
1718
+ Math.max(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1])
1719
+ ];
1720
+ }
1721
+ getDistanceScales(coordinateOrigin) {
1722
+ if (coordinateOrigin && this.isGeospatial) {
1723
+ return (0, import_web_mercator.getDistanceScales)({
1724
+ longitude: coordinateOrigin[0],
1725
+ latitude: coordinateOrigin[1],
1726
+ highPrecision: true
1727
+ });
1728
+ }
1729
+ return this.distanceScales;
1730
+ }
1731
+ containsPixel({
1732
+ x,
1733
+ y,
1734
+ width = 1,
1735
+ height = 1
1736
+ }) {
1737
+ return x < this.x + this.width && this.x < x + width && y < this.y + this.height && this.y < y + height;
1738
+ }
1739
+ // Extract frustum planes in common space
1740
+ getFrustumPlanes() {
1741
+ if (this._frustumPlanes.near) {
1742
+ return this._frustumPlanes;
1743
+ }
1744
+ Object.assign(this._frustumPlanes, getFrustumPlanes(this.viewProjectionMatrix));
1745
+ return this._frustumPlanes;
1746
+ }
1747
+ // EXPERIMENTAL METHODS
1748
+ /**
1749
+ * Needed by panning and linear transition
1750
+ * Pan the viewport to place a given world coordinate at screen point [x, y]
1751
+ *
1752
+ * @param {Array} coords - world coordinates
1753
+ * @param {Array} pixel - [x,y] coordinates on screen
1754
+ * @param {Array} startPixel - [x,y] screen position where pan started (optional, for delta-based panning)
1755
+ * @return {Object} props of the new viewport
1756
+ */
1757
+ panByPosition(coords, pixel, startPixel) {
1758
+ return null;
1759
+ }
1760
+ // INTERNAL METHODS
1761
+ /* eslint-disable complexity, max-statements */
1762
+ _initProps(opts) {
1763
+ const longitude = opts.longitude;
1764
+ const latitude = opts.latitude;
1765
+ if (this.isGeospatial) {
1766
+ if (!Number.isFinite(opts.zoom)) {
1767
+ this.zoom = (0, import_web_mercator.getMeterZoom)({ latitude }) + Math.log2(this.focalDistance);
1768
+ }
1769
+ this.distanceScales = opts.distanceScales || (0, import_web_mercator.getDistanceScales)({ latitude, longitude });
1770
+ }
1771
+ const scale = Math.pow(2, this.zoom);
1772
+ this.scale = scale;
1773
+ const { position, modelMatrix } = opts;
1774
+ let meterOffset = ZERO_VECTOR2;
1775
+ if (position) {
1776
+ meterOffset = modelMatrix ? new import_core3.Matrix4(modelMatrix).transformAsVector(position, []) : position;
1777
+ }
1778
+ if (this.isGeospatial) {
1779
+ const center = this.projectPosition([longitude, latitude, 0]);
1780
+ this.center = new import_core3.Vector3(meterOffset).scale(this.distanceScales.unitsPerMeter).add(center);
1781
+ } else {
1782
+ this.center = this.projectPosition(meterOffset);
1783
+ }
1784
+ }
1785
+ /* eslint-enable complexity, max-statements */
1786
+ _initMatrices(opts) {
1787
+ const {
1788
+ // View matrix
1789
+ viewMatrix: viewMatrix2 = IDENTITY,
1790
+ // Projection matrix
1791
+ projectionMatrix = null,
1792
+ // Projection matrix parameters, used if projectionMatrix not supplied
1793
+ orthographic = false,
1794
+ fovyRadians,
1795
+ fovy = 75,
1796
+ near = 0.1,
1797
+ // Distance of near clipping plane
1798
+ far = 1e3,
1799
+ // Distance of far clipping plane
1800
+ padding = null,
1801
+ // Center offset in pixels
1802
+ focalDistance = 1
1803
+ } = opts;
1804
+ this.viewMatrixUncentered = viewMatrix2;
1805
+ this.viewMatrix = new import_core3.Matrix4().multiplyRight(viewMatrix2).translate(new import_core3.Vector3(this.center).negate());
1806
+ this.projectionMatrix = projectionMatrix || createProjectionMatrix({
1807
+ width: this.width,
1808
+ height: this.height,
1809
+ orthographic,
1810
+ fovyRadians: fovyRadians || fovy * DEGREES_TO_RADIANS,
1811
+ focalDistance,
1812
+ padding,
1813
+ near,
1814
+ far
1815
+ });
1816
+ const vpm = createMat4();
1817
+ import_core3.mat4.multiply(vpm, vpm, this.projectionMatrix);
1818
+ import_core3.mat4.multiply(vpm, vpm, this.viewMatrix);
1819
+ this.viewProjectionMatrix = vpm;
1820
+ this.viewMatrixInverse = import_core3.mat4.invert([], this.viewMatrix) || this.viewMatrix;
1821
+ this.cameraPosition = getCameraPosition(this.viewMatrixInverse);
1822
+ const viewportMatrix = createMat4();
1823
+ const pixelProjectionMatrix = createMat4();
1824
+ import_core3.mat4.scale(viewportMatrix, viewportMatrix, [this.width / 2, -this.height / 2, 1]);
1825
+ import_core3.mat4.translate(viewportMatrix, viewportMatrix, [1, -1, 0]);
1826
+ import_core3.mat4.multiply(pixelProjectionMatrix, viewportMatrix, this.viewProjectionMatrix);
1827
+ this.pixelProjectionMatrix = pixelProjectionMatrix;
1828
+ this.pixelUnprojectionMatrix = import_core3.mat4.invert(createMat4(), this.pixelProjectionMatrix);
1829
+ if (!this.pixelUnprojectionMatrix) {
1830
+ log_default.warn("Pixel project matrix not invertible")();
1831
+ }
1832
+ }
1833
+ };
1834
+ _Viewport.displayName = "Viewport";
1835
+ var Viewport = _Viewport;
1836
+
1837
+ // ../core/src/viewports/web-mercator-viewport.ts
1838
+ var import_web_mercator2 = require("@math.gl/web-mercator");
1839
+ var import_core4 = require("@math.gl/core");
1840
+ var _WebMercatorViewport = class _WebMercatorViewport extends Viewport {
1841
+ /* eslint-disable complexity, max-statements */
1842
+ constructor(opts = {}) {
1843
+ const {
1844
+ latitude = 0,
1845
+ longitude = 0,
1846
+ zoom = 0,
1847
+ pitch = 0,
1848
+ bearing = 0,
1849
+ nearZMultiplier = 0.1,
1850
+ farZMultiplier = 1.01,
1851
+ nearZ,
1852
+ farZ,
1853
+ orthographic = false,
1854
+ projectionMatrix,
1855
+ repeat = false,
1856
+ worldOffset = 0,
1857
+ position,
1858
+ padding,
1859
+ // backward compatibility
1860
+ // TODO: remove in v9
1861
+ legacyMeterSizes = false
1862
+ } = opts;
1863
+ let { width, height, altitude = 1.5 } = opts;
1864
+ const scale = Math.pow(2, zoom);
1865
+ width = width || 1;
1866
+ height = height || 1;
1867
+ let fovy;
1868
+ let projectionParameters = null;
1869
+ if (projectionMatrix) {
1870
+ altitude = projectionMatrix[5] / 2;
1871
+ fovy = (0, import_web_mercator2.altitudeToFovy)(altitude);
1872
+ } else {
1873
+ if (opts.fovy) {
1874
+ fovy = opts.fovy;
1875
+ altitude = (0, import_web_mercator2.fovyToAltitude)(fovy);
1876
+ } else {
1877
+ fovy = (0, import_web_mercator2.altitudeToFovy)(altitude);
1878
+ }
1879
+ let offset;
1880
+ if (padding) {
1881
+ const { top = 0, bottom = 0 } = padding;
1882
+ offset = [0, (0, import_core4.clamp)((top + height - bottom) / 2, 0, height) - height / 2];
1883
+ }
1884
+ projectionParameters = (0, import_web_mercator2.getProjectionParameters)({
1885
+ width,
1886
+ height,
1887
+ scale,
1888
+ center: position && [0, 0, position[2] * (0, import_web_mercator2.unitsPerMeter)(latitude)],
1889
+ offset,
1890
+ pitch,
1891
+ fovy,
1892
+ nearZMultiplier,
1893
+ farZMultiplier
1894
+ });
1895
+ if (Number.isFinite(nearZ)) {
1896
+ projectionParameters.near = nearZ;
1897
+ }
1898
+ if (Number.isFinite(farZ)) {
1899
+ projectionParameters.far = farZ;
1900
+ }
1901
+ }
1902
+ let viewMatrixUncentered = (0, import_web_mercator2.getViewMatrix)({
1903
+ height,
1904
+ pitch,
1905
+ bearing,
1906
+ scale,
1907
+ altitude
1908
+ });
1909
+ if (worldOffset) {
1910
+ const viewOffset = new import_core4.Matrix4().translate([512 * worldOffset, 0, 0]);
1911
+ viewMatrixUncentered = viewOffset.multiplyLeft(viewMatrixUncentered);
1912
+ }
1913
+ super({
1914
+ ...opts,
1915
+ // x, y,
1916
+ width,
1917
+ height,
1918
+ // view matrix
1919
+ viewMatrix: viewMatrixUncentered,
1920
+ longitude,
1921
+ latitude,
1922
+ zoom,
1923
+ // projection matrix parameters
1924
+ ...projectionParameters,
1925
+ fovy,
1926
+ focalDistance: altitude
1927
+ });
1928
+ this.latitude = latitude;
1929
+ this.longitude = longitude;
1930
+ this.zoom = zoom;
1931
+ this.pitch = pitch;
1932
+ this.bearing = bearing;
1933
+ this.altitude = altitude;
1934
+ this.fovy = fovy;
1935
+ this.orthographic = orthographic;
1936
+ this._subViewports = repeat ? [] : null;
1937
+ this._pseudoMeters = legacyMeterSizes;
1938
+ Object.freeze(this);
1939
+ }
1940
+ /* eslint-enable complexity, max-statements */
1941
+ get subViewports() {
1942
+ if (this._subViewports && !this._subViewports.length) {
1943
+ const bounds = this.getBounds();
1944
+ const minOffset = Math.floor((bounds[0] + 180) / 360);
1945
+ const maxOffset = Math.ceil((bounds[2] - 180) / 360);
1946
+ for (let x = minOffset; x <= maxOffset; x++) {
1947
+ const offsetViewport = x ? new _WebMercatorViewport({
1948
+ ...this,
1949
+ worldOffset: x
1950
+ }) : this;
1951
+ this._subViewports.push(offsetViewport);
1952
+ }
1953
+ }
1954
+ return this._subViewports;
1955
+ }
1956
+ /** Returns whether two Web Mercator viewports use the same projection settings. */
1957
+ equals(viewport) {
1958
+ return viewport instanceof _WebMercatorViewport && viewport._pseudoMeters === this._pseudoMeters && super.equals(viewport);
1959
+ }
1960
+ projectPosition(xyz) {
1961
+ if (this._pseudoMeters) {
1962
+ return super.projectPosition(xyz);
1963
+ }
1964
+ const [X, Y] = this.projectFlat(xyz);
1965
+ const Z = (xyz[2] || 0) * (0, import_web_mercator2.unitsPerMeter)(xyz[1]);
1966
+ return [X, Y, Z];
1967
+ }
1968
+ unprojectPosition(xyz) {
1969
+ if (this._pseudoMeters) {
1970
+ return super.unprojectPosition(xyz);
1971
+ }
1972
+ const [X, Y] = this.unprojectFlat(xyz);
1973
+ const Z = (xyz[2] || 0) / (0, import_web_mercator2.unitsPerMeter)(Y);
1974
+ return [X, Y, Z];
1975
+ }
1976
+ /**
1977
+ * Add a meter delta to a base lnglat coordinate, returning a new lnglat array
1978
+ *
1979
+ * Note: Uses simple linear approximation around the viewport center
1980
+ * Error increases with size of offset (roughly 1% per 100km)
1981
+ *
1982
+ * @param {[Number,Number]|[Number,Number,Number]) lngLatZ - base coordinate
1983
+ * @param {[Number,Number]|[Number,Number,Number]) xyz - array of meter deltas
1984
+ * @return {[Number,Number]|[Number,Number,Number]) array of [lng,lat,z] deltas
1985
+ */
1986
+ addMetersToLngLat(lngLatZ, xyz) {
1987
+ return (0, import_web_mercator2.addMetersToLngLat)(lngLatZ, xyz);
1988
+ }
1989
+ panByPosition(coords, pixel, startPixel) {
1990
+ const fromLocation = (0, import_web_mercator2.pixelsToWorld)(pixel, this.pixelUnprojectionMatrix);
1991
+ const toLocation = this.projectFlat(coords);
1992
+ const translate = import_core4.vec2.add([], toLocation, import_core4.vec2.negate([], fromLocation));
1993
+ const newCenter = import_core4.vec2.add([], this.center, translate);
1994
+ const [longitude, latitude] = this.unprojectFlat(newCenter);
1995
+ return { longitude, latitude };
1996
+ }
1997
+ /**
1998
+ * Returns a new longitude and latitude that keeps a 3D world coordinate at a given screen pixel
1999
+ * This version handles the z-component (altitude) properly for cameras positioned above ground
2000
+ */
2001
+ panByPosition3D(coords, pixel) {
2002
+ const targetZ = coords[2] || 0;
2003
+ const deltaLngLat = import_core4.vec2.sub([], coords, this.unproject(pixel, { targetZ }));
2004
+ return { longitude: this.longitude + deltaLngLat[0], latitude: this.latitude + deltaLngLat[1] };
2005
+ }
2006
+ getBounds(options = {}) {
2007
+ const corners = (0, import_web_mercator2.getBounds)(this, options.z || 0);
2008
+ return [
2009
+ Math.min(corners[0][0], corners[1][0], corners[2][0], corners[3][0]),
2010
+ Math.min(corners[0][1], corners[1][1], corners[2][1], corners[3][1]),
2011
+ Math.max(corners[0][0], corners[1][0], corners[2][0], corners[3][0]),
2012
+ Math.max(corners[0][1], corners[1][1], corners[2][1], corners[3][1])
2013
+ ];
2014
+ }
2015
+ /**
2016
+ * Returns a new viewport that fit around the given rectangle.
2017
+ * Only supports non-perspective mode.
2018
+ */
2019
+ fitBounds(bounds, options = {}) {
2020
+ const { width, height } = this;
2021
+ const { longitude, latitude, zoom } = (0, import_web_mercator2.fitBounds)({ width, height, bounds, ...options });
2022
+ return new _WebMercatorViewport({ width, height, longitude, latitude, zoom });
2023
+ }
2024
+ };
2025
+ _WebMercatorViewport.displayName = "WebMercatorViewport";
2026
+ var WebMercatorViewport = _WebMercatorViewport;
2027
+
2028
+ // ../core/src/passes/pick-layers-pass.ts
2029
+ var PICKING_BLENDING = {
2030
+ blendColorOperation: "add",
2031
+ blendColorSrcFactor: "one",
2032
+ blendColorDstFactor: "zero",
2033
+ blendAlphaOperation: "add",
2034
+ blendAlphaSrcFactor: "constant",
2035
+ blendAlphaDstFactor: "zero"
2036
+ };
2037
+ var PickLayersPass = class extends LayersPass {
2038
+ constructor() {
2039
+ super(...arguments);
2040
+ this._colorEncoderState = null;
2041
+ }
2042
+ render(props) {
2043
+ if ("pickingFBO" in props) {
2044
+ return this._drawPickingBuffer(props);
2045
+ }
2046
+ const stats = super._render(props);
2047
+ return { decodePickingColor: null, stats };
2048
+ }
2049
+ // Private
2050
+ // Draws list of layers and viewports into the picking buffer
2051
+ // Note: does not sample the buffer, that has to be done by the caller
2052
+ _drawPickingBuffer({
2053
+ layers,
2054
+ layerFilter,
2055
+ views,
2056
+ viewports,
2057
+ onViewportActive,
2058
+ pickingFBO,
2059
+ deviceRect: { x, y, width, height },
2060
+ cullRect,
2061
+ effects,
2062
+ pass = "picking",
2063
+ pickZ,
2064
+ canvasContext,
2065
+ shaderModuleProps,
2066
+ clearColor
2067
+ }) {
2068
+ this.pickZ = pickZ;
2069
+ const colorEncoderState = this._resetColorEncoder(pickZ);
2070
+ const scissorRect = [x, y, width, height];
2071
+ const renderStatus = super._render({
2072
+ target: pickingFBO,
2073
+ layers,
2074
+ layerFilter,
2075
+ views,
2076
+ viewports,
2077
+ onViewportActive,
2078
+ cullRect,
2079
+ effects: effects == null ? void 0 : effects.filter((e) => e.useInPicking),
2080
+ pass,
2081
+ canvasContext,
2082
+ isPicking: true,
2083
+ shaderModuleProps,
2084
+ clearColor: clearColor ?? [0, 0, 0, 0],
2085
+ colorMask: 15,
2086
+ scissorRect
2087
+ });
2088
+ this._colorEncoderState = null;
2089
+ const decodePickingColor = colorEncoderState && decodeColor.bind(null, colorEncoderState);
2090
+ return { decodePickingColor, stats: renderStatus };
2091
+ }
2092
+ shouldDrawLayer(layer) {
2093
+ const { pickable, operation } = layer.props;
2094
+ return pickable && operation.includes("draw") || operation.includes("terrain") || operation.includes("mask");
2095
+ }
2096
+ getShaderModuleProps(layer, effects, otherShaderModuleProps) {
2097
+ var _a;
2098
+ return {
2099
+ picking: {
2100
+ isActive: 1,
2101
+ isAttribute: this.pickZ,
2102
+ disabledPickingIndices: (_a = layer.internalState) == null ? void 0 : _a.disabledPickingIndices
2103
+ },
2104
+ lighting: { enabled: false }
2105
+ };
2106
+ }
2107
+ getLayerParameters(layer, layerIndex, viewport) {
2108
+ var _a;
2109
+ const pickParameters = {
2110
+ ...layer.props.parameters
2111
+ };
2112
+ const { pickable, operation } = layer.props;
2113
+ if (!this._colorEncoderState) {
2114
+ pickParameters.blend = false;
2115
+ } else if (pickable && operation.includes("draw")) {
2116
+ Object.assign(pickParameters, PICKING_BLENDING);
2117
+ pickParameters.blend = true;
2118
+ if (this.device.type === "webgpu") {
2119
+ pickParameters.blendConstant = encodeColor(this._colorEncoderState, layer, viewport);
2120
+ } else {
2121
+ pickParameters.blendColor = encodeColor(this._colorEncoderState, layer, viewport);
2122
+ }
2123
+ if (operation.includes("terrain") && ((_a = layer.state) == null ? void 0 : _a._hasPickingCover)) {
2124
+ pickParameters.blendAlphaSrcFactor = "one";
2125
+ }
2126
+ } else if (operation.includes("terrain")) {
2127
+ pickParameters.blend = false;
2128
+ }
2129
+ return pickParameters;
2130
+ }
2131
+ _resetColorEncoder(pickZ) {
2132
+ this._colorEncoderState = pickZ ? null : {
2133
+ byLayer: /* @__PURE__ */ new Map(),
2134
+ byAlpha: []
2135
+ };
2136
+ return this._colorEncoderState;
2137
+ }
2138
+ };
2139
+ function encodeColor(encoded, layer, viewport) {
2140
+ const { byLayer, byAlpha } = encoded;
2141
+ let a;
2142
+ let entry = byLayer.get(layer);
2143
+ if (entry) {
2144
+ entry.viewports.push(viewport);
2145
+ a = entry.a;
2146
+ } else {
2147
+ a = byLayer.size + 1;
2148
+ if (a <= 255) {
2149
+ entry = { a, layer, viewports: [viewport] };
2150
+ byLayer.set(layer, entry);
2151
+ byAlpha[a] = entry;
2152
+ } else {
2153
+ log_default.warn("Too many pickable layers, only picking the first 255")();
2154
+ a = 0;
2155
+ }
2156
+ }
2157
+ return [0, 0, 0, a / 255];
2158
+ }
2159
+ function decodeColor(encoded, pickedColor) {
2160
+ const entry = encoded.byAlpha[pickedColor[3]];
2161
+ return entry && {
2162
+ pickedLayer: entry.layer,
2163
+ pickedViewports: entry.viewports,
2164
+ pickedObjectIndex: entry.layer.decodePickingColor(pickedColor)
2165
+ };
2166
+ }
2167
+
2168
+ // ../core/src/utils/deep-equal.ts
2169
+ function deepEqual(a, b, depth) {
2170
+ if (a === b) {
2171
+ return true;
2172
+ }
2173
+ if (!depth || !a || !b) {
2174
+ return false;
2175
+ }
2176
+ if (Array.isArray(a)) {
2177
+ if (!Array.isArray(b) || a.length !== b.length) {
2178
+ return false;
2179
+ }
2180
+ for (let i = 0; i < a.length; i++) {
2181
+ if (!deepEqual(a[i], b[i], depth - 1)) {
2182
+ return false;
2183
+ }
2184
+ }
2185
+ return true;
2186
+ }
2187
+ if (Array.isArray(b)) {
2188
+ return false;
2189
+ }
2190
+ if (typeof a === "object" && typeof b === "object") {
2191
+ const aKeys = Object.keys(a);
2192
+ const bKeys = Object.keys(b);
2193
+ if (aKeys.length !== bKeys.length) {
2194
+ return false;
2195
+ }
2196
+ for (const key of aKeys) {
2197
+ if (!b.hasOwnProperty(key)) {
2198
+ return false;
2199
+ }
2200
+ if (!deepEqual(a[key], b[key], depth - 1)) {
2201
+ return false;
2202
+ }
2203
+ }
2204
+ return true;
2205
+ }
2206
+ return false;
2207
+ }
2208
+
2209
+ // ../core/src/utils/shader.ts
2210
+ function mergeShaders(target, source2) {
2211
+ if (!source2) {
2212
+ return target;
2213
+ }
2214
+ const result = { ...target, ...source2 };
2215
+ if ("defines" in source2) {
2216
+ result.defines = { ...target.defines, ...source2.defines };
2217
+ }
2218
+ if ("modules" in source2) {
2219
+ result.modules = (target.modules || []).concat(source2.modules);
2220
+ if (source2.modules.some((module2) => module2.name === "project64")) {
2221
+ const index = result.modules.findIndex((module2) => module2.name === "project32");
2222
+ if (index >= 0) {
2223
+ result.modules.splice(index, 1);
2224
+ }
2225
+ }
2226
+ }
2227
+ if ("inject" in source2) {
2228
+ if (!target.inject) {
2229
+ result.inject = source2.inject;
2230
+ } else {
2231
+ const mergedInjection = { ...target.inject };
2232
+ for (const key in source2.inject) {
2233
+ mergedInjection[key] = (mergedInjection[key] || "") + source2.inject[key];
2234
+ }
2235
+ result.inject = mergedInjection;
2236
+ }
2237
+ }
2238
+ return result;
2239
+ }
2240
+
2241
+ // ../core/src/viewports/globe-viewport.ts
2242
+ var import_core5 = require("@math.gl/core");
2243
+ var import_web_mercator3 = require("@math.gl/web-mercator");
2244
+ var DEGREES_TO_RADIANS2 = Math.PI / 180;
2245
+ var RADIANS_TO_DEGREES = 180 / Math.PI;
2246
+ var NORTH_UP_BEARING_THRESHOLD = 1;
2247
+ var EARTH_RADIUS = 6370972;
2248
+ var GLOBE_RADIUS = 256;
2249
+ var GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO = 0.75;
2250
+ var GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO = 1.15;
2251
+ function isGlobeNorthUp(bearing) {
2252
+ const normalizedBearing = mod(bearing + 180, 360) - 180;
2253
+ return Math.abs(normalizedBearing) < NORTH_UP_BEARING_THRESHOLD;
2254
+ }
2255
+ function getDistanceScales2() {
2256
+ const unitsPerMeter2 = GLOBE_RADIUS / EARTH_RADIUS;
2257
+ const unitsPerDegree = Math.PI / 180 * GLOBE_RADIUS;
2258
+ return {
2259
+ unitsPerMeter: [unitsPerMeter2, unitsPerMeter2, unitsPerMeter2],
2260
+ unitsPerMeter2: [0, 0, 0],
2261
+ metersPerUnit: [1 / unitsPerMeter2, 1 / unitsPerMeter2, 1 / unitsPerMeter2],
2262
+ unitsPerDegree: [unitsPerDegree, unitsPerDegree, unitsPerMeter2],
2263
+ unitsPerDegree2: [0, 0, 0],
2264
+ degreesPerUnit: [1 / unitsPerDegree, 1 / unitsPerDegree, 1 / unitsPerMeter2]
2265
+ };
2266
+ }
2267
+ var GlobeViewport = class extends Viewport {
2268
+ constructor(opts = {}) {
2269
+ const {
2270
+ longitude = 0,
2271
+ bearing = 0,
2272
+ pitch = 0,
2273
+ zoom = 0,
2274
+ // Matches Maplibre defaults
2275
+ // https://github.com/maplibre/maplibre-gl-js/blob/f8ab4b48d59ab8fe7b068b102538793bbdd4c848/src/geo/projection/globe_transform.ts#L632-L633
2276
+ nearZMultiplier = 0.5,
2277
+ farZMultiplier = 1,
2278
+ resolution = 10
2279
+ } = opts;
2280
+ let { latitude = 0, height, altitude = 1.5, fovy } = opts;
2281
+ latitude = Math.max(Math.min(latitude, 90), -90);
2282
+ height = height || 1;
2283
+ if (fovy) {
2284
+ altitude = (0, import_web_mercator3.fovyToAltitude)(fovy);
2285
+ } else {
2286
+ fovy = (0, import_web_mercator3.altitudeToFovy)(altitude);
2287
+ }
2288
+ const scaleLatitude = Math.max(Math.min(latitude, import_web_mercator3.MAX_LATITUDE), -import_web_mercator3.MAX_LATITUDE);
2289
+ const scale = Math.pow(2, zoom - zoomAdjust(scaleLatitude));
2290
+ const pitchRadians = pitch * DEGREES_TO_RADIANS2;
2291
+ const nearZ = opts.nearZ ?? nearZMultiplier;
2292
+ const farZ = opts.farZ ?? (altitude + GLOBE_RADIUS * 2 * scale / height / Math.max(Math.cos(pitchRadians), 0.1)) * farZMultiplier;
2293
+ const viewMatrix2 = new import_core5.Matrix4().lookAt({ eye: [0, -altitude, 0], up: [0, 0, 1] }).rotateX(-pitchRadians).rotateY(-bearing * DEGREES_TO_RADIANS2).rotateX(latitude * DEGREES_TO_RADIANS2).rotateZ(-longitude * DEGREES_TO_RADIANS2).scale(scale / height);
2294
+ super({
2295
+ ...opts,
2296
+ // x, y, width,
2297
+ height,
2298
+ // view matrix
2299
+ viewMatrix: viewMatrix2,
2300
+ longitude,
2301
+ latitude,
2302
+ zoom,
2303
+ // projection matrix parameters
2304
+ distanceScales: getDistanceScales2(),
2305
+ fovy,
2306
+ focalDistance: altitude,
2307
+ near: nearZ,
2308
+ far: farZ
2309
+ });
2310
+ this.scale = scale;
2311
+ this.latitude = latitude;
2312
+ this.longitude = longitude;
2313
+ this.bearing = bearing;
2314
+ this.pitch = pitch;
2315
+ this.fovy = fovy;
2316
+ this.resolution = resolution;
2317
+ }
2318
+ get projectionMode() {
2319
+ return PROJECTION_MODE.GLOBE;
2320
+ }
2321
+ getDistanceScales() {
2322
+ return this.distanceScales;
2323
+ }
2324
+ getBounds(options = {}) {
2325
+ const unprojectOption = { targetZ: options.z || 0 };
2326
+ const left = this.unproject([0, this.height / 2], unprojectOption);
2327
+ const top = this.unproject([this.width / 2, 0], unprojectOption);
2328
+ const right = this.unproject([this.width, this.height / 2], unprojectOption);
2329
+ const bottom = this.unproject([this.width / 2, this.height], unprojectOption);
2330
+ if (right[0] < this.longitude) right[0] += 360;
2331
+ if (left[0] > this.longitude) left[0] -= 360;
2332
+ return [
2333
+ Math.min(left[0], right[0], top[0], bottom[0]),
2334
+ Math.min(left[1], right[1], top[1], bottom[1]),
2335
+ Math.max(left[0], right[0], top[0], bottom[0]),
2336
+ Math.max(left[1], right[1], top[1], bottom[1])
2337
+ ];
2338
+ }
2339
+ /**
2340
+ * Builds the screen-pixel → globe-center ray and the intermediate ray/sphere
2341
+ * math reused by `unproject` and anchored zoom. One function so the same
2342
+ * pixelUnprojectionMatrix work isn't duplicated.
2343
+ */
2344
+ _getRayToGlobe(screenPosition, { topLeft = true, targetZ } = {}) {
2345
+ const [screenX, screenY] = screenPosition;
2346
+ const adjustedScreenY = topLeft ? screenY : this.height - screenY;
2347
+ const { pixelUnprojectionMatrix } = this;
2348
+ const rayStartPosition = transformVector(pixelUnprojectionMatrix, [
2349
+ screenX,
2350
+ adjustedScreenY,
2351
+ -1,
2352
+ 1
2353
+ ]);
2354
+ const rayEndPosition = transformVector(pixelUnprojectionMatrix, [
2355
+ screenX,
2356
+ adjustedScreenY,
2357
+ 1,
2358
+ 1
2359
+ ]);
2360
+ const radius = ((targetZ || 0) / EARTH_RADIUS + 1) * GLOBE_RADIUS;
2361
+ const rayLengthSquared = import_core5.vec3.sqrLen(import_core5.vec3.sub([], rayStartPosition, rayEndPosition));
2362
+ const rayStartDistanceSquared = import_core5.vec3.sqrLen(rayStartPosition);
2363
+ const rayEndDistanceSquared = import_core5.vec3.sqrLen(rayEndPosition);
2364
+ const triangleAreaSquared = (4 * rayStartDistanceSquared * rayEndDistanceSquared - (rayLengthSquared - rayStartDistanceSquared - rayEndDistanceSquared) ** 2) / 16;
2365
+ const distanceToCenterSquared = 4 * triangleAreaSquared / rayLengthSquared;
2366
+ return {
2367
+ rayStartPosition,
2368
+ rayEndPosition,
2369
+ radius,
2370
+ rayLengthSquared,
2371
+ rayStartDistanceSquared,
2372
+ distanceToCenterSquared
2373
+ };
2374
+ }
2375
+ _getRayDistanceToGlobeCenterRatio(screenPosition, options) {
2376
+ const { distanceToCenterSquared, radius } = this._getRayToGlobe(screenPosition, options);
2377
+ return Math.sqrt(Math.max(0, distanceToCenterSquared)) / radius;
2378
+ }
2379
+ /**
2380
+ * Returns how strongly a screen position should anchor zoom on the visible globe.
2381
+ * A value of `0` means that the controller should fall back to center zoom.
2382
+ * @param screenPosition - Screen position to evaluate.
2383
+ * @returns Anchor strength from `0` to `1`.
2384
+ */
2385
+ getZoomAnchorStrength(screenPosition) {
2386
+ const distanceRatio = this._getRayDistanceToGlobeCenterRatio(screenPosition);
2387
+ if (distanceRatio >= GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO) {
2388
+ return 0;
2389
+ }
2390
+ const edgeProgress = Math.max(
2391
+ 0,
2392
+ Math.min(
2393
+ 1,
2394
+ (distanceRatio - GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO) / (GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO - GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO)
2395
+ )
2396
+ );
2397
+ const smoothProgress = edgeProgress * edgeProgress * (3 - 2 * edgeProgress);
2398
+ return 1 - smoothProgress;
2399
+ }
2400
+ unproject(xyz, { topLeft = true, targetZ } = {}) {
2401
+ const [x, y, z] = xyz;
2402
+ const y2 = topLeft ? y : this.height - y;
2403
+ const { pixelUnprojectionMatrix } = this;
2404
+ let coord;
2405
+ if (Number.isFinite(z)) {
2406
+ coord = transformVector(pixelUnprojectionMatrix, [x, y2, z, 1]);
2407
+ } else {
2408
+ const {
2409
+ rayStartPosition,
2410
+ rayEndPosition,
2411
+ radius,
2412
+ rayLengthSquared,
2413
+ rayStartDistanceSquared,
2414
+ distanceToCenterSquared
2415
+ } = this._getRayToGlobe(xyz, { topLeft, targetZ });
2416
+ const rayStartToClosestApproach = Math.sqrt(
2417
+ rayStartDistanceSquared - distanceToCenterSquared
2418
+ );
2419
+ const closestApproachToIntersection = Math.sqrt(
2420
+ Math.max(0, radius * radius - distanceToCenterSquared)
2421
+ );
2422
+ const intersectionRatio = (rayStartToClosestApproach - closestApproachToIntersection) / Math.sqrt(rayLengthSquared);
2423
+ coord = import_core5.vec3.lerp([], rayStartPosition, rayEndPosition, intersectionRatio);
2424
+ }
2425
+ const [X, Y, Z] = this.unprojectPosition(coord);
2426
+ if (Number.isFinite(z)) {
2427
+ return [X, Y, Z];
2428
+ }
2429
+ return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
2430
+ }
2431
+ projectPosition(xyz) {
2432
+ const [lng, lat, Z = 0] = xyz;
2433
+ const lambda = lng * DEGREES_TO_RADIANS2;
2434
+ const phi = lat * DEGREES_TO_RADIANS2;
2435
+ const cosPhi = Math.cos(phi);
2436
+ const D = (Z / EARTH_RADIUS + 1) * GLOBE_RADIUS;
2437
+ return [Math.sin(lambda) * cosPhi * D, -Math.cos(lambda) * cosPhi * D, Math.sin(phi) * D];
2438
+ }
2439
+ unprojectPosition(xyz) {
2440
+ const [x, y, z] = xyz;
2441
+ const D = import_core5.vec3.len(xyz);
2442
+ const phi = Math.asin(z / D);
2443
+ const lambda = Math.atan2(x, -y);
2444
+ const lng = lambda * RADIANS_TO_DEGREES;
2445
+ const lat = phi * RADIANS_TO_DEGREES;
2446
+ const Z = (D / GLOBE_RADIUS - 1) * EARTH_RADIUS;
2447
+ return [lng, lat, Z];
2448
+ }
2449
+ projectFlat(xyz) {
2450
+ return xyz;
2451
+ }
2452
+ unprojectFlat(xyz) {
2453
+ return xyz;
2454
+ }
2455
+ /**
2456
+ * Pan the globe to place geographic coordinates at a screen pixel.
2457
+ * When `dragStartPosition` is supplied, applies the delta-based movement used by globe dragging.
2458
+ * @param coordinates - Geographic anchor, or the starting longitude, latitude and zoom.
2459
+ * @param screenPosition - Current screen position.
2460
+ * @param dragStartPosition - Screen position where a drag started.
2461
+ * @returns Updated viewport options.
2462
+ */
2463
+ panByPosition(coordinates, screenPosition, dragStartPosition) {
2464
+ if (!dragStartPosition) {
2465
+ let anchorStrength = this.getZoomAnchorStrength(screenPosition);
2466
+ if (anchorStrength === 0) {
2467
+ return { longitude: this.longitude, latitude: this.latitude };
2468
+ }
2469
+ const currentCoordinates = this.unproject(screenPosition);
2470
+ const longitudeDelta = mod(coordinates[0] - currentCoordinates[0] + 180, 360) - 180;
2471
+ const latitudeDelta = coordinates[1] - currentCoordinates[1];
2472
+ const crossesPole = Math.abs(currentCoordinates[1]) > import_web_mercator3.MAX_LATITUDE || Math.abs(longitudeDelta) > 90;
2473
+ if (isGlobeNorthUp(this.bearing) && crossesPole) {
2474
+ return { longitude: this.longitude, latitude: this.latitude };
2475
+ }
2476
+ if (isGlobeNorthUp(this.bearing) && latitudeDelta !== 0) {
2477
+ const latitudeLimit = latitudeDelta > 0 ? import_web_mercator3.MAX_LATITUDE : -import_web_mercator3.MAX_LATITUDE;
2478
+ const latitudeConstraintStrength = (latitudeLimit - this.latitude) / latitudeDelta;
2479
+ anchorStrength = Math.min(anchorStrength, Math.max(0, latitudeConstraintStrength));
2480
+ }
2481
+ const longitude2 = this.longitude + longitudeDelta * anchorStrength;
2482
+ const latitude2 = Math.max(Math.min(this.latitude + latitudeDelta * anchorStrength, 90), -90);
2483
+ return { longitude: longitude2, latitude: latitude2 };
2484
+ }
2485
+ const [startLongitude, startLatitude, startZoom] = coordinates;
2486
+ const scale = Math.pow(2, this.zoom - zoomAdjust(this.latitude));
2487
+ const rotationSpeed = 0.25 / scale;
2488
+ const longitude = startLongitude + rotationSpeed * (dragStartPosition[0] - screenPosition[0]);
2489
+ let latitude = startLatitude - rotationSpeed * (dragStartPosition[1] - screenPosition[1]);
2490
+ latitude = Math.max(Math.min(latitude, 90), -90);
2491
+ const nextViewState = { longitude, latitude, zoom: startZoom - zoomAdjust(startLatitude) };
2492
+ nextViewState.zoom += zoomAdjust(nextViewState.latitude);
2493
+ return nextViewState;
2494
+ }
2495
+ };
2496
+ GlobeViewport.displayName = "GlobeViewport";
2497
+ function zoomAdjust(latitude, clampToPoles) {
2498
+ if (clampToPoles) {
2499
+ latitude = Math.max(Math.min(latitude, import_web_mercator3.MAX_LATITUDE), -import_web_mercator3.MAX_LATITUDE);
2500
+ }
2501
+ const scaleAdjust = Math.PI * Math.cos(latitude * Math.PI / 180);
2502
+ return Math.log2(scaleAdjust);
2503
+ }
2504
+ function transformVector(matrix, vector) {
2505
+ const result = import_core5.vec4.transformMat4([], vector, matrix);
2506
+ import_core5.vec4.scale(result, result, 1 / result[3]);
2507
+ return result;
2508
+ }
2509
+
2510
+ // ../core/src/viewports/orthographic-viewport.ts
2511
+ var import_core6 = require("@math.gl/core");
2512
+ var import_web_mercator4 = require("@math.gl/web-mercator");
2513
+ var viewMatrix = new import_core6.Matrix4().lookAt({ eye: [0, 0, 1] });
2514
+ function getProjectionMatrix({
2515
+ width,
2516
+ height,
2517
+ near,
2518
+ far,
2519
+ padding
2520
+ }) {
2521
+ let left = -width / 2;
2522
+ let right = width / 2;
2523
+ let bottom = -height / 2;
2524
+ let top = height / 2;
2525
+ if (padding) {
2526
+ const { left: l = 0, right: r = 0, top: t = 0, bottom: b = 0 } = padding;
2527
+ const offsetX = (0, import_core6.clamp)((l + width - r) / 2, 0, width) - width / 2;
2528
+ const offsetY = (0, import_core6.clamp)((t + height - b) / 2, 0, height) - height / 2;
2529
+ left -= offsetX;
2530
+ right -= offsetX;
2531
+ bottom += offsetY;
2532
+ top += offsetY;
2533
+ }
2534
+ return new import_core6.Matrix4().ortho({
2535
+ left,
2536
+ right,
2537
+ bottom,
2538
+ top,
2539
+ near,
2540
+ far
2541
+ });
2542
+ }
2543
+ var OrthographicViewport = class extends Viewport {
2544
+ constructor(props) {
2545
+ const {
2546
+ width,
2547
+ height,
2548
+ near = 0.1,
2549
+ far = 1e3,
2550
+ zoom = 0,
2551
+ target = [0, 0, 0],
2552
+ padding = null,
2553
+ flipY = true
2554
+ } = props;
2555
+ const zoomX = props.zoomX ?? (Array.isArray(zoom) ? zoom[0] : zoom);
2556
+ const zoomY = props.zoomY ?? (Array.isArray(zoom) ? zoom[1] : zoom);
2557
+ const zoom_ = Number.isFinite(props.zoom) ? props.zoom : Math.min(zoomX, zoomY);
2558
+ const scale = Math.pow(2, zoom_);
2559
+ let distanceScales;
2560
+ if (zoomX !== zoom_ || zoomY !== zoom_) {
2561
+ const scaleX = Math.pow(2, zoomX);
2562
+ const scaleY = Math.pow(2, zoomY);
2563
+ distanceScales = {
2564
+ unitsPerMeter: [scaleX / scale, scaleY / scale, 1],
2565
+ metersPerUnit: [scale / scaleX, scale / scaleY, 1]
2566
+ };
2567
+ }
2568
+ super({
2569
+ ...props,
2570
+ // in case viewState contains longitude/latitude values,
2571
+ // make sure that the base Viewport class does not treat this as a geospatial viewport
2572
+ longitude: void 0,
2573
+ position: target,
2574
+ viewMatrix: viewMatrix.clone().scale([scale, scale * (flipY ? -1 : 1), scale]),
2575
+ projectionMatrix: getProjectionMatrix({
2576
+ width: width || 1,
2577
+ height: height || 1,
2578
+ padding,
2579
+ near,
2580
+ far
2581
+ }),
2582
+ zoom: zoom_,
2583
+ distanceScales
2584
+ });
2585
+ this.target = target;
2586
+ this.zoomX = zoomX;
2587
+ this.zoomY = zoomY;
2588
+ this.flipY = flipY;
2589
+ }
2590
+ projectFlat([X, Y]) {
2591
+ const { unitsPerMeter: unitsPerMeter2 } = this.distanceScales;
2592
+ return [X * unitsPerMeter2[0], Y * unitsPerMeter2[1]];
2593
+ }
2594
+ unprojectFlat([x, y]) {
2595
+ const { metersPerUnit } = this.distanceScales;
2596
+ return [x * metersPerUnit[0], y * metersPerUnit[1]];
2597
+ }
2598
+ /* Needed by LinearInterpolator */
2599
+ panByPosition(coords, pixel, startPixel) {
2600
+ const fromLocation = (0, import_web_mercator4.pixelsToWorld)(pixel, this.pixelUnprojectionMatrix);
2601
+ const toLocation = this.projectFlat(coords);
2602
+ const translate = import_core6.vec2.add([], toLocation, import_core6.vec2.negate([], fromLocation));
2603
+ const newCenter = import_core6.vec2.add([], this.center, translate);
2604
+ return { target: this.unprojectFlat(newCenter) };
2605
+ }
2606
+ };
2607
+ OrthographicViewport.displayName = "OrthographicViewport";
2608
+
2609
+ // ../core/src/lib/layer-extension.ts
2610
+ var LayerExtension = class {
2611
+ static get componentName() {
2612
+ return Object.prototype.hasOwnProperty.call(this, "extensionName") ? this.extensionName : "";
2613
+ }
2614
+ constructor(opts) {
2615
+ if (opts) {
2616
+ this.opts = opts;
2617
+ }
2618
+ }
2619
+ /** Returns true if two extensions are equivalent */
2620
+ equals(extension) {
2621
+ if (this === extension) {
2622
+ return true;
2623
+ }
2624
+ return this.constructor === extension.constructor && deepEqual(this.opts, extension.opts, 1);
2625
+ }
2626
+ /** Only called if attached to a primitive layer */
2627
+ getShaders(extension) {
2628
+ return null;
2629
+ }
2630
+ /** Only called if attached to a CompositeLayer */
2631
+ getSubLayerProps(extension) {
2632
+ const { defaultProps: defaultProps9 } = extension.constructor;
2633
+ const newProps = {
2634
+ updateTriggers: {}
2635
+ };
2636
+ for (const key in defaultProps9) {
2637
+ if (key in this.props) {
2638
+ const propDef = defaultProps9[key];
2639
+ const propValue = this.props[key];
2640
+ newProps[key] = propValue;
2641
+ if (propDef && propDef.type === "accessor") {
2642
+ newProps.updateTriggers[key] = this.props.updateTriggers[key];
2643
+ if (typeof propValue === "function") {
2644
+ newProps[key] = this.getSubLayerAccessor(propValue);
2645
+ }
2646
+ }
2647
+ }
2648
+ }
2649
+ return newProps;
2650
+ }
2651
+ /* eslint-disable @typescript-eslint/no-empty-function */
2652
+ initializeState(context, extension) {
2653
+ }
2654
+ updateState(params, extension) {
2655
+ }
2656
+ onNeedsRedraw(extension) {
2657
+ }
2658
+ getNeedsPickingBuffer(extension) {
2659
+ return false;
2660
+ }
2661
+ draw(params, extension) {
2662
+ }
2663
+ finalizeState(context, extension) {
2664
+ }
2665
+ };
2666
+ LayerExtension.defaultProps = {};
2667
+ LayerExtension.extensionName = "LayerExtension";
38
2668
 
39
2669
  // dist/brushing/shader-module.js
40
- var import_core = require("@deck.gl/core");
41
2670
  var uniformBlock = (
42
2671
  /* glsl */
43
2672
  `layout(std140) uniform brushingUniforms {
@@ -75,7 +2704,7 @@ var vertex = (
75
2704
  }
76
2705
  `
77
2706
  );
78
- var vs = `
2707
+ var vs2 = `
79
2708
  ${uniformBlock}
80
2709
  ${vertex}
81
2710
  `;
@@ -85,7 +2714,7 @@ var fragment = (
85
2714
  in float brushing_isVisible;
86
2715
  `
87
2716
  );
88
- var fs = `
2717
+ var fs2 = `
89
2718
  ${uniformBlock}
90
2719
  ${fragment}
91
2720
  `;
@@ -128,9 +2757,9 @@ var inject = {
128
2757
  };
129
2758
  var shader_module_default = {
130
2759
  name: "brushing",
131
- dependencies: [import_core.project],
132
- vs,
133
- fs,
2760
+ dependencies: [project_default],
2761
+ vs: vs2,
2762
+ fs: fs2,
134
2763
  inject,
135
2764
  getUniforms: (opts) => {
136
2765
  if (!opts || !("viewport" in opts)) {
@@ -159,7 +2788,7 @@ var defaultProps = {
159
2788
  brushingEnabled: true,
160
2789
  brushingRadius: 1e4
161
2790
  };
162
- var BrushingExtension = class extends import_core2.LayerExtension {
2791
+ var BrushingExtension = class extends LayerExtension {
163
2792
  getShaders() {
164
2793
  return {
165
2794
  modules: [shader_module_default]
@@ -214,9 +2843,6 @@ BrushingExtension.defaultProps = defaultProps;
214
2843
  BrushingExtension.extensionName = "BrushingExtension";
215
2844
  var brushing_extension_default = BrushingExtension;
216
2845
 
217
- // dist/data-filter/data-filter-extension.js
218
- var import_core3 = require("@deck.gl/core");
219
-
220
2846
  // dist/data-filter/shader-module.js
221
2847
  var uniformBlock2 = (
222
2848
  /* glsl */
@@ -400,7 +3026,7 @@ float dataFilter_reduceValue(vec4 value) {
400
3026
  #endif
401
3027
  `
402
3028
  );
403
- var vs2 = `
3029
+ var vs3 = `
404
3030
  ${uniformBlock2}
405
3031
  ${vertex2}
406
3032
  `;
@@ -410,11 +3036,11 @@ var fragment2 = (
410
3036
  in float dataFilter_value;
411
3037
  `
412
3038
  );
413
- var fs2 = `
3039
+ var fs3 = `
414
3040
  ${uniformBlock2}
415
3041
  ${fragment2}
416
3042
  `;
417
- function getUniforms(opts) {
3043
+ function getUniforms2(opts) {
418
3044
  if (!opts || !("extensions" in opts)) {
419
3045
  return {};
420
3046
  }
@@ -449,7 +3075,7 @@ function getUniforms64(opts) {
449
3075
  if (!opts || !("extensions" in opts)) {
450
3076
  return {};
451
3077
  }
452
- const uniforms = getUniforms(opts);
3078
+ const uniforms = getUniforms2(opts);
453
3079
  const min64High = uniforms.min.map(Math.fround);
454
3080
  uniforms.min = uniforms.min.map((x, i) => x - min64High[i]);
455
3081
  uniforms.softMin = uniforms.softMin.map((x, i) => x - min64High[i]);
@@ -527,16 +3153,16 @@ function uniformTypesFromOptions(opts) {
527
3153
  }
528
3154
  var dataFilter = {
529
3155
  name: "dataFilter",
530
- vs: vs2,
531
- fs: fs2,
3156
+ vs: vs3,
3157
+ fs: fs3,
532
3158
  inject: inject2,
533
- getUniforms,
3159
+ getUniforms: getUniforms2,
534
3160
  uniformTypesFromOptions
535
3161
  };
536
3162
  var dataFilter64 = {
537
3163
  name: "dataFilter",
538
- vs: vs2,
539
- fs: fs2,
3164
+ vs: vs3,
3165
+ fs: fs3,
540
3166
  inject: inject2,
541
3167
  getUniforms: getUniforms64,
542
3168
  uniformTypesFromOptions
@@ -681,24 +3307,24 @@ var DATA_TYPE_FROM_SIZE = {
681
3307
  3: "vec3",
682
3308
  4: "vec4"
683
3309
  };
684
- var DataFilterExtension = class extends import_core3.LayerExtension {
3310
+ var DataFilterExtension = class extends LayerExtension {
685
3311
  constructor(opts = {}) {
686
3312
  super({ ...defaultOptions, ...opts });
687
3313
  }
688
3314
  getShaders(extension) {
689
3315
  const { categorySize, filterSize, fp64: fp642 } = extension.opts;
690
- const defines = {};
3316
+ const defines2 = {};
691
3317
  if (categorySize) {
692
- defines.DATACATEGORY_TYPE = CATEGORY_TYPE_FROM_SIZE[categorySize];
693
- defines.DATACATEGORY_CHANNELS = categorySize;
3318
+ defines2.DATACATEGORY_TYPE = CATEGORY_TYPE_FROM_SIZE[categorySize];
3319
+ defines2.DATACATEGORY_CHANNELS = categorySize;
694
3320
  }
695
3321
  if (filterSize) {
696
- defines.DATAFILTER_TYPE = DATA_TYPE_FROM_SIZE[filterSize];
697
- defines.DATAFILTER_DOUBLE = Boolean(fp642);
3322
+ defines2.DATAFILTER_TYPE = DATA_TYPE_FROM_SIZE[filterSize];
3323
+ defines2.DATAFILTER_DOUBLE = Boolean(fp642);
698
3324
  }
699
3325
  const module2 = fp642 ? dataFilter64 : dataFilter;
700
3326
  module2.uniformTypes = module2.uniformTypesFromOptions(extension.opts);
701
- return { modules: [module2], defines };
3327
+ return { modules: [module2], defines: defines2 };
702
3328
  }
703
3329
  initializeState(context, extension) {
704
3330
  const attributeManager = this.getAttributeManager();
@@ -768,7 +3394,7 @@ var DataFilterExtension = class extends import_core3.LayerExtension {
768
3394
  }
769
3395
  }
770
3396
  if (attributeManager == null ? void 0 : attributeManager.attributes.filterCategoryValues) {
771
- const categoryBitMaskNeedsUpdate = attributeManager.attributes.filterCategoryValues.needsUpdate() || !(0, import_core3._deepEqual)(props.filterCategories, oldProps.filterCategories, 2);
3397
+ const categoryBitMaskNeedsUpdate = attributeManager.attributes.filterCategoryValues.needsUpdate() || !deepEqual(props.filterCategories, oldProps.filterCategories, 2);
772
3398
  if (categoryBitMaskNeedsUpdate) {
773
3399
  this.setState({ categoryBitMask: null });
774
3400
  }
@@ -862,7 +3488,7 @@ var DataFilterExtension = class extends import_core3.LayerExtension {
862
3488
  const channel = c * (maxCategories / 32) + Math.floor(key / 32);
863
3489
  categoryBitMask[channel] += Math.pow(2, key % 32);
864
3490
  } else {
865
- import_core3.log.warn(`Exceeded maximum number of categories (${maxCategories})`)();
3491
+ log_default.warn(`Exceeded maximum number of categories (${maxCategories})`)();
866
3492
  }
867
3493
  }
868
3494
  }
@@ -884,12 +3510,8 @@ DataFilterExtension.defaultProps = defaultProps2;
884
3510
  DataFilterExtension.extensionName = "DataFilterExtension";
885
3511
  var data_filter_extension_default = DataFilterExtension;
886
3512
 
887
- // dist/fp64/fp64-extension.js
888
- var import_core5 = require("@deck.gl/core");
889
-
890
3513
  // dist/fp64/project64.js
891
- var import_shadertools = require("@luma.gl/shadertools");
892
- var import_core4 = require("@deck.gl/core");
3514
+ var import_shadertools3 = require("@luma.gl/shadertools");
893
3515
 
894
3516
  // dist/fp64/project64.glsl.js
895
3517
  var project64_glsl_default = `const vec2 WORLD_SCALE_FP64 = vec2(81.4873275756836, 0.0000032873668232014097);
@@ -968,12 +3590,12 @@ position, position64xyLow, offset, commonPosition
968
3590
  `;
969
3591
 
970
3592
  // dist/fp64/project64.js
971
- var { fp64ify, fp64ifyMatrix4 } = import_shadertools.fp64;
3593
+ var { fp64ify, fp64ifyMatrix4 } = import_shadertools3.fp64;
972
3594
  var project64_default = {
973
3595
  name: "project64",
974
- dependencies: [import_core4.project, import_shadertools.fp64],
3596
+ dependencies: [project_default, import_shadertools3.fp64],
975
3597
  vs: project64_glsl_default,
976
- getUniforms: getUniforms2,
3598
+ getUniforms: getUniforms3,
977
3599
  uniformTypes: {
978
3600
  scale: "vec2<f32>",
979
3601
  // Cannot pass as vec2[16], so instead split into 2 mat4x4
@@ -981,8 +3603,8 @@ var project64_default = {
981
3603
  viewProjectionMatrix64Low: "mat4x4<f32>"
982
3604
  }
983
3605
  };
984
- var getMemoizedUniforms = (0, import_core4._memoize)(calculateUniforms);
985
- function getUniforms2(opts) {
3606
+ var getMemoizedUniforms = memoize(calculateUniforms);
3607
+ function getUniforms3(opts) {
986
3608
  if (opts && "viewport" in opts) {
987
3609
  const { viewProjectionMatrix, scale } = opts.viewport;
988
3610
  return getMemoizedUniforms({ viewProjectionMatrix, scale });
@@ -1009,7 +3631,7 @@ function calculateUniforms({ viewProjectionMatrix, scale }) {
1009
3631
  }
1010
3632
 
1011
3633
  // dist/fp64/fp64-extension.js
1012
- var Fp64Extension = class extends import_core5.LayerExtension {
3634
+ var Fp64Extension = class extends LayerExtension {
1013
3635
  getShaders() {
1014
3636
  const { coordinateSystem } = this.props;
1015
3637
  if (coordinateSystem !== "lnglat" && coordinateSystem !== "default") {
@@ -1028,8 +3650,7 @@ Fp64Extension.extensionName = "Fp64Extension";
1028
3650
  var fp64_extension_default = Fp64Extension;
1029
3651
 
1030
3652
  // dist/path-style/path-style-extension.js
1031
- var import_core6 = require("@deck.gl/core");
1032
- var import_core7 = require("@math.gl/core");
3653
+ var import_core13 = require("@math.gl/core");
1033
3654
 
1034
3655
  // dist/path-style/shaders.glsl.js
1035
3656
  var dashShaders = {
@@ -1037,26 +3658,68 @@ var dashShaders = {
1037
3658
  "vs:#decl": `
1038
3659
  in vec2 instanceDashArrays;
1039
3660
  #ifdef HIGH_PRECISION_DASH
1040
- in float instanceDashOffsets;
3661
+ in vec2 instanceDashOffsets;
1041
3662
  #endif
1042
3663
  out vec2 vDashArray;
1043
3664
  out float vDashOffset;
3665
+ out float vDashPathLength;
3666
+ layout(std140) uniform pathStyleUniforms {
3667
+ float dashAlignMode;
3668
+ bool dashGapPickable;
3669
+ highp int dashUnits;
3670
+ } pathStyle;
1044
3671
  `,
1045
3672
  "vs:#main-end": `
1046
- vDashArray = instanceDashArrays;
3673
+ float dashWidthPixels = path.billboard
3674
+ ? width.x * project.focalDistance
3675
+ : width.x * project.scale;
3676
+ float strokeHalfWidthPixels = path.billboard ? width.x : width.x * project.scale;
3677
+ float dashUnitPixels = strokeHalfWidthPixels;
3678
+ if (pathStyle.dashUnits == 1) {
3679
+ dashUnitPixels = 1.0;
3680
+ } else if (pathStyle.dashUnits == 2) {
3681
+ dashUnitPixels = project_size_to_pixel(1.0);
3682
+ } else if (pathStyle.dashUnits == 3) {
3683
+ dashUnitPixels = project.scale;
3684
+ }
3685
+ vDashArray = instanceDashArrays * (dashUnitPixels / dashWidthPixels);
1047
3686
  #ifdef HIGH_PRECISION_DASH
1048
- vDashOffset = instanceDashOffsets / width.x;
3687
+ vec2 dashOffsetAndLength = (instanceDashOffsets * project.scale) / dashWidthPixels;
3688
+ vDashPathLength = dashOffsetAndLength.y;
3689
+ float dashUnitLength = vDashArray.x + vDashArray.y;
3690
+ float dashPeriod = pathStyle.dashAlignMode == 0.0
3691
+ ? dashUnitLength
3692
+ : vDashPathLength / max(round(vDashPathLength / max(dashUnitLength, 0.0001)), 1.0);
3693
+ vDashOffset = dashPeriod > 0.0 ? mod(dashOffsetAndLength.x, dashPeriod) : 0.0;
1049
3694
  #else
1050
3695
  vDashOffset = 0.0;
3696
+ vDashPathLength = 0.0;
1051
3697
  #endif
1052
3698
  `,
1053
3699
  "fs:#decl": `
1054
3700
  layout(std140) uniform pathStyleUniforms {
1055
3701
  float dashAlignMode;
1056
3702
  bool dashGapPickable;
3703
+ highp int dashUnits;
1057
3704
  } pathStyle;
1058
3705
  in vec2 vDashArray;
1059
3706
  in float vDashOffset;
3707
+ in float vDashPathLength;
3708
+ float dashPatternIntegral(float position, float solidLength, float unitLength) {
3709
+ return floor(position / unitLength) * solidLength +
3710
+ min(mod(position, unitLength), solidLength);
3711
+ }
3712
+ float dashPatternCoverage(
3713
+ float position,
3714
+ float solidLength,
3715
+ float unitLength,
3716
+ float filterWidth
3717
+ ) {
3718
+ float halfFilter = 0.5 * filterWidth;
3719
+ float reducedPosition = mod(position, unitLength);
3720
+ return (dashPatternIntegral(reducedPosition + halfFilter, solidLength, unitLength) -
3721
+ dashPatternIntegral(reducedPosition - halfFilter, solidLength, unitLength)) / filterWidth;
3722
+ }
1060
3723
  `,
1061
3724
  // if given position is in the gap part of the dashed line
1062
3725
  // dashArray.x: solid stroke length, relative to width
@@ -1067,36 +3730,90 @@ in float vDashOffset;
1067
3730
  // 1 - stretch to fit, draw half dash at each end for nicer joints
1068
3731
  // o-- ---- ---- ---- --o-- --o-- ---- --o
1069
3732
  "fs:#main-start": `
3733
+ float dashCoverage = 1.0;
3734
+ bool shouldDiscardDash = false;
3735
+ bool inRoundedDashGap = false;
3736
+ float roundedDashResolvedCoverage = 1.0;
3737
+ float roundedDashSubPixelBlend = 0.0;
3738
+ float roundedDashDutyCycle = 1.0;
1070
3739
  float solidLength = vDashArray.x;
1071
3740
  float gapLength = vDashArray.y;
1072
3741
  float unitLength = solidLength + gapLength;
3742
+ if (unitLength > 0.0 && gapLength > 0.0) {
1073
3743
  float offset;
1074
- if (unitLength > 0.0) {
1075
3744
  if (pathStyle.dashAlignMode == 0.0) {
1076
3745
  offset = vDashOffset;
1077
3746
  } else {
1078
- unitLength = vPathLength / round(vPathLength / unitLength);
3747
+ #ifdef HIGH_PRECISION_DASH
3748
+ unitLength = vDashPathLength / max(round(vDashPathLength / unitLength), 1.0);
3749
+ #else
3750
+ unitLength = vPathLength / max(round(vPathLength / unitLength), 1.0);
3751
+ #endif
3752
+ solidLength = min(solidLength, unitLength);
1079
3753
  offset = solidLength / 2.0;
3754
+ #ifdef HIGH_PRECISION_DASH
3755
+ offset += vDashOffset;
3756
+ #endif
1080
3757
  }
1081
- float unitOffset = mod(vPathPosition.y + offset, unitLength);
1082
- if (gapLength > 0.0 && unitOffset > solidLength) {
1083
- if (path.capType <= 0.5) {
1084
- if (!(pathStyle.dashGapPickable && bool(picking.isActive))) {
1085
- discard;
1086
- }
1087
- } else {
1088
- float distToEnd = length(vec2(
3758
+ float alongPath = vPathPosition.y + offset;
3759
+ float unitOffset = mod(alongPath, unitLength);
3760
+ float filterWidth = max(fwidth(alongPath), 0.0001);
3761
+ if (bool(picking.isActive)) {
3762
+ bool inGap = unitOffset > solidLength;
3763
+ if (inGap && path.capType > 0.5) {
3764
+ inGap = length(vec2(
1089
3765
  min(unitOffset - solidLength, unitLength - unitOffset),
1090
3766
  vPathPosition.x
1091
- ));
1092
- if (distToEnd > 1.0) {
1093
- if (!(pathStyle.dashGapPickable && bool(picking.isActive))) {
1094
- discard;
3767
+ )) > 1.0;
3768
+ }
3769
+ if (inGap && !pathStyle.dashGapPickable) {
3770
+ shouldDiscardDash = true;
3771
+ }
3772
+ } else if (path.capType <= 0.5) {
3773
+ dashCoverage = dashPatternCoverage(alongPath, solidLength, unitLength, filterWidth);
3774
+ } else {
3775
+ float distanceAlongGap = min(unitOffset - solidLength, unitLength - unitOffset);
3776
+ float distanceToEnd = length(vec2(max(distanceAlongGap, 0.0), vPathPosition.x));
3777
+ float capEdgePixels = (1.0 - distanceToEnd) / max(fwidth(distanceToEnd), 1e-6);
3778
+ if (distanceAlongGap > 0.0) {
3779
+ inRoundedDashGap = true;
3780
+ roundedDashResolvedCoverage = smoothedge(0.0, capEdgePixels);
3781
+ dashCoverage = roundedDashResolvedCoverage;
1095
3782
  }
3783
+ float subPixelBlend = smoothstep(unitLength, 2.0 * unitLength, filterWidth);
3784
+ roundedDashSubPixelBlend = subPixelBlend;
3785
+ float boundedSolidLength = min(solidLength, unitLength);
3786
+ float effectiveGap = max(unitLength - boundedSolidLength, 0.0);
3787
+ float capSpan = 2.0 * sqrt(max(1.0 - vPathPosition.x * vPathPosition.x, 0.0));
3788
+ float roundedDutyCycle = clamp(
3789
+ (boundedSolidLength + min(effectiveGap, capSpan)) / unitLength,
3790
+ 0.0,
3791
+ 1.0
3792
+ );
3793
+ roundedDashDutyCycle = roundedDutyCycle;
3794
+ dashCoverage = mix(dashCoverage, roundedDutyCycle, subPixelBlend);
1096
3795
  }
3796
+ dashCoverage = clamp(dashCoverage, 0.0, 1.0);
3797
+ shouldDiscardDash = shouldDiscardDash || dashCoverage <= 0.0;
1097
3798
  }
3799
+ `,
3800
+ "fs:#main-end": `
3801
+ #ifdef ANTIALIASING
3802
+ if (inRoundedDashGap) {
3803
+ float pathCoverage = smoothedge(0.0, edgePixels);
3804
+ float resolvedCapMultiplier =
3805
+ min(pathCoverage, roundedDashResolvedCoverage) / max(pathCoverage, 1e-6);
3806
+ dashCoverage = mix(
3807
+ resolvedCapMultiplier,
3808
+ roundedDashDutyCycle,
3809
+ roundedDashSubPixelBlend
3810
+ );
1098
3811
  }
3812
+ #endif
3813
+ if (shouldDiscardDash) {
3814
+ discard;
1099
3815
  }
3816
+ fragColor.a *= dashCoverage;
1100
3817
  `
1101
3818
  }
1102
3819
  };
@@ -1297,13 +4014,32 @@ float offsetDir = sign(instanceOffsets);
1297
4014
  vPathPosition.x = (vPathPosition.x + offsetDir) * offsetWidth - offsetDir;
1298
4015
  vPathPosition.y *= offsetWidth;
1299
4016
  vPathLength *= offsetWidth;
4017
+ #ifdef DASH_ENABLED
4018
+ if (pathStyle.dashUnits != 0) {
4019
+ vDashArray *= offsetWidth;
4020
+ }
4021
+ vPathBounds *= offsetWidth;
4022
+ #ifdef HIGH_PRECISION_DASH
4023
+ vDashPathLength *= offsetWidth;
4024
+ float restoredDashUnitLength = vDashArray.x + vDashArray.y;
4025
+ float restoredDashPeriod = pathStyle.dashAlignMode == 0.0
4026
+ ? restoredDashUnitLength
4027
+ : vDashPathLength /
4028
+ max(round(vDashPathLength / max(restoredDashUnitLength, 0.0001)), 1.0);
4029
+ vDashOffset = restoredDashPeriod > 0.0
4030
+ ? mod(dashOffsetAndLength.x * offsetWidth, restoredDashPeriod)
4031
+ : 0.0;
4032
+ #else
4033
+ vDashOffset *= offsetWidth;
4034
+ #endif
4035
+ #endif
1300
4036
  `,
1301
- "fs:#main-start": `
1302
- float isInside;
1303
- isInside = step(-1.0, vPathPosition.x) * step(vPathPosition.x, 1.0);
1304
- if (isInside == 0.0) {
4037
+ "fs:#main-end": `
4038
+ #ifndef ANTIALIASING
4039
+ if (abs(vPathPosition.x) > 1.0) {
1305
4040
  discard;
1306
4041
  }
4042
+ #endif
1307
4043
  `
1308
4044
  }
1309
4045
  };
@@ -1313,11 +4049,56 @@ var defaultProps3 = {
1313
4049
  getDashArray: { type: "accessor", value: [0, 0] },
1314
4050
  getOffset: { type: "accessor", value: 0 },
1315
4051
  dashJustified: false,
1316
- dashGapPickable: false
4052
+ dashGapPickable: false,
4053
+ dashUnits: "widths"
1317
4054
  };
1318
- var PathStyleExtension = class extends import_core6.LayerExtension {
1319
- constructor({ dash = false, offset = false, highPrecisionDash = false } = {}) {
1320
- super({ dash: dash || highPrecisionDash, offset, highPrecisionDash });
4055
+ var DASH_UNITS = {
4056
+ widths: 0,
4057
+ pixels: 1,
4058
+ meters: 2,
4059
+ common: 3
4060
+ };
4061
+ var PATH_STYLE_ATTRIBUTES = ["instanceDashArrays", "instanceDashOffsets", "instanceOffsets"];
4062
+ function dashMetricsProjectionPropsChanged({ props, oldProps }) {
4063
+ return !deepEqual(props.modelMatrix, oldProps.modelMatrix, 2) || props.coordinateSystem !== oldProps.coordinateSystem || !deepEqual(props.coordinateOrigin, oldProps.coordinateOrigin, 1);
4064
+ }
4065
+ function projectRenderedPathPosition(layer, position, projectUniforms) {
4066
+ const viewport = layer.context.viewport;
4067
+ if (!(viewport instanceof WebMercatorViewport) || viewport.zoom < 12) {
4068
+ return layer.projectPosition(position, { autoOffset: false });
4069
+ }
4070
+ const positionWorld = import_core13.vec4.transformMat4([], [position[0], position[1], position[2] || 0, 1], projectUniforms.modelMatrix);
4071
+ const coordinateSystem = layer.props.coordinateSystem === "default" ? "lnglat" : layer.props.coordinateSystem;
4072
+ if (coordinateSystem === "lnglat") {
4073
+ if (Math.abs(positionWorld[1] - projectUniforms.coordinateOrigin[1]) > 0.25) {
4074
+ let longitude = positionWorld[0];
4075
+ if (projectUniforms.wrapLongitude) {
4076
+ longitude = ((longitude + 180) % 360 + 360) % 360 - 180;
4077
+ }
4078
+ const commonPosition = viewport.projectPosition([longitude, positionWorld[1], 0]);
4079
+ return [
4080
+ commonPosition[0] - projectUniforms.commonOrigin[0],
4081
+ commonPosition[1] - projectUniforms.commonOrigin[1],
4082
+ positionWorld[2] * projectUniforms.commonUnitsPerMeter[2]
4083
+ ];
4084
+ }
4085
+ import_core13.vec3.sub(positionWorld, positionWorld, projectUniforms.coordinateOrigin);
4086
+ } else if (coordinateSystem === "cartesian") {
4087
+ import_core13.vec3.sub(positionWorld, positionWorld, projectUniforms.coordinateOrigin);
4088
+ }
4089
+ const deltaLatitude = positionWorld[1];
4090
+ const scale = import_core13.vec3.scaleAndAdd([], projectUniforms.commonUnitsPerWorldUnit, projectUniforms.commonUnitsPerWorldUnit2, deltaLatitude);
4091
+ return import_core13.vec3.multiply([], positionWorld, scale);
4092
+ }
4093
+ var PathStyleExtension = class extends LayerExtension {
4094
+ constructor({ dash = false, offset = false, dashMode, highPrecisionDash = false } = {}) {
4095
+ const resolvedDashMode = dashMode ?? (highPrecisionDash ? "path" : "segment");
4096
+ super({
4097
+ dash: dash || highPrecisionDash || dashMode !== void 0,
4098
+ offset,
4099
+ dashMode: resolvedDashMode,
4100
+ highPrecisionDash: resolvedDashMode === "path"
4101
+ });
1321
4102
  }
1322
4103
  getLayerType(layer) {
1323
4104
  if ("pathTesselator" in layer.state) {
@@ -1332,6 +4113,59 @@ var PathStyleExtension = class extends import_core6.LayerExtension {
1332
4113
  }
1333
4114
  return null;
1334
4115
  }
4116
+ synchronizeAttributes(layer) {
4117
+ const attributeManager = layer.getAttributeManager();
4118
+ const layerType = this.getLayerType(layer);
4119
+ if (!attributeManager || !layerType) {
4120
+ return false;
4121
+ }
4122
+ const attributes = attributeManager.getAttributes();
4123
+ const desiredAttributes = /* @__PURE__ */ new Set();
4124
+ if (this.opts.dash) {
4125
+ desiredAttributes.add("instanceDashArrays");
4126
+ }
4127
+ if (layerType === "path" && this.opts.dash && this.opts.dashMode === "path") {
4128
+ desiredAttributes.add("instanceDashOffsets");
4129
+ }
4130
+ if (layerType === "path" && this.opts.offset) {
4131
+ desiredAttributes.add("instanceOffsets");
4132
+ }
4133
+ let topologyChanged = false;
4134
+ for (const attributeName of PATH_STYLE_ATTRIBUTES) {
4135
+ if (attributes[attributeName] && !desiredAttributes.has(attributeName)) {
4136
+ attributeManager.remove([attributeName]);
4137
+ topologyChanged = true;
4138
+ }
4139
+ }
4140
+ if (desiredAttributes.has("instanceDashArrays") && !attributes.instanceDashArrays) {
4141
+ attributeManager.addInstanced({
4142
+ instanceDashArrays: { size: 2, accessor: "getDashArray" }
4143
+ });
4144
+ topologyChanged = true;
4145
+ }
4146
+ if (desiredAttributes.has("instanceDashOffsets") && !attributes.instanceDashOffsets) {
4147
+ attributeManager.addInstanced({
4148
+ instanceDashOffsets: {
4149
+ // [distance from the start of the path, total length of the path]
4150
+ size: 2,
4151
+ // Recalculate from rendered geometry when getPath changes. A binary getPath buffer
4152
+ // contains positions, not dash metrics, so keep it as an update trigger rather than
4153
+ // binding it directly to this attribute.
4154
+ accessor: ["getPath"],
4155
+ // eslint-disable-next-line @typescript-eslint/unbound-method
4156
+ update: this.calculateDashMetrics
4157
+ }
4158
+ });
4159
+ topologyChanged = true;
4160
+ }
4161
+ if (desiredAttributes.has("instanceOffsets") && !attributes.instanceOffsets) {
4162
+ attributeManager.addInstanced({
4163
+ instanceOffsets: { size: 1, accessor: "getOffset" }
4164
+ });
4165
+ topologyChanged = true;
4166
+ }
4167
+ return topologyChanged;
4168
+ }
1335
4169
  isEnabled(layer) {
1336
4170
  return this.getLayerType(layer) !== null;
1337
4171
  }
@@ -1355,60 +4189,58 @@ var PathStyleExtension = class extends import_core6.LayerExtension {
1355
4189
  return { modules: [pathStyle2] };
1356
4190
  }
1357
4191
  let result = {};
1358
- const defines = {};
4192
+ const defines2 = {};
1359
4193
  if (extension.opts.dash) {
1360
- result = (0, import_core6._mergeShaders)(result, dashShaders);
1361
- if (extension.opts.highPrecisionDash) {
1362
- defines.HIGH_PRECISION_DASH = true;
4194
+ result = mergeShaders(result, dashShaders);
4195
+ defines2.DASH_ENABLED = true;
4196
+ if (extension.opts.dashMode === "path") {
4197
+ defines2.HIGH_PRECISION_DASH = true;
1363
4198
  }
1364
4199
  }
1365
4200
  if (extension.opts.offset) {
1366
- result = (0, import_core6._mergeShaders)(result, offsetShaders);
4201
+ result = mergeShaders(result, offsetShaders);
4202
+ defines2.PATH_STYLE_OFFSET = true;
1367
4203
  }
1368
4204
  const { inject: inject6 } = result;
1369
4205
  const pathStyle = {
1370
4206
  name: "pathStyle",
1371
- inject: inject6,
1372
- uniformTypes: {
1373
- dashAlignMode: "f32",
1374
- dashGapPickable: "i32"
1375
- }
4207
+ inject: inject6
1376
4208
  };
4209
+ if (extension.opts.dash) {
4210
+ pathStyle.uniformTypes = {
4211
+ dashAlignMode: "f32",
4212
+ dashGapPickable: "i32",
4213
+ dashUnits: "i32"
4214
+ };
4215
+ }
1377
4216
  return {
1378
4217
  modules: [pathStyle],
1379
- defines
4218
+ defines: defines2
1380
4219
  };
1381
4220
  }
1382
4221
  initializeState(context, extension) {
1383
- const attributeManager = this.getAttributeManager();
1384
- const layerType = extension.getLayerType(this);
1385
- if (!attributeManager || !layerType) {
1386
- return;
1387
- }
1388
- if (extension.opts.dash) {
1389
- attributeManager.addInstanced({
1390
- instanceDashArrays: { size: 2, accessor: "getDashArray" },
1391
- ...layerType === "path" && extension.opts.highPrecisionDash ? {
1392
- instanceDashOffsets: {
1393
- size: 1,
1394
- accessor: "getPath",
1395
- transform: extension.getDashOffsets.bind(this)
1396
- }
1397
- } : {}
1398
- });
1399
- }
1400
- if (layerType === "path" && extension.opts.offset) {
1401
- attributeManager.addInstanced({
1402
- instanceOffsets: { size: 1, accessor: "getOffset" }
1403
- });
1404
- }
4222
+ extension.synchronizeAttributes(this);
1405
4223
  }
1406
4224
  updateState(params, extension) {
4225
+ var _a;
1407
4226
  if (!extension.isEnabled(this)) {
1408
4227
  return;
1409
4228
  }
4229
+ if (params.changeFlags.extensionsChanged) {
4230
+ const topologyChanged = extension.synchronizeAttributes(this);
4231
+ const attributeManager = this.getAttributeManager();
4232
+ if (attributeManager && topologyChanged) {
4233
+ attributeManager.invalidateAll();
4234
+ for (const model of this.getModels()) {
4235
+ model.setBufferLayout(attributeManager.getBufferLayouts(model));
4236
+ }
4237
+ }
4238
+ }
1410
4239
  if (extension.opts.dash) {
1411
4240
  const layerType = extension.getLayerType(this);
4241
+ if (layerType === "path" && extension.opts.dashMode === "path" && dashMetricsProjectionPropsChanged(params)) {
4242
+ (_a = this.getAttributeManager()) == null ? void 0 : _a.invalidate("instanceDashOffsets");
4243
+ }
1412
4244
  if (layerType === "scatterplot" || layerType === "textBackground") {
1413
4245
  const pathStyleProps = {
1414
4246
  dashGapPickable: Boolean(this.props.dashGapPickable)
@@ -1417,12 +4249,96 @@ var PathStyleExtension = class extends import_core6.LayerExtension {
1417
4249
  } else {
1418
4250
  const pathStyleProps = {
1419
4251
  dashAlignMode: this.props.dashJustified ? 1 : 0,
1420
- dashGapPickable: Boolean(this.props.dashGapPickable)
4252
+ dashGapPickable: Boolean(this.props.dashGapPickable),
4253
+ dashUnits: DASH_UNITS[this.props.dashUnits ?? "widths"]
1421
4254
  };
1422
4255
  this.setShaderModuleProps({ pathStyle: pathStyleProps });
1423
4256
  }
1424
4257
  }
1425
4258
  }
4259
+ calculateDashMetrics(attribute, { startRow, endRow }) {
4260
+ var _a, _b, _c;
4261
+ const pathTesselator = this.state.pathTesselator;
4262
+ const vertexStarts = pathTesselator == null ? void 0 : pathTesselator.vertexStarts;
4263
+ const instanceCount = pathTesselator == null ? void 0 : pathTesselator.instanceCount;
4264
+ const packedPositions = pathTesselator == null ? void 0 : pathTesselator.get("positions");
4265
+ if (!vertexStarts || instanceCount === void 0 || typeof (pathTesselator == null ? void 0 : pathTesselator.getPathSegmentIndices) !== "function") {
4266
+ throw new Error("PathStyleExtension requires PathLayer tessellation data for path dashes.");
4267
+ }
4268
+ const binaryPath = (_b = (_a = this.props.data) == null ? void 0 : _a.attributes) == null ? void 0 : _b.getPath;
4269
+ const binaryValue = ArrayBuffer.isView(binaryPath) ? binaryPath : binaryPath == null ? void 0 : binaryPath.value;
4270
+ const hasReadableBinaryPath = ArrayBuffer.isView(binaryValue);
4271
+ const usePackedPositions = packedPositions && (!binaryPath || pathTesselator.normalize || ((_c = pathTesselator.opts) == null ? void 0 : _c.isWebGPU));
4272
+ if (binaryPath && !hasReadableBinaryPath || !usePackedPositions && !hasReadableBinaryPath) {
4273
+ throw new Error("PathStyleExtension cannot calculate whole-path dash metrics from GPU-only getPath data; supply data.attributes.instanceDashOffsets.");
4274
+ }
4275
+ const output = attribute.value;
4276
+ const metricSize = attribute.size;
4277
+ const firstRow = Math.max(0, startRow);
4278
+ const lastRow = Math.min(endRow, vertexStarts.length - 1);
4279
+ let binarySize = 3;
4280
+ let binaryStride = 3;
4281
+ let binaryOffset = 0;
4282
+ if (!usePackedPositions) {
4283
+ const bytesPerElement = binaryValue.BYTES_PER_ELEMENT;
4284
+ binarySize = (binaryPath == null ? void 0 : binaryPath.size) || (this.props.positionFormat === "XY" ? 2 : 3);
4285
+ binaryStride = (binaryPath == null ? void 0 : binaryPath.stride) ? binaryPath.stride / bytesPerElement : binarySize;
4286
+ binaryOffset = (binaryPath == null ? void 0 : binaryPath.offset) ? binaryPath.offset / bytesPerElement : 0;
4287
+ }
4288
+ const sourcePosition = new Array(3);
4289
+ const projectUniforms = project_default.getUniforms({
4290
+ viewport: this.context.viewport,
4291
+ modelMatrix: this.props.modelMatrix,
4292
+ coordinateSystem: this.props.coordinateSystem,
4293
+ coordinateOrigin: this.props.coordinateOrigin,
4294
+ // PathLayer normalizes/cuts wrapped paths on the CPU; its shader never wraps again.
4295
+ autoWrapLongitude: this.wrapLongitude
4296
+ });
4297
+ const readPosition = (vertexIndex) => {
4298
+ if (usePackedPositions) {
4299
+ sourcePosition[0] = packedPositions[vertexIndex * 3];
4300
+ sourcePosition[1] = packedPositions[vertexIndex * 3 + 1];
4301
+ sourcePosition[2] = packedPositions[vertexIndex * 3 + 2];
4302
+ } else {
4303
+ const sourceIndex = binaryOffset + vertexIndex * binaryStride;
4304
+ sourcePosition[0] = binaryValue[sourceIndex];
4305
+ sourcePosition[1] = binaryValue[sourceIndex + 1];
4306
+ sourcePosition[2] = binarySize === 3 ? binaryValue[sourceIndex + 2] : 0;
4307
+ }
4308
+ return sourcePosition;
4309
+ };
4310
+ attribute.startIndices = vertexStarts;
4311
+ for (let row = firstRow; row < lastRow; row++) {
4312
+ const rowStart = vertexStarts[row];
4313
+ const rowEnd = Math.min(vertexStarts[row + 1] ?? instanceCount, instanceCount);
4314
+ output.fill(0, rowStart * metricSize, rowEnd * metricSize);
4315
+ const validSegments = pathTesselator.getPathSegmentIndices(row);
4316
+ let phase = 0;
4317
+ let previousSegment = -2;
4318
+ let previousEnd = null;
4319
+ for (const segmentIndex of validSegments) {
4320
+ const outputIndex = segmentIndex * metricSize;
4321
+ output[outputIndex] = phase;
4322
+ const start = segmentIndex === previousSegment + 1 && previousEnd ? previousEnd : projectRenderedPathPosition(this, readPosition(segmentIndex), projectUniforms);
4323
+ const end = projectRenderedPathPosition(this, readPosition(segmentIndex + 1), projectUniforms);
4324
+ phase += import_core13.vec3.dist(start, end);
4325
+ previousSegment = segmentIndex;
4326
+ previousEnd = end;
4327
+ }
4328
+ if (metricSize > 1) {
4329
+ for (let vertexIndex = rowStart; vertexIndex < rowEnd; vertexIndex++) {
4330
+ output[vertexIndex * metricSize + 1] = phase;
4331
+ }
4332
+ }
4333
+ }
4334
+ }
4335
+ /**
4336
+ * Calculates the distance from the start of a path to each rendered segment.
4337
+ *
4338
+ * The final entry is zero because PathLayer reserves the final vertex as invalid padding.
4339
+ * This scalar return shape is retained for compatibility; path-mode rendering uses an
4340
+ * internal attribute containing both each segment offset and the total path length.
4341
+ */
1426
4342
  getDashOffsets(path) {
1427
4343
  const result = [0];
1428
4344
  const positionSize = this.props.positionFormat === "XY" ? 2 : 3;
@@ -1432,9 +4348,9 @@ var PathStyleExtension = class extends import_core6.LayerExtension {
1432
4348
  let prevP;
1433
4349
  for (let i = 0; i < geometrySize - 1; i++) {
1434
4350
  p = isNested ? path[i] : path.slice(i * positionSize, i * positionSize + positionSize);
1435
- p = this.projectPosition(p);
4351
+ p = this.projectPosition(p, { autoOffset: false });
1436
4352
  if (i > 0) {
1437
- result[i] = result[i - 1] + import_core7.vec3.dist(prevP, p);
4353
+ result[i] = result[i - 1] + import_core13.vec3.dist(prevP, p);
1438
4354
  }
1439
4355
  prevP = p;
1440
4356
  }
@@ -1446,17 +4362,27 @@ PathStyleExtension.defaultProps = defaultProps3;
1446
4362
  PathStyleExtension.extensionName = "PathStyleExtension";
1447
4363
  var path_style_extension_default = PathStyleExtension;
1448
4364
 
1449
- // dist/fill-style/fill-style-extension.js
1450
- var import_core9 = require("@deck.gl/core");
1451
-
1452
4365
  // dist/fill-style/shader-module.js
1453
- var import_core8 = require("@deck.gl/core");
4366
+ var FILL_UV_SCALE = 512 / 4e7;
4367
+ function getPatternUnitScale(sizeUnits, viewport) {
4368
+ switch (sizeUnits) {
4369
+ case "pixels":
4370
+ return 2 ** -Math.round(viewport.zoom);
4371
+ case "common":
4372
+ return 1;
4373
+ default:
4374
+ return FILL_UV_SCALE;
4375
+ }
4376
+ }
1454
4377
  var uniformBlock3 = (
1455
4378
  /* glsl */
1456
4379
  `layout(std140) uniform fillUniforms {
1457
4380
  vec2 patternTextureSize;
4381
+ float patternUnitScale;
1458
4382
  bool patternEnabled;
1459
4383
  bool patternMask;
4384
+ bool procedural;
4385
+ bool flipY;
1460
4386
  vec2 uvCoordinateOrigin;
1461
4387
  vec2 uvCoordinateOrigin64Low;
1462
4388
  } fill;
@@ -1465,16 +4391,23 @@ var uniformBlock3 = (
1465
4391
  var patternVs = (
1466
4392
  /* glsl */
1467
4393
  `
4394
+ uniform sampler2D fill_patternTexture;
4395
+
1468
4396
  in vec4 fillPatternFrames;
1469
4397
  in float fillPatternScales;
1470
4398
  in vec2 fillPatternOffsets;
4399
+ in vec4 fillPatternBackgroundColors;
1471
4400
 
1472
4401
  out vec2 fill_uv;
1473
4402
  out vec4 fill_patternBounds;
1474
4403
  out vec4 fill_patternPlacement;
4404
+ out vec4 fill_backgroundColor;
4405
+ out float fill_patternCoordinateScale;
4406
+ flat out vec4 fill_patternParams0;
4407
+ flat out vec4 fill_patternParams1;
1475
4408
  `
1476
4409
  );
1477
- var vs3 = `
4410
+ var vs4 = `
1478
4411
  ${uniformBlock3}
1479
4412
  ${patternVs}
1480
4413
  `;
@@ -1485,29 +4418,172 @@ uniform sampler2D fill_patternTexture;
1485
4418
 
1486
4419
  in vec4 fill_patternBounds;
1487
4420
  in vec4 fill_patternPlacement;
4421
+ in vec4 fill_backgroundColor;
1488
4422
  in vec2 fill_uv;
4423
+ in float fill_patternCoordinateScale;
4424
+ flat in vec4 fill_patternParams0;
4425
+ flat in vec4 fill_patternParams1;
4426
+
4427
+ const float FILL_PATTERN_HATCH = 1.0;
4428
+ const float FILL_PATTERN_CROSS_HATCH = 2.0;
4429
+ const float FILL_PATTERN_DOTS = 3.0;
4430
+
4431
+ float fill_getPatternCoordinate(vec2 axis, float period, float offset) {
4432
+ float coordinateScale = max(abs(fill_patternCoordinateScale), 1.0e-20);
4433
+ vec2 coordinateOrigin = fill.uvCoordinateOrigin;
4434
+ vec2 coordinateOffset = fill.uvCoordinateOrigin64Low + fill_uv;
4435
+ float yDirection = fill.flipY ? 1.0 : -1.0;
4436
+ coordinateOrigin.y *= yDirection;
4437
+ coordinateOffset.y *= yDirection;
4438
+
4439
+ // Reduce the high part before adding the local offset to retain precision in large coordinates.
4440
+ float highPart = mod(dot(coordinateOrigin, axis) / coordinateScale, period);
4441
+ float lowPart = dot(coordinateOffset, axis) / coordinateScale;
4442
+ return mod(highPart + lowPart + offset * period, period);
4443
+ }
4444
+
4445
+ vec2 fill_getPatternCoords(
4446
+ vec2 axis0,
4447
+ vec2 axis1,
4448
+ vec2 period,
4449
+ vec2 offset
4450
+ ) {
4451
+ return vec2(
4452
+ fill_getPatternCoordinate(axis0, period.x, offset.x),
4453
+ fill_getPatternCoordinate(axis1, period.y, offset.y)
4454
+ );
4455
+ }
1489
4456
 
1490
- const float FILL_UV_SCALE = 512.0 / 40000000.0;
4457
+ float fill_getScreenSpaceAlpha(float signedDistance) {
4458
+ float distanceDerivative = fwidth(signedDistance);
4459
+ if (distanceDerivative == 0.0) {
4460
+ return signedDistance <= 0.0 ? 1.0 : 0.0;
4461
+ }
4462
+ // Normalize the SDF to screen pixels so the coverage ramp is independent of pattern units.
4463
+ float distanceInPixels = signedDistance / distanceDerivative;
4464
+ return 1.0 - smoothstep(-0.5, 0.5, distanceInPixels);
4465
+ }
4466
+
4467
+ float fill_getLineAlpha(float position, float center, float period, float strokeWidth) {
4468
+ float distanceToLine = abs(mod(position - center + period * 0.5, period) - period * 0.5);
4469
+ return fill_getScreenSpaceAlpha(distanceToLine - strokeWidth * 0.5);
4470
+ }
4471
+
4472
+ float fill_getHatchAlpha(float angle, float strokeWidth, vec2 gaps, bool alternating) {
4473
+ vec2 lineDirection = vec2(cos(angle), sin(angle));
4474
+ vec2 lineNormal = vec2(-lineDirection.y, lineDirection.x);
4475
+ float period = alternating
4476
+ ? strokeWidth * 2.0 + gaps.x + gaps.y
4477
+ : strokeWidth + gaps.x;
4478
+ float offset = dot(fill_patternPlacement.xy, lineNormal);
4479
+ float position = fill_getPatternCoordinate(lineNormal, period, offset);
4480
+ float alpha = fill_getLineAlpha(position, 0.0, period, strokeWidth);
4481
+ if (alternating) {
4482
+ alpha = max(
4483
+ alpha,
4484
+ fill_getLineAlpha(position, strokeWidth + gaps.x, period, strokeWidth)
4485
+ );
4486
+ }
4487
+ return alpha;
4488
+ }
4489
+
4490
+ float fill_getDotAlpha(float angle, float skew, float radius, float gap) {
4491
+ vec2 axis0 = vec2(cos(angle), sin(angle));
4492
+ float axis1Angle = angle + 1.5707963267948966 - skew;
4493
+ vec2 axis1 = vec2(cos(axis1Angle), sin(axis1Angle));
4494
+ float determinant = axis0.x * axis1.y - axis0.y * axis1.x;
4495
+
4496
+ // The reciprocal basis maps common coordinates into the skewed dot lattice.
4497
+ vec2 reciprocal0 = vec2(axis1.y, -axis1.x) / determinant;
4498
+ vec2 reciprocal1 = vec2(-axis0.y, axis0.x) / determinant;
4499
+ float period = radius * 2.0 + gap;
4500
+ vec2 latticeCoords = fill_getPatternCoords(
4501
+ reciprocal0,
4502
+ reciprocal1,
4503
+ vec2(period),
4504
+ fill_patternPlacement.xy
4505
+ );
4506
+ vec2 cellOffset = mod(latticeCoords + period * 0.5, period) - period * 0.5;
4507
+ float distanceToDotSquared = 1.0e20;
4508
+ // In a skewed lattice the closest dot is not necessarily in the independently wrapped cell.
4509
+ // Include adjacent cells to keep the repeated distance field continuous at cell boundaries.
4510
+ for (int x = -1; x <= 1; x++) {
4511
+ for (int y = -1; y <= 1; y++) {
4512
+ vec2 neighborOffset = cellOffset + vec2(float(x), float(y)) * period;
4513
+ vec2 dotOffset = axis0 * neighborOffset.x + axis1 * neighborOffset.y;
4514
+ distanceToDotSquared = min(distanceToDotSquared, dot(dotOffset, dotOffset));
4515
+ }
4516
+ }
4517
+ float distanceToDot = sqrt(distanceToDotSquared);
4518
+ return fill_getScreenSpaceAlpha(distanceToDot - radius);
4519
+ }
4520
+
4521
+ float fill_getProceduralPatternAlpha() {
4522
+ float patternType = fill_patternParams0.x;
4523
+ float size = fill_patternParams0.y;
4524
+ vec2 gaps = fill_patternParams0.zw;
4525
+ vec2 angles = fill_patternParams1.xy;
4526
+ float elementCount = fill_patternParams1.z;
4527
+
4528
+ if (patternType == FILL_PATTERN_HATCH) {
4529
+ return fill_getHatchAlpha(angles.x, size, gaps, elementCount == 2.0);
4530
+ }
4531
+ if (patternType == FILL_PATTERN_CROSS_HATCH) {
4532
+ return max(
4533
+ fill_getHatchAlpha(angles.x, size, gaps, false),
4534
+ fill_getHatchAlpha(angles.y, size, gaps, false)
4535
+ );
4536
+ }
4537
+ if (patternType == FILL_PATTERN_DOTS) {
4538
+ return fill_getDotAlpha(angles.x, angles.y, size, gaps.x);
4539
+ }
4540
+ return 1.0;
4541
+ }
4542
+
4543
+ // Draw the pattern over the background fill (Porter-Duff source-over)
4544
+ vec4 fill_blendOverBackground(vec4 pattern, vec4 background) {
4545
+ if (background.a == 0.0 || layer.opacity == 0.0) return pattern;
4546
+ float patternAlpha = pattern.a / layer.opacity;
4547
+ float blendedAlpha = patternAlpha + background.a * (1.0 - patternAlpha);
4548
+ vec3 blendedRGB = mix(background.rgb, pattern.rgb, patternAlpha / blendedAlpha);
4549
+ return vec4(blendedRGB, blendedAlpha * layer.opacity);
4550
+ }
1491
4551
  `
1492
4552
  );
1493
- var fs3 = `
4553
+ var fs4 = `
1494
4554
  ${uniformBlock3}
1495
4555
  ${patternFs}
1496
4556
  `;
1497
4557
  var inject3 = {
1498
4558
  "vs:DECKGL_FILTER_GL_POSITION": (
1499
- /* glsl */
1500
- `
1501
- fill_uv = geometry.position.xy;
1502
- `
1503
- ),
1504
- "vs:DECKGL_FILTER_COLOR": (
1505
4559
  /* glsl */
1506
4560
  `
1507
4561
  if (fill.patternEnabled) {
1508
- fill_patternBounds = fillPatternFrames / vec4(fill.patternTextureSize, fill.patternTextureSize);
1509
4562
  fill_patternPlacement.xy = fillPatternOffsets;
1510
- fill_patternPlacement.zw = fillPatternScales * fillPatternFrames.zw;
4563
+ fill_backgroundColor = fillPatternBackgroundColors;
4564
+ if (fill.procedural) {
4565
+ fill_uv = geometry.position.xy;
4566
+ fill_patternCoordinateScale = fill.patternUnitScale * fillPatternScales;
4567
+ int texelIndex = int(fillPatternFrames.x) * 4;
4568
+ fill_patternParams0 = vec4(
4569
+ texelFetch(fill_patternTexture, ivec2(texelIndex, 0), 0).rg,
4570
+ texelFetch(fill_patternTexture, ivec2(texelIndex + 1, 0), 0).rg
4571
+ );
4572
+ fill_patternParams1 = vec4(
4573
+ texelFetch(fill_patternTexture, ivec2(texelIndex + 2, 0), 0).rg,
4574
+ texelFetch(fill_patternTexture, ivec2(texelIndex + 3, 0), 0).rg
4575
+ );
4576
+ } else {
4577
+ fill_patternBounds = fillPatternFrames / vec4(fill.patternTextureSize, fill.patternTextureSize);
4578
+ vec2 patternFrameCommon = fill.patternUnitScale * fillPatternScales * fillPatternFrames.zw;
4579
+ // Reduce the coordinate origin to within one tile before adding the vertex position. The
4580
+ // origin is large in common space, and fp32 cannot carry the sum at full precision.
4581
+ vec2 origin = mod(fill.uvCoordinateOrigin, patternFrameCommon) + fill.uvCoordinateOrigin64Low;
4582
+ fill_uv = (origin + geometry.position.xy) / patternFrameCommon;
4583
+ // Pattern atlases use top-left coordinates, so reverse common-space Y in bottom-left views.
4584
+ fill_uv.y *= fill.flipY ? 1.0 : -1.0;
4585
+ fill_uv += fillPatternOffsets;
4586
+ }
1511
4587
  }
1512
4588
  `
1513
4589
  ),
@@ -1515,22 +4591,31 @@ var inject3 = {
1515
4591
  /* glsl */
1516
4592
  `
1517
4593
  if (fill.patternEnabled) {
1518
- vec2 scale = FILL_UV_SCALE * fill_patternPlacement.zw;
1519
- vec2 patternUV = mod(mod(fill.uvCoordinateOrigin, scale) + fill.uvCoordinateOrigin64Low + fill_uv, scale) / scale;
1520
- patternUV = mod(fill_patternPlacement.xy + patternUV, 1.0);
4594
+ if (fill.procedural) {
4595
+ color.a *= fill_getProceduralPatternAlpha();
4596
+ } else {
4597
+ vec2 patternUV = fract(fill_uv);
4598
+ vec2 texCoords = fill_patternBounds.xy + fill_patternBounds.zw * patternUV;
1521
4599
 
1522
- vec2 texCoords = fill_patternBounds.xy + fill_patternBounds.zw * patternUV;
4600
+ // Tiling is emulated by wrapping the coordinate, so texCoords jumps from the end of the
4601
+ // frame back to its start once per tile, leading to the wrong mip level being selected,
4602
+ // which leads to artifacts at pattern edges. fill_uv is continuous across the primitive,
4603
+ // meaning that the correct mip level is always selected
4604
+ vec4 grad = fill_patternBounds.zwzw * vec4(dFdx(fill_uv), dFdy(fill_uv));
4605
+ vec4 patternColor = textureGrad(fill_patternTexture, texCoords, grad.xy, grad.zw);
1523
4606
 
1524
- vec4 patternColor = texture(fill_patternTexture, texCoords);
1525
- color.a *= patternColor.a;
1526
- if (!fill.patternMask) {
1527
- color.rgb = patternColor.rgb;
4607
+ color.a *= patternColor.a;
4608
+ if (!fill.patternMask) {
4609
+ color.rgb = patternColor.rgb;
4610
+ }
1528
4611
  }
4612
+ color = fill_blendOverBackground(color, fill_backgroundColor);
1529
4613
  }
1530
4614
  `
1531
4615
  )
1532
4616
  };
1533
4617
  function getPatternUniforms(opts) {
4618
+ var _a;
1534
4619
  if (!opts) {
1535
4620
  return {};
1536
4621
  }
@@ -1541,54 +4626,200 @@ function getPatternUniforms(opts) {
1541
4626
  uniforms.patternTextureSize = [fillPatternTexture.width, fillPatternTexture.height];
1542
4627
  }
1543
4628
  if ("project" in opts) {
1544
- const { fillPatternMask = true, fillPatternEnabled = true } = opts;
1545
- const projectUniforms = import_core8.project.getUniforms(opts.project);
4629
+ const { fillPatternMask = true, fillPatternEnabled = true, fillPatternSizeUnits = "meters", fillPatternCommonFrame = null, procedural = false } = opts;
4630
+ const projectUniforms = project_default.getUniforms(opts.project);
1546
4631
  const { commonOrigin: coordinateOriginCommon } = projectUniforms;
1547
- const coordinateOriginCommon64Low = [
1548
- (0, import_core8.fp64LowPart)(coordinateOriginCommon[0]),
1549
- (0, import_core8.fp64LowPart)(coordinateOriginCommon[1])
1550
- ];
1551
- uniforms.uvCoordinateOrigin = coordinateOriginCommon.slice(0, 2);
1552
- uniforms.uvCoordinateOrigin64Low = coordinateOriginCommon64Low;
4632
+ const unitScale = getPatternUnitScale(fillPatternSizeUnits, opts.project.viewport);
4633
+ const origin = [coordinateOriginCommon[0], coordinateOriginCommon[1]];
4634
+ if (fillPatternCommonFrame) {
4635
+ origin[0] %= unitScale * fillPatternCommonFrame[0];
4636
+ origin[1] %= unitScale * fillPatternCommonFrame[1];
4637
+ }
4638
+ uniforms.uvCoordinateOrigin = origin;
4639
+ uniforms.uvCoordinateOrigin64Low = [fp64LowPart(origin[0]), fp64LowPart(origin[1])];
4640
+ uniforms.patternUnitScale = unitScale;
1553
4641
  uniforms.patternMask = fillPatternMask;
1554
4642
  uniforms.patternEnabled = fillPatternEnabled;
4643
+ uniforms.procedural = procedural;
4644
+ uniforms.flipY = ((_a = opts.project.viewport) == null ? void 0 : _a.flipY) ?? false;
1555
4645
  }
1556
4646
  return uniforms;
1557
4647
  }
1558
4648
  var patternShaders = {
1559
4649
  name: "fill",
1560
- vs: vs3,
1561
- fs: fs3,
4650
+ vs: vs4,
4651
+ fs: fs4,
1562
4652
  inject: inject3,
1563
- dependencies: [import_core8.project],
4653
+ dependencies: [project_default],
1564
4654
  getUniforms: getPatternUniforms,
1565
4655
  uniformTypes: {
1566
4656
  patternTextureSize: "vec2<f32>",
4657
+ patternUnitScale: "f32",
1567
4658
  patternEnabled: "i32",
1568
4659
  patternMask: "i32",
4660
+ procedural: "i32",
4661
+ flipY: "i32",
1569
4662
  uvCoordinateOrigin: "vec2<f32>",
1570
4663
  uvCoordinateOrigin64Low: "vec2<f32>"
1571
4664
  }
1572
4665
  };
1573
4666
 
4667
+ // dist/fill-style/procedural-pattern.js
4668
+ var PROCEDURAL_PATTERN_TEXTURE_FORMAT = "rg32float";
4669
+ var PROCEDURAL_PATTERN_COMPONENTS_PER_TEXEL = 2;
4670
+ var PROCEDURAL_PATTERN_TEXELS_PER_PATTERN = 4;
4671
+ var PROCEDURAL_PATTERN_TYPES = {
4672
+ none: 0,
4673
+ hatch: 1,
4674
+ crossHatch: 2,
4675
+ dots: 3
4676
+ };
4677
+ var PROCEDURAL_PATTERN_OFFSETS = {
4678
+ type: 0,
4679
+ size: 1,
4680
+ gap0: 2,
4681
+ gap1: 3,
4682
+ angle0: 4,
4683
+ angle1: 5,
4684
+ /** Number of alternating gaps, hatch directions, or dot-grid axes, depending on type. */
4685
+ elementCount: 6
4686
+ };
4687
+ var COMPONENTS_PER_PATTERN = PROCEDURAL_PATTERN_COMPONENTS_PER_TEXEL * PROCEDURAL_PATTERN_TEXELS_PER_PATTERN;
4688
+ var DEGREES_TO_RADIANS3 = Math.PI / 180;
4689
+ var DEFAULT_SIZE = 1;
4690
+ var DEFAULT_GAP = 1;
4691
+ var DEFAULT_HATCH_ANGLE = 0;
4692
+ var DEFAULT_CROSS_HATCH_ANGLES = [45, 135];
4693
+ var DEFAULT_DOT_ANGLE = 0;
4694
+ var DEFAULT_DOT_SKEW = 0;
4695
+ function packProceduralPatterns(mapping) {
4696
+ const entries = Object.entries(mapping);
4697
+ const data = new Float32Array((entries.length + 1) * COMPONENTS_PER_PATTERN);
4698
+ const patternIndices = /* @__PURE__ */ new Map();
4699
+ entries.forEach(([id, config], entryIndex) => {
4700
+ const patternIndex = entryIndex + 1;
4701
+ const offset = patternIndex * COMPONENTS_PER_PATTERN;
4702
+ patternIndices.set(id, patternIndex);
4703
+ switch (config.type) {
4704
+ case "hatch": {
4705
+ const gap = config.gap === void 0 ? DEFAULT_GAP : config.gap;
4706
+ const strokeWidth = config.strokeWidth === void 0 ? DEFAULT_SIZE : config.strokeWidth;
4707
+ const angle = config.angle === void 0 ? DEFAULT_HATCH_ANGLE : config.angle;
4708
+ if (Array.isArray(gap) && gap.length !== 2) {
4709
+ throw new Error(`${id}.gap must be a number or a two-element array`);
4710
+ }
4711
+ const gaps = Array.isArray(gap) ? gap : [gap, gap];
4712
+ validatePositive(strokeWidth, `${id}.strokeWidth`);
4713
+ validateNonNegative(gaps[0], `${id}.gap[0]`);
4714
+ validateNonNegative(gaps[1], `${id}.gap[1]`);
4715
+ validateAngle(angle, `${id}.angle`);
4716
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.type] = PROCEDURAL_PATTERN_TYPES.hatch;
4717
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.size] = strokeWidth;
4718
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.gap0] = gaps[0];
4719
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.gap1] = gaps[1];
4720
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.angle0] = angle * DEGREES_TO_RADIANS3;
4721
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.elementCount] = Array.isArray(gap) ? 2 : 1;
4722
+ break;
4723
+ }
4724
+ case "cross-hatch": {
4725
+ const angles = config.angles === void 0 ? DEFAULT_CROSS_HATCH_ANGLES : config.angles;
4726
+ const strokeWidth = config.strokeWidth === void 0 ? DEFAULT_SIZE : config.strokeWidth;
4727
+ const gap = config.gap === void 0 ? DEFAULT_GAP : config.gap;
4728
+ if (!Array.isArray(angles) || angles.length !== 2) {
4729
+ throw new Error(`${id}.angles must be a two-element array`);
4730
+ }
4731
+ validatePositive(strokeWidth, `${id}.strokeWidth`);
4732
+ validateNonNegative(gap, `${id}.gap`);
4733
+ validateAngle(angles[0], `${id}.angles[0]`);
4734
+ validateAngle(angles[1], `${id}.angles[1]`);
4735
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.type] = PROCEDURAL_PATTERN_TYPES.crossHatch;
4736
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.size] = strokeWidth;
4737
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.gap0] = gap;
4738
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.gap1] = gap;
4739
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.angle0] = angles[0] * DEGREES_TO_RADIANS3;
4740
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.angle1] = angles[1] * DEGREES_TO_RADIANS3;
4741
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.elementCount] = 2;
4742
+ break;
4743
+ }
4744
+ case "dots": {
4745
+ const radius = config.radius === void 0 ? DEFAULT_SIZE : config.radius;
4746
+ const gap = config.gap === void 0 ? DEFAULT_GAP : config.gap;
4747
+ const angle = config.angle === void 0 ? DEFAULT_DOT_ANGLE : config.angle;
4748
+ const skew = config.skew === void 0 ? DEFAULT_DOT_SKEW : config.skew;
4749
+ validatePositive(radius, `${id}.radius`);
4750
+ validateNonNegative(gap, `${id}.gap`);
4751
+ validateAngle(angle, `${id}.angle`);
4752
+ validateSkew(skew, `${id}.skew`);
4753
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.type] = PROCEDURAL_PATTERN_TYPES.dots;
4754
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.size] = radius;
4755
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.gap0] = gap;
4756
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.gap1] = gap;
4757
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.angle0] = angle * DEGREES_TO_RADIANS3;
4758
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.angle1] = skew * DEGREES_TO_RADIANS3;
4759
+ data[offset + PROCEDURAL_PATTERN_OFFSETS.elementCount] = 2;
4760
+ break;
4761
+ }
4762
+ default:
4763
+ throw new Error(`${id}.type is not a supported procedural pattern type`);
4764
+ }
4765
+ });
4766
+ return {
4767
+ data,
4768
+ width: data.length / PROCEDURAL_PATTERN_COMPONENTS_PER_TEXEL,
4769
+ height: 1,
4770
+ patternIndices
4771
+ };
4772
+ }
4773
+ function validatePositive(value, name) {
4774
+ if (!Number.isFinite(value) || value <= 0) {
4775
+ throw new Error(`${name} must be a finite number greater than 0`);
4776
+ }
4777
+ }
4778
+ function validateNonNegative(value, name) {
4779
+ if (!Number.isFinite(value) || value < 0) {
4780
+ throw new Error(`${name} must be a finite number greater than or equal to 0`);
4781
+ }
4782
+ }
4783
+ function validateAngle(value, name) {
4784
+ if (!Number.isFinite(value)) {
4785
+ throw new Error(`${name} must be a finite number`);
4786
+ }
4787
+ }
4788
+ function validateSkew(value, name) {
4789
+ if (!Number.isFinite(value) || value <= -90 || value >= 90) {
4790
+ throw new Error(`${name} must be a finite number greater than -90 and less than 90`);
4791
+ }
4792
+ }
4793
+
1574
4794
  // dist/fill-style/fill-style-extension.js
4795
+ function leastCommonMultiple(a, b) {
4796
+ if (!Number.isInteger(b) || b <= 0)
4797
+ return 0;
4798
+ if (a === 0)
4799
+ return b;
4800
+ let [x, y] = [a, b];
4801
+ while (y > 0)
4802
+ [x, y] = [y, x % y];
4803
+ return a / x * b;
4804
+ }
1575
4805
  var defaultProps4 = {
1576
4806
  fillPatternEnabled: true,
1577
4807
  fillPatternAtlas: {
1578
4808
  type: "image",
1579
4809
  value: null,
1580
- async: true,
1581
- parameters: { lodMaxClamp: 0 }
4810
+ async: true
1582
4811
  },
1583
4812
  fillPatternMapping: { type: "object", value: {}, async: true },
1584
4813
  fillPatternMask: true,
4814
+ fillPatternSizeUnits: "meters",
1585
4815
  getFillPattern: { type: "accessor", value: (d) => d.pattern },
1586
4816
  getFillPatternScale: { type: "accessor", value: 1 },
1587
- getFillPatternOffset: { type: "accessor", value: [0, 0] }
4817
+ getFillPatternOffset: { type: "accessor", value: [0, 0] },
4818
+ getFillPatternBackgroundColor: { type: "accessor", value: [0, 0, 0, 0] }
1588
4819
  };
1589
- var FillStyleExtension = class extends import_core9.LayerExtension {
1590
- constructor({ pattern = false } = {}) {
1591
- super({ pattern });
4820
+ var FillStyleExtension = class extends LayerExtension {
4821
+ constructor({ pattern = false, proceduralPattern = false } = {}) {
4822
+ super({ pattern: pattern || proceduralPattern, proceduralPattern });
1592
4823
  }
1593
4824
  isEnabled(layer) {
1594
4825
  return layer.getAttributeManager() !== null && !("pathTesselator" in layer.state);
@@ -1612,7 +4843,7 @@ var FillStyleExtension = class extends import_core9.LayerExtension {
1612
4843
  size: 4,
1613
4844
  stepMode: "dynamic",
1614
4845
  accessor: "getFillPattern",
1615
- transform: extension.getPatternFrame.bind(this)
4846
+ transform: extension.opts.proceduralPattern ? extension.getProceduralPatternIndex.bind(this) : extension.getPatternFrame.bind(this)
1616
4847
  },
1617
4848
  fillPatternScales: {
1618
4849
  size: 1,
@@ -1624,6 +4855,13 @@ var FillStyleExtension = class extends import_core9.LayerExtension {
1624
4855
  size: 2,
1625
4856
  stepMode: "dynamic",
1626
4857
  accessor: "getFillPatternOffset"
4858
+ },
4859
+ fillPatternBackgroundColors: {
4860
+ size: 4,
4861
+ type: "unorm8",
4862
+ stepMode: "dynamic",
4863
+ accessor: "getFillPatternBackgroundColor",
4864
+ defaultValue: [0, 0, 0, 255]
1627
4865
  }
1628
4866
  });
1629
4867
  }
@@ -1632,7 +4870,8 @@ var FillStyleExtension = class extends import_core9.LayerExtension {
1632
4870
  data: new Uint8Array(4),
1633
4871
  width: 1,
1634
4872
  height: 1
1635
- })
4873
+ }),
4874
+ ...extension.opts.proceduralPattern && extension.createProceduralPatternTexture.call(this, {})
1636
4875
  });
1637
4876
  }
1638
4877
  updateState({ props, oldProps }, extension) {
@@ -1640,30 +4879,81 @@ var FillStyleExtension = class extends import_core9.LayerExtension {
1640
4879
  return;
1641
4880
  }
1642
4881
  if (props.fillPatternMapping && props.fillPatternMapping !== oldProps.fillPatternMapping) {
4882
+ if (extension.opts.proceduralPattern && typeof props.fillPatternMapping !== "string") {
4883
+ const oldTexture = this.state.proceduralPatternTexture;
4884
+ this.setState(extension.createProceduralPatternTexture.call(this, props.fillPatternMapping));
4885
+ oldTexture == null ? void 0 : oldTexture.delete();
4886
+ }
1643
4887
  this.getAttributeManager().invalidate("getFillPattern");
1644
4888
  }
4889
+ if (props.fillPatternMapping !== oldProps.fillPatternMapping || props.getFillPatternScale !== oldProps.getFillPatternScale) {
4890
+ this.setState({ commonFrame: extension.getCommonFrame(props) });
4891
+ }
1645
4892
  }
1646
4893
  draw(params, extension) {
1647
4894
  if (!extension.isEnabled(this)) {
1648
4895
  return;
1649
4896
  }
1650
- const { fillPatternAtlas, fillPatternEnabled, fillPatternMask } = this.props;
4897
+ const { fillPatternAtlas, fillPatternEnabled, fillPatternMask, fillPatternSizeUnits } = this.props;
1651
4898
  const fillProps = {
1652
4899
  project: params.shaderModuleProps.project,
1653
4900
  fillPatternEnabled,
1654
4901
  fillPatternMask,
1655
- fillPatternTexture: fillPatternAtlas || this.state.emptyTexture
4902
+ fillPatternSizeUnits,
4903
+ procedural: extension.opts.proceduralPattern,
4904
+ fillPatternTexture: extension.opts.proceduralPattern ? this.state.proceduralPatternTexture || this.state.emptyTexture : fillPatternAtlas || this.state.emptyTexture,
4905
+ fillPatternCommonFrame: this.state.commonFrame
1656
4906
  };
1657
4907
  this.setShaderModuleProps({ fill: fillProps });
1658
4908
  }
1659
4909
  finalizeState() {
1660
4910
  const emptyTexture = this.state.emptyTexture;
1661
4911
  emptyTexture == null ? void 0 : emptyTexture.delete();
4912
+ const proceduralPatternTexture = this.state.proceduralPatternTexture;
4913
+ proceduralPatternTexture == null ? void 0 : proceduralPatternTexture.delete();
1662
4914
  }
1663
4915
  getPatternFrame(name) {
1664
4916
  const { fillPatternMapping } = this.getCurrentLayer().props;
1665
4917
  const def = fillPatternMapping && fillPatternMapping[name];
1666
- return def ? [def.x, def.y, def.width, def.height] : [0, 0, 0, 0];
4918
+ return def && "x" in def ? [def.x, def.y, def.width, def.height] : [0, 0, 0, 0];
4919
+ }
4920
+ /**
4921
+ * Returns common frame into which all patterns fit as integer multiples
4922
+ */
4923
+ getCommonFrame(props) {
4924
+ const { fillPatternMapping, getFillPatternScale: scale } = props;
4925
+ if (typeof scale !== "number" || !(scale > 0) || typeof fillPatternMapping !== "object") {
4926
+ return null;
4927
+ }
4928
+ let width = 0;
4929
+ let height = 0;
4930
+ for (const name in fillPatternMapping) {
4931
+ const frame = fillPatternMapping[name];
4932
+ if (!("width" in frame))
4933
+ return null;
4934
+ width = leastCommonMultiple(width, frame.width);
4935
+ height = leastCommonMultiple(height, frame.height);
4936
+ if (!width || !height)
4937
+ return null;
4938
+ }
4939
+ return width ? [scale * width, scale * height] : null;
4940
+ }
4941
+ getProceduralPatternIndex(name) {
4942
+ const patternIndices = this.state.proceduralPatternIndices;
4943
+ return [(patternIndices == null ? void 0 : patternIndices.get(name)) ?? 0, 0, 0, 0];
4944
+ }
4945
+ createProceduralPatternTexture(mapping) {
4946
+ const packedPatterns = packProceduralPatterns(mapping);
4947
+ return {
4948
+ proceduralPatternIndices: packedPatterns.patternIndices,
4949
+ proceduralPatternTexture: this.context.device.createTexture({
4950
+ data: packedPatterns.data,
4951
+ width: packedPatterns.width,
4952
+ height: packedPatterns.height,
4953
+ format: PROCEDURAL_PATTERN_TEXTURE_FORMAT,
4954
+ sampler: { minFilter: "nearest", magFilter: "nearest" }
4955
+ })
4956
+ };
1667
4957
  }
1668
4958
  };
1669
4959
  FillStyleExtension.defaultProps = defaultProps4;
@@ -1671,7 +4961,6 @@ FillStyleExtension.extensionName = "FillStyleExtension";
1671
4961
  var fill_style_extension_default = FillStyleExtension;
1672
4962
 
1673
4963
  // dist/clip/clip-extension.js
1674
- var import_core10 = require("@deck.gl/core");
1675
4964
  var defaultProps5 = {
1676
4965
  clipBounds: [0, 0, 1, 1],
1677
4966
  clipByInstance: void 0
@@ -1754,13 +5043,16 @@ in vec2 clip_commonPosition;
1754
5043
  `
1755
5044
  )
1756
5045
  };
1757
- var ClipExtension = class extends import_core10.LayerExtension {
5046
+ var ClipExtension = class extends LayerExtension {
1758
5047
  getShaders() {
1759
5048
  let clipByInstance = "instancePositions" in this.getAttributeManager().attributes;
1760
5049
  if (this.props.clipByInstance !== void 0) {
1761
5050
  clipByInstance = Boolean(this.props.clipByInstance);
1762
5051
  }
1763
5052
  this.state.clipByInstance = clipByInstance;
5053
+ if (this.context.device.type === "webgpu") {
5054
+ return {};
5055
+ }
1764
5056
  return clipByInstance ? {
1765
5057
  modules: [shaderModuleVs],
1766
5058
  inject: injectionVs
@@ -1785,6 +5077,10 @@ var ClipExtension = class extends import_core10.LayerExtension {
1785
5077
  Math.max(corner0[1], corner1[1])
1786
5078
  ];
1787
5079
  }
5080
+ if (this.context.device.type === "webgpu") {
5081
+ clipProps.enabled = true;
5082
+ clipProps.mode = this.state.clipByInstance ? "instance" : "geometry";
5083
+ }
1788
5084
  this.setShaderModuleProps({ clip: clipProps });
1789
5085
  }
1790
5086
  };
@@ -1792,12 +5088,8 @@ ClipExtension.defaultProps = defaultProps5;
1792
5088
  ClipExtension.extensionName = "ClipExtension";
1793
5089
  var clip_extension_default = ClipExtension;
1794
5090
 
1795
- // dist/collision-filter/collision-filter-extension.js
1796
- var import_core15 = require("@deck.gl/core");
1797
-
1798
5091
  // dist/collision-filter/shader-module.js
1799
- var import_core11 = require("@deck.gl/core");
1800
- var vs4 = (
5092
+ var vs5 = (
1801
5093
  /* glsl */
1802
5094
  `
1803
5095
  in float collisionPriorities;
@@ -1895,8 +5187,8 @@ var getCollisionUniforms = (opts) => {
1895
5187
  };
1896
5188
  var shader_module_default2 = {
1897
5189
  name: "collision",
1898
- dependencies: [import_core11.project],
1899
- vs: vs4,
5190
+ dependencies: [project_default],
5191
+ vs: vs5,
1900
5192
  inject: inject4,
1901
5193
  getUniforms: getCollisionUniforms,
1902
5194
  uniformTypes: {
@@ -1906,12 +5198,10 @@ var shader_module_default2 = {
1906
5198
  };
1907
5199
 
1908
5200
  // dist/collision-filter/collision-filter-effect.js
1909
- var import_core13 = require("@math.gl/core");
1910
- var import_core14 = require("@deck.gl/core");
5201
+ var import_core19 = require("@math.gl/core");
1911
5202
 
1912
5203
  // dist/collision-filter/collision-filter-pass.js
1913
- var import_core12 = require("@deck.gl/core");
1914
- var CollisionFilterPass = class extends import_core12._LayersPass {
5204
+ var CollisionFilterPass = class extends LayersPass {
1915
5205
  renderCollisionMap(target, options) {
1916
5206
  const padding = 1;
1917
5207
  const clearColor = [0, 0, 0, 0];
@@ -2002,8 +5292,8 @@ var CollisionFilterEffect = class {
2002
5292
  }
2003
5293
  const needsRender = viewportChanged || // If render info is new
2004
5294
  renderInfo === oldRenderInfo || // If sublayers have changed
2005
- !(0, import_core14._deepEqual)(oldRenderInfo.layers, renderInfo.layers, 1) || // If a sublayer's bounds have been updated
2006
- renderInfo.layerBounds.some((b, i) => !(0, import_core13.equals)(b, oldRenderInfo.layerBounds[i])) || // If a sublayer's isLoaded state has been updated
5295
+ !deepEqual(oldRenderInfo.layers, renderInfo.layers, 1) || // If a sublayer's bounds have been updated
5296
+ renderInfo.layerBounds.some((b, i) => !(0, import_core19.equals)(b, oldRenderInfo.layerBounds[i])) || // If a sublayer's isLoaded state has been updated
2007
5297
  renderInfo.allLayersLoaded !== oldRenderInfo.allLayersLoaded || // Some prop is in transition
2008
5298
  renderInfo.layers.some((layer) => layer.props.transitions);
2009
5299
  this.channels[collisionGroup] = renderInfo;
@@ -2135,7 +5425,7 @@ var defaultProps6 = {
2135
5425
  collisionGroup: { type: "string", value: "default" },
2136
5426
  collisionTestProps: {}
2137
5427
  };
2138
- var CollisionFilterExtension = class extends import_core15.LayerExtension {
5428
+ var CollisionFilterExtension = class extends LayerExtension {
2139
5429
  getShaders() {
2140
5430
  return { modules: [shader_module_default2] };
2141
5431
  }
@@ -2169,11 +5459,7 @@ CollisionFilterExtension.defaultProps = defaultProps6;
2169
5459
  CollisionFilterExtension.extensionName = "CollisionFilterExtension";
2170
5460
  var collision_filter_extension_default = CollisionFilterExtension;
2171
5461
 
2172
- // dist/mask/mask-extension.js
2173
- var import_core21 = require("@deck.gl/core");
2174
-
2175
5462
  // dist/mask/shader-module.js
2176
- var import_core16 = require("@deck.gl/core");
2177
5463
  var uniformBlock4 = (
2178
5464
  /* glsl */
2179
5465
  `layout(std140) uniform maskUniforms {
@@ -2193,7 +5479,7 @@ vec2 mask_getCoords(vec4 position) {
2193
5479
  }
2194
5480
  `
2195
5481
  );
2196
- var vs5 = `
5482
+ var vs6 = `
2197
5483
  ${uniformBlock4}
2198
5484
  ${vertex3}
2199
5485
  `;
@@ -2226,7 +5512,7 @@ bool mask_isInBounds(vec2 texCoords) {
2226
5512
  }
2227
5513
  `
2228
5514
  );
2229
- var fs4 = `
5515
+ var fs5 = `
2230
5516
  ${uniformBlock4}
2231
5517
  ${fragment3}
2232
5518
  `;
@@ -2280,9 +5566,9 @@ var getMaskUniforms = (opts) => {
2280
5566
  };
2281
5567
  var shader_module_default3 = {
2282
5568
  name: "mask",
2283
- dependencies: [import_core16.project],
2284
- vs: vs5,
2285
- fs: fs4,
5569
+ dependencies: [project_default],
5570
+ vs: vs6,
5571
+ fs: fs5,
2286
5572
  inject: inject5,
2287
5573
  getUniforms: getMaskUniforms,
2288
5574
  uniformTypes: {
@@ -2295,11 +5581,9 @@ var shader_module_default3 = {
2295
5581
  };
2296
5582
 
2297
5583
  // dist/mask/mask-effect.js
2298
- var import_core19 = require("@deck.gl/core");
2299
- var import_core20 = require("@math.gl/core");
5584
+ var import_core26 = require("@math.gl/core");
2300
5585
 
2301
5586
  // dist/mask/mask-pass.js
2302
- var import_core17 = require("@deck.gl/core");
2303
5587
  var MASK_BLENDING = {
2304
5588
  blendColorOperation: "subtract",
2305
5589
  blendColorSrcFactor: "zero",
@@ -2308,7 +5592,7 @@ var MASK_BLENDING = {
2308
5592
  blendAlphaSrcFactor: "zero",
2309
5593
  blendAlphaDstFactor: "one"
2310
5594
  };
2311
- var MaskPass = class extends import_core17._LayersPass {
5595
+ var MaskPass = class extends LayersPass {
2312
5596
  constructor(device, props) {
2313
5597
  super(device, props);
2314
5598
  const { mapSize = 2048 } = props;
@@ -2353,8 +5637,7 @@ var MaskPass = class extends import_core17._LayersPass {
2353
5637
  };
2354
5638
 
2355
5639
  // dist/utils/projection-utils.js
2356
- var import_core18 = require("@deck.gl/core");
2357
- var MERCATOR_REFERENCE_VIEWPORT = new import_core18.WebMercatorViewport({
5640
+ var MERCATOR_REFERENCE_VIEWPORT = new WebMercatorViewport({
2358
5641
  width: 1,
2359
5642
  height: 1,
2360
5643
  longitude: 0,
@@ -2416,7 +5699,7 @@ function makeViewport(opts) {
2416
5699
  zoom += Math.log2(r);
2417
5700
  }
2418
5701
  }
2419
- return isGeospatial ? new import_core18.WebMercatorViewport({
5702
+ return isGeospatial ? new WebMercatorViewport({
2420
5703
  id: viewport.id,
2421
5704
  x: border,
2422
5705
  y: border,
@@ -2426,7 +5709,7 @@ function makeViewport(opts) {
2426
5709
  latitude: centerWorld[1],
2427
5710
  zoom,
2428
5711
  orthographic: true
2429
- }) : new import_core18.OrthographicViewport({
5712
+ }) : new OrthographicViewport({
2430
5713
  id: viewport.id,
2431
5714
  x: border,
2432
5715
  y: border,
@@ -2519,7 +5802,7 @@ var MaskEffect = class {
2519
5802
  const viewport = viewports[0];
2520
5803
  const viewportChanged = !this.lastViewport || !this.lastViewport.equals(viewport);
2521
5804
  if (viewport.resolution !== void 0) {
2522
- import_core19.log.warn("MaskExtension is not supported in GlobeView")();
5805
+ log_default.warn("MaskExtension is not supported in GlobeView")();
2523
5806
  return { didRender };
2524
5807
  }
2525
5808
  for (const maskId in channelMap) {
@@ -2562,7 +5845,7 @@ var MaskEffect = class {
2562
5845
  this.lastViewport = viewport;
2563
5846
  const layerBounds = joinLayerBounds(channelInfo.layers, viewport);
2564
5847
  channelInfo.bounds = layerBounds && getRenderBounds(layerBounds, viewport);
2565
- if (maskChanged || !(0, import_core20.equals)(channelInfo.bounds, oldChannelInfo.bounds)) {
5848
+ if (maskChanged || !(0, import_core26.equals)(channelInfo.bounds, oldChannelInfo.bounds)) {
2566
5849
  const { maskPass, maskMap } = this;
2567
5850
  const maskViewport = layerBounds && makeViewport({
2568
5851
  bounds: channelInfo.bounds,
@@ -2613,7 +5896,7 @@ var MaskEffect = class {
2613
5896
  let channelInfo = channelMap[id];
2614
5897
  if (!channelInfo) {
2615
5898
  if (++channelCount > 4) {
2616
- import_core19.log.warn("Too many mask layers. The max supported is 4")();
5899
+ log_default.warn("Too many mask layers. The max supported is 4")();
2617
5900
  continue;
2618
5901
  }
2619
5902
  channelInfo = {
@@ -2674,7 +5957,7 @@ var defaultProps7 = {
2674
5957
  maskByInstance: void 0,
2675
5958
  maskInverted: false
2676
5959
  };
2677
- var MaskExtension = class extends import_core21.LayerExtension {
5960
+ var MaskExtension = class extends LayerExtension {
2678
5961
  initializeState() {
2679
5962
  var _a;
2680
5963
  (_a = this.context.deck) == null ? void 0 : _a._addDefaultEffect(new MaskEffect());
@@ -2702,8 +5985,8 @@ var MaskExtension = class extends import_core21.LayerExtension {
2702
5985
  maskProps.enabled = true;
2703
5986
  maskProps.channel = index;
2704
5987
  maskProps.inverted = maskInverted;
2705
- if (fromCoordinateSystem === import_core21.COORDINATE_SYSTEM.DEFAULT) {
2706
- fromCoordinateSystem = viewport.isGeospatial ? import_core21.COORDINATE_SYSTEM.LNGLAT : import_core21.COORDINATE_SYSTEM.CARTESIAN;
5988
+ if (fromCoordinateSystem === COORDINATE_SYSTEM.DEFAULT) {
5989
+ fromCoordinateSystem = viewport.isGeospatial ? COORDINATE_SYSTEM.LNGLAT : COORDINATE_SYSTEM.CARTESIAN;
2707
5990
  }
2708
5991
  const opts = { modelMatrix: null, fromCoordinateOrigin, fromCoordinateSystem };
2709
5992
  const bl = this.projectPosition([bounds[0], bounds[1], 0], opts);
@@ -2711,7 +5994,7 @@ var MaskExtension = class extends import_core21.LayerExtension {
2711
5994
  maskProps.bounds = [bl[0], bl[1], tr[0], tr[1]];
2712
5995
  } else {
2713
5996
  if (maskId) {
2714
- import_core21.log.warn(`Could not find a mask layer with id: ${maskId}`)();
5997
+ log_default.warn(`Could not find a mask layer with id: ${maskId}`)();
2715
5998
  }
2716
5999
  maskProps.enabled = false;
2717
6000
  }
@@ -2722,14 +6005,7 @@ MaskExtension.defaultProps = defaultProps7;
2722
6005
  MaskExtension.extensionName = "MaskExtension";
2723
6006
  var mask_extension_default = MaskExtension;
2724
6007
 
2725
- // dist/terrain/terrain-extension.js
2726
- var import_core28 = require("@deck.gl/core");
2727
-
2728
- // dist/terrain/terrain-effect.js
2729
- var import_core27 = require("@deck.gl/core");
2730
-
2731
6008
  // dist/terrain/shader-module.js
2732
- var import_core22 = require("@deck.gl/core");
2733
6009
  var TERRAIN_MODE = {
2734
6010
  NONE: 0,
2735
6011
  /** A terrain layer rendering encoded ground elevation into the height map */
@@ -2758,7 +6034,7 @@ uniform sampler2D terrain_map;
2758
6034
  );
2759
6035
  var terrainModule = {
2760
6036
  name: "terrain",
2761
- dependencies: [import_core22.project],
6037
+ dependencies: [project_default],
2762
6038
  vs: `${uniformBlock5}
2763
6039
  out vec3 commonPos;
2764
6040
  // In globe mode, absolute Mercator position for terrain FBO UV lookups
@@ -2856,7 +6132,7 @@ if ((terrain.mode == TERRAIN_MODE_USE_COVER) || (terrain.mode == TERRAIN_MODE_US
2856
6132
  }
2857
6133
  if ("terrainSkipRender" in opts || "drawToTerrainHeightMap" in opts) {
2858
6134
  const { drawToTerrainHeightMap, heightMap, heightMapBounds, dummyHeightMap, terrainCover, useTerrainHeightMap, terrainSkipRender } = opts;
2859
- const projectUniforms = import_core22.project.getUniforms(opts.project);
6135
+ const projectUniforms = project_default.getUniforms(opts.project);
2860
6136
  const { commonOrigin } = projectUniforms;
2861
6137
  let mode = terrainSkipRender ? TERRAIN_MODE.SKIP : TERRAIN_MODE.NONE;
2862
6138
  let sampler = dummyHeightMap;
@@ -2906,11 +6182,7 @@ if ((terrain.mode == TERRAIN_MODE_USE_COVER) || (terrain.mode == TERRAIN_MODE_US
2906
6182
  }
2907
6183
  };
2908
6184
 
2909
- // dist/terrain/terrain-cover.js
2910
- var import_core23 = require("@deck.gl/core");
2911
-
2912
6185
  // dist/terrain/utils.js
2913
- var import_constants = require("@luma.gl/webgl/constants");
2914
6186
  function createRenderTarget(device, opts) {
2915
6187
  return device.createFramebuffer({
2916
6188
  id: opts.id,
@@ -3024,7 +6296,7 @@ var TerrainCover = class {
3024
6296
  } else {
3025
6297
  const oldZoom = (_a = this.renderViewport) == null ? void 0 : _a.zoom;
3026
6298
  shouldRedraw = shouldRedraw || newZoom !== oldZoom;
3027
- const newBounds = viewport instanceof import_core23._GlobeViewport ? this.targetBoundsCommon : getRenderBounds(this.targetBoundsCommon, viewport);
6299
+ const newBounds = viewport instanceof GlobeViewport ? this.targetBoundsCommon : getRenderBounds(this.targetBoundsCommon, viewport);
3028
6300
  const oldBounds = this.bounds;
3029
6301
  shouldRedraw = shouldRedraw || !oldBounds || newBounds.some((x, i) => x !== oldBounds[i]);
3030
6302
  this.bounds = newBounds;
@@ -3101,7 +6373,6 @@ function intersect(b1, b2) {
3101
6373
  }
3102
6374
 
3103
6375
  // dist/terrain/terrain-pass.js
3104
- var import_core24 = require("@deck.gl/core");
3105
6376
  var TERRAIN_BLENDING = {
3106
6377
  blendColorOperation: "max",
3107
6378
  blendColorSrcFactor: "one",
@@ -3110,7 +6381,7 @@ var TERRAIN_BLENDING = {
3110
6381
  blendAlphaSrcFactor: "one",
3111
6382
  blendAlphaDstFactor: "one"
3112
6383
  };
3113
- var TerrainPass = class extends import_core24._LayersPass {
6384
+ var TerrainPass = class extends LayersPass {
3114
6385
  getRenderableLayers(viewport, opts) {
3115
6386
  const { layers } = opts;
3116
6387
  const result = [];
@@ -3175,8 +6446,7 @@ var TerrainPass = class extends import_core24._LayersPass {
3175
6446
  };
3176
6447
 
3177
6448
  // dist/terrain/terrain-picking-pass.js
3178
- var import_core25 = require("@deck.gl/core");
3179
- var TerrainPickingPass = class extends import_core25._PickLayersPass {
6449
+ var TerrainPickingPass = class extends PickLayersPass {
3180
6450
  constructor() {
3181
6451
  super(...arguments);
3182
6452
  this.drawParameters = {};
@@ -3247,7 +6517,6 @@ var TerrainPickingPass = class extends import_core25._PickLayersPass {
3247
6517
  };
3248
6518
 
3249
6519
  // dist/terrain/height-map-builder.js
3250
- var import_core26 = require("@deck.gl/core");
3251
6520
  var MAP_MAX_SIZE = 2048;
3252
6521
  var HeightMapBuilder = class {
3253
6522
  static isSupported(device) {
@@ -3294,7 +6563,7 @@ var HeightMapBuilder = class {
3294
6563
  if (!this.layersBoundsCommon) {
3295
6564
  this.renderViewport = null;
3296
6565
  } else if (layersChanged || viewportChanged) {
3297
- const bounds = viewport instanceof import_core26._GlobeViewport ? this.layersBoundsCommon : getRenderBounds(this.layersBoundsCommon, viewport);
6566
+ const bounds = viewport instanceof GlobeViewport ? this.layersBoundsCommon : getRenderBounds(this.layersBoundsCommon, viewport);
3298
6567
  if (bounds[2] <= bounds[0] || bounds[3] <= bounds[1]) {
3299
6568
  this.renderViewport = null;
3300
6569
  return false;
@@ -3352,7 +6621,7 @@ var TerrainEffect = class {
3352
6621
  if (HeightMapBuilder.isSupported(device)) {
3353
6622
  this.heightMap = new HeightMapBuilder(device);
3354
6623
  } else {
3355
- import_core27.log.warn("Terrain offset mode is not supported by this browser")();
6624
+ log_default.warn("Terrain offset mode is not supported by this browser")();
3356
6625
  }
3357
6626
  deck._addDefaultShaderModule(terrainModule);
3358
6627
  }
@@ -3520,7 +6789,7 @@ var TerrainEffect = class {
3520
6789
  var defaultProps8 = {
3521
6790
  terrainDrawMode: void 0
3522
6791
  };
3523
- var TerrainExtension = class extends import_core28.LayerExtension {
6792
+ var TerrainExtension = class extends LayerExtension {
3524
6793
  getShaders() {
3525
6794
  return {
3526
6795
  modules: [terrainModule]