@deck.gl/geo-layers 9.4.0-beta.1 → 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.
@@ -17,15 +17,15 @@ var __copyProps = (to, from, except, desc) => {
17
17
  }
18
18
  return to;
19
19
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ var __toESM = (mod2, isNodeMode, target) => (target = mod2 != null ? __create(__getProtoOf(mod2)) : {}, __copyProps(
21
21
  // If the importer is in node compatibility mode or this is not an ESM
22
22
  // file that has been converted to a CommonJS file using a Babel-
23
23
  // compatible transform (i.e. "__esModule" has not been set), then set
24
24
  // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
25
+ isNodeMode || !mod2 || !mod2.__esModule ? __defProp(target, "default", { value: mod2, enumerable: true }) : target,
26
+ mod2
27
27
  ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
29
29
 
30
30
  // dist/index.js
31
31
  var index_exports = {};
@@ -768,7 +768,7 @@ fn project_mercator_(lnglat: vec2<f32>) -> vec2<f32> {
768
768
  let y = clamp(lnglat.y, -89.9, 89.9);
769
769
  return vec2<f32>(
770
770
  radians(x) + PI,
771
- PI + log(tan(PI * 0.25 + radians(y) * 0.5))
771
+ PI + log(tan_fp32(PI * 0.25 + radians(y) * 0.5))
772
772
  ) * WORLD_SCALE;
773
773
  }
774
774
 
@@ -1581,6 +1581,10 @@ var import_core2 = require("@math.gl/core");
1581
1581
  function createMat4() {
1582
1582
  return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
1583
1583
  }
1584
+ function mod(value, divisor) {
1585
+ const modulus = value % divisor;
1586
+ return modulus < 0 ? divisor + modulus : modulus;
1587
+ }
1584
1588
  function getCameraPosition(viewMatrixInverse) {
1585
1589
  return [viewMatrixInverse[12], viewMatrixInverse[13], viewMatrixInverse[14]];
1586
1590
  }
@@ -1748,8 +1752,8 @@ var _Viewport = class _Viewport {
1748
1752
  }
1749
1753
  return PROJECTION_MODE.IDENTITY;
1750
1754
  }
1751
- // Two viewports are equal if width and height are identical, and if
1752
- // their view and projection matrices are (approximately) equal.
1755
+ // Two viewports are equal if their dimensions, common-space scale, and
1756
+ // view and projection matrices are (approximately) equal.
1753
1757
  equals(viewport) {
1754
1758
  if (!(viewport instanceof _Viewport)) {
1755
1759
  return false;
@@ -1757,7 +1761,7 @@ var _Viewport = class _Viewport {
1757
1761
  if (this === viewport) {
1758
1762
  return true;
1759
1763
  }
1760
- return viewport.width === this.width && viewport.height === this.height && viewport.scale === this.scale && (0, import_core3.equals)(viewport.projectionMatrix, this.projectionMatrix) && (0, import_core3.equals)(viewport.viewMatrix, this.viewMatrix);
1764
+ 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);
1761
1765
  }
1762
1766
  /**
1763
1767
  * Projects xyz (possibly latitude and longitude) to pixel coordinates in window
@@ -2094,6 +2098,10 @@ var _WebMercatorViewport = class _WebMercatorViewport extends Viewport {
2094
2098
  }
2095
2099
  return this._subViewports;
2096
2100
  }
2101
+ /** Returns whether two Web Mercator viewports use the same projection settings. */
2102
+ equals(viewport) {
2103
+ return viewport instanceof _WebMercatorViewport && viewport._pseudoMeters === this._pseudoMeters && super.equals(viewport);
2104
+ }
2097
2105
  projectPosition(xyz) {
2098
2106
  if (this._pseudoMeters) {
2099
2107
  return super.projectPosition(xyz);
@@ -2453,182 +2461,6 @@ function assert(condition, message) {
2453
2461
  }
2454
2462
  }
2455
2463
 
2456
- // ../core/src/viewports/globe-viewport.ts
2457
- var import_core6 = require("@math.gl/core");
2458
- var import_web_mercator4 = require("@math.gl/web-mercator");
2459
- var import_core7 = require("@math.gl/core");
2460
- var import_web_mercator5 = require("@math.gl/web-mercator");
2461
- var DEGREES_TO_RADIANS2 = Math.PI / 180;
2462
- var RADIANS_TO_DEGREES = 180 / Math.PI;
2463
- var EARTH_RADIUS = 6370972;
2464
- var GLOBE_RADIUS = 256;
2465
- function getDistanceScales2() {
2466
- const unitsPerMeter2 = GLOBE_RADIUS / EARTH_RADIUS;
2467
- const unitsPerDegree = Math.PI / 180 * GLOBE_RADIUS;
2468
- return {
2469
- unitsPerMeter: [unitsPerMeter2, unitsPerMeter2, unitsPerMeter2],
2470
- unitsPerMeter2: [0, 0, 0],
2471
- metersPerUnit: [1 / unitsPerMeter2, 1 / unitsPerMeter2, 1 / unitsPerMeter2],
2472
- unitsPerDegree: [unitsPerDegree, unitsPerDegree, unitsPerMeter2],
2473
- unitsPerDegree2: [0, 0, 0],
2474
- degreesPerUnit: [1 / unitsPerDegree, 1 / unitsPerDegree, 1 / unitsPerMeter2]
2475
- };
2476
- }
2477
- var GlobeViewport = class extends Viewport {
2478
- constructor(opts = {}) {
2479
- const {
2480
- longitude = 0,
2481
- bearing = 0,
2482
- pitch = 0,
2483
- zoom = 0,
2484
- // Matches Maplibre defaults
2485
- // https://github.com/maplibre/maplibre-gl-js/blob/f8ab4b48d59ab8fe7b068b102538793bbdd4c848/src/geo/projection/globe_transform.ts#L632-L633
2486
- nearZMultiplier = 0.5,
2487
- farZMultiplier = 1,
2488
- resolution = 10
2489
- } = opts;
2490
- let { latitude = 0, height, altitude = 1.5, fovy } = opts;
2491
- latitude = Math.max(Math.min(latitude, 90), -90);
2492
- height = height || 1;
2493
- if (fovy) {
2494
- altitude = (0, import_web_mercator4.fovyToAltitude)(fovy);
2495
- } else {
2496
- fovy = (0, import_web_mercator4.altitudeToFovy)(altitude);
2497
- }
2498
- const scaleLatitude = Math.max(Math.min(latitude, import_web_mercator5.MAX_LATITUDE), -import_web_mercator5.MAX_LATITUDE);
2499
- const scale = Math.pow(2, zoom - zoomAdjust(scaleLatitude));
2500
- const pitchRadians = pitch * DEGREES_TO_RADIANS2;
2501
- const nearZ = opts.nearZ ?? nearZMultiplier;
2502
- const farZ = opts.farZ ?? (altitude + GLOBE_RADIUS * 2 * scale / height / Math.max(Math.cos(pitchRadians), 0.1)) * farZMultiplier;
2503
- const viewMatrix = new import_core6.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);
2504
- super({
2505
- ...opts,
2506
- // x, y, width,
2507
- height,
2508
- // view matrix
2509
- viewMatrix,
2510
- longitude,
2511
- latitude,
2512
- zoom,
2513
- // projection matrix parameters
2514
- distanceScales: getDistanceScales2(),
2515
- fovy,
2516
- focalDistance: altitude,
2517
- near: nearZ,
2518
- far: farZ
2519
- });
2520
- this.scale = scale;
2521
- this.latitude = latitude;
2522
- this.longitude = longitude;
2523
- this.bearing = bearing;
2524
- this.pitch = pitch;
2525
- this.fovy = fovy;
2526
- this.resolution = resolution;
2527
- }
2528
- get projectionMode() {
2529
- return PROJECTION_MODE.GLOBE;
2530
- }
2531
- getDistanceScales() {
2532
- return this.distanceScales;
2533
- }
2534
- getBounds(options = {}) {
2535
- const unprojectOption = { targetZ: options.z || 0 };
2536
- const left = this.unproject([0, this.height / 2], unprojectOption);
2537
- const top = this.unproject([this.width / 2, 0], unprojectOption);
2538
- const right = this.unproject([this.width, this.height / 2], unprojectOption);
2539
- const bottom = this.unproject([this.width / 2, this.height], unprojectOption);
2540
- if (right[0] < this.longitude) right[0] += 360;
2541
- if (left[0] > this.longitude) left[0] -= 360;
2542
- return [
2543
- Math.min(left[0], right[0], top[0], bottom[0]),
2544
- Math.min(left[1], right[1], top[1], bottom[1]),
2545
- Math.max(left[0], right[0], top[0], bottom[0]),
2546
- Math.max(left[1], right[1], top[1], bottom[1])
2547
- ];
2548
- }
2549
- unproject(xyz, { topLeft = true, targetZ } = {}) {
2550
- const [x, y, z] = xyz;
2551
- const y2 = topLeft ? y : this.height - y;
2552
- const { pixelUnprojectionMatrix } = this;
2553
- let coord;
2554
- if (Number.isFinite(z)) {
2555
- coord = transformVector(pixelUnprojectionMatrix, [x, y2, z, 1]);
2556
- } else {
2557
- const coord0 = transformVector(pixelUnprojectionMatrix, [x, y2, -1, 1]);
2558
- const coord1 = transformVector(pixelUnprojectionMatrix, [x, y2, 1, 1]);
2559
- const lt = ((targetZ || 0) / EARTH_RADIUS + 1) * GLOBE_RADIUS;
2560
- const lSqr = import_core7.vec3.sqrLen(import_core7.vec3.sub([], coord0, coord1));
2561
- const l0Sqr = import_core7.vec3.sqrLen(coord0);
2562
- const l1Sqr = import_core7.vec3.sqrLen(coord1);
2563
- const sSqr = (4 * l0Sqr * l1Sqr - (lSqr - l0Sqr - l1Sqr) ** 2) / 16;
2564
- const dSqr = 4 * sSqr / lSqr;
2565
- const r0 = Math.sqrt(l0Sqr - dSqr);
2566
- const dr = Math.sqrt(Math.max(0, lt * lt - dSqr));
2567
- const t = (r0 - dr) / Math.sqrt(lSqr);
2568
- coord = import_core7.vec3.lerp([], coord0, coord1, t);
2569
- }
2570
- const [X, Y, Z] = this.unprojectPosition(coord);
2571
- if (Number.isFinite(z)) {
2572
- return [X, Y, Z];
2573
- }
2574
- return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
2575
- }
2576
- projectPosition(xyz) {
2577
- const [lng, lat, Z = 0] = xyz;
2578
- const lambda = lng * DEGREES_TO_RADIANS2;
2579
- const phi = lat * DEGREES_TO_RADIANS2;
2580
- const cosPhi = Math.cos(phi);
2581
- const D = (Z / EARTH_RADIUS + 1) * GLOBE_RADIUS;
2582
- return [Math.sin(lambda) * cosPhi * D, -Math.cos(lambda) * cosPhi * D, Math.sin(phi) * D];
2583
- }
2584
- unprojectPosition(xyz) {
2585
- const [x, y, z] = xyz;
2586
- const D = import_core7.vec3.len(xyz);
2587
- const phi = Math.asin(z / D);
2588
- const lambda = Math.atan2(x, -y);
2589
- const lng = lambda * RADIANS_TO_DEGREES;
2590
- const lat = phi * RADIANS_TO_DEGREES;
2591
- const Z = (D / GLOBE_RADIUS - 1) * EARTH_RADIUS;
2592
- return [lng, lat, Z];
2593
- }
2594
- projectFlat(xyz) {
2595
- return xyz;
2596
- }
2597
- unprojectFlat(xyz) {
2598
- return xyz;
2599
- }
2600
- /**
2601
- * Pan the globe using delta-based movement
2602
- * @param coords - the geographic coordinates where the pan started
2603
- * @param pixel - the current screen position
2604
- * @param startPixel - the screen position where the pan started
2605
- * @returns updated viewport options with new longitude/latitude
2606
- */
2607
- panByPosition([startLng, startLat, startZoom], pixel, startPixel) {
2608
- const scale = Math.pow(2, this.zoom - zoomAdjust(this.latitude));
2609
- const rotationSpeed = 0.25 / scale;
2610
- const longitude = startLng + rotationSpeed * (startPixel[0] - pixel[0]);
2611
- let latitude = startLat - rotationSpeed * (startPixel[1] - pixel[1]);
2612
- latitude = Math.max(Math.min(latitude, 90), -90);
2613
- const out = { longitude, latitude, zoom: startZoom - zoomAdjust(startLat) };
2614
- out.zoom += zoomAdjust(out.latitude);
2615
- return out;
2616
- }
2617
- };
2618
- GlobeViewport.displayName = "GlobeViewport";
2619
- function zoomAdjust(latitude, clampToPoles) {
2620
- if (clampToPoles) {
2621
- latitude = Math.max(Math.min(latitude, import_web_mercator5.MAX_LATITUDE), -import_web_mercator5.MAX_LATITUDE);
2622
- }
2623
- const scaleAdjust = Math.PI * Math.cos(latitude * Math.PI / 180);
2624
- return Math.log2(scaleAdjust);
2625
- }
2626
- function transformVector(matrix, vector) {
2627
- const result = import_core7.vec4.transformMat4([], vector, matrix);
2628
- import_core7.vec4.scale(result, result, 1 / result[3]);
2629
- return result;
2630
- }
2631
-
2632
2464
  // ../../node_modules/@luma.gl/webgl/dist/constants/webgl-constants.js
2633
2465
  var GLEnum;
2634
2466
  (function(GLEnum2) {
@@ -3281,10 +3113,10 @@ var GLEnum;
3281
3113
  })(GLEnum || (GLEnum = {}));
3282
3114
 
3283
3115
  // ../core/src/lib/attribute/data-column.ts
3284
- var import_core9 = require("@luma.gl/core");
3116
+ var import_core7 = require("@luma.gl/core");
3285
3117
 
3286
3118
  // ../core/src/lib/attribute/gl-utils.ts
3287
- var import_core8 = require("@luma.gl/core");
3119
+ var import_core6 = require("@luma.gl/core");
3288
3120
  function typedArrayFromDataType(type) {
3289
3121
  switch (type) {
3290
3122
  case "float64":
@@ -3293,10 +3125,10 @@ function typedArrayFromDataType(type) {
3293
3125
  case "unorm8":
3294
3126
  return Uint8ClampedArray;
3295
3127
  default:
3296
- return (0, import_core8.getTypedArrayConstructor)(type);
3128
+ return (0, import_core6.getTypedArrayConstructor)(type);
3297
3129
  }
3298
3130
  }
3299
- var dataTypeFromTypedArray = import_core8.dataTypeDecoder.getDataType.bind(import_core8.dataTypeDecoder);
3131
+ var dataTypeFromTypedArray = import_core6.dataTypeDecoder.getDataType.bind(import_core6.dataTypeDecoder);
3300
3132
  function getBufferAttributeLayout(name, accessor, deviceType) {
3301
3133
  if (accessor.size > 4) {
3302
3134
  return null;
@@ -3427,6 +3259,7 @@ var DataColumn = class {
3427
3259
  this._buffer = null;
3428
3260
  }
3429
3261
  typed_array_manager_default.release(this.state.allocatedValue);
3262
+ this.state.allocatedValue = null;
3430
3263
  }
3431
3264
  getBuffer() {
3432
3265
  if (this.state.constant && this.device.type !== "webgpu") {
@@ -3546,7 +3379,7 @@ var DataColumn = class {
3546
3379
  let opts;
3547
3380
  if (ArrayBuffer.isView(data)) {
3548
3381
  opts = { value: data };
3549
- } else if (data instanceof import_core9.Buffer) {
3382
+ } else if (data instanceof import_core7.Buffer) {
3550
3383
  opts = { buffer: data };
3551
3384
  } else {
3552
3385
  opts = data;
@@ -3746,7 +3579,7 @@ var DataColumn = class {
3746
3579
  this._buffer.destroy();
3747
3580
  }
3748
3581
  const { isIndexed, type } = this.settings;
3749
- const usage = this.device.type === "webgpu" && !isIndexed ? import_core9.Buffer.VERTEX | import_core9.Buffer.STORAGE | import_core9.Buffer.COPY_DST | import_core9.Buffer.COPY_SRC : (isIndexed ? import_core9.Buffer.INDEX : import_core9.Buffer.VERTEX) | import_core9.Buffer.COPY_DST;
3582
+ const usage = this.device.type === "webgpu" && !isIndexed ? import_core7.Buffer.VERTEX | import_core7.Buffer.STORAGE | import_core7.Buffer.COPY_DST | import_core7.Buffer.COPY_SRC : (isIndexed ? import_core7.Buffer.INDEX : import_core7.Buffer.VERTEX) | import_core7.Buffer.COPY_DST;
3750
3583
  this._buffer = this.device.createBuffer({
3751
3584
  ...(_a = this._buffer) == null ? void 0 : _a.props,
3752
3585
  id: this.id,
@@ -4376,6 +4209,7 @@ ${getComputeBlock(module2.name, inputEntries, output, elementWise, expression)}
4376
4209
  );
4377
4210
  const computation = new import_engine.Computation(outputBuffer.device, {
4378
4211
  source: source7,
4212
+ modules: module2.dependencies,
4379
4213
  shaderAssembler: GPGPU_SHADER_ASSEMBLER,
4380
4214
  shaderLayout: {
4381
4215
  bindings: [
@@ -5767,11 +5601,11 @@ var AttributeManager = class {
5767
5601
  };
5768
5602
 
5769
5603
  // ../core/src/lib/layer.ts
5770
- var import_core12 = require("@luma.gl/core");
5604
+ var import_core10 = require("@luma.gl/core");
5771
5605
  var import_webgl = require("@luma.gl/webgl");
5772
5606
 
5773
5607
  // ../core/src/transitions/cpu-interpolation-transition.ts
5774
- var import_core10 = require("@math.gl/core");
5608
+ var import_core8 = require("@math.gl/core");
5775
5609
  var CPUInterpolationTransition = class extends Transition {
5776
5610
  get value() {
5777
5611
  return this._value;
@@ -5782,7 +5616,7 @@ var CPUInterpolationTransition = class extends Transition {
5782
5616
  settings: { fromValue, toValue, duration, easing }
5783
5617
  } = this;
5784
5618
  const t = easing(time / duration);
5785
- this._value = (0, import_core10.lerp)(fromValue, toValue, t);
5619
+ this._value = (0, import_core8.lerp)(fromValue, toValue, t);
5786
5620
  }
5787
5621
  };
5788
5622
 
@@ -6142,7 +5976,7 @@ function mergeShaders(target, source7) {
6142
5976
  }
6143
5977
 
6144
5978
  // ../core/src/utils/texture.ts
6145
- var import_core11 = require("@luma.gl/core");
5979
+ var import_core9 = require("@luma.gl/core");
6146
5980
  var DEFAULT_TEXTURE_PARAMETERS = {
6147
5981
  minFilter: "linear",
6148
5982
  mipmapFilter: "linear",
@@ -6152,7 +5986,7 @@ var DEFAULT_TEXTURE_PARAMETERS = {
6152
5986
  };
6153
5987
  var internalTextures = {};
6154
5988
  function createTexture(owner, device, image, sampler) {
6155
- if (image instanceof import_core11.Texture) {
5989
+ if (image instanceof import_core9.Texture) {
6156
5990
  return image;
6157
5991
  } else if (image.constructor && image.constructor.name !== "Object") {
6158
5992
  image = { data: image };
@@ -6183,7 +6017,7 @@ function createTexture(owner, device, image, sampler) {
6183
6017
  return texture;
6184
6018
  }
6185
6019
  function destroyTexture(owner, texture) {
6186
- if (!texture || !(texture instanceof import_core11.Texture)) {
6020
+ if (!texture || !(texture instanceof import_core9.Texture)) {
6187
6021
  return;
6188
6022
  }
6189
6023
  if (internalTextures[texture.id] === owner) {
@@ -6811,8 +6645,8 @@ var LayerState = class extends ComponentState {
6811
6645
  };
6812
6646
 
6813
6647
  // ../core/src/lib/layer.ts
6814
- var import_web_mercator6 = require("@math.gl/web-mercator");
6815
- var import_core13 = require("@loaders.gl/core");
6648
+ var import_web_mercator4 = require("@math.gl/web-mercator");
6649
+ var import_core11 = require("@loaders.gl/core");
6816
6650
  var TRACE_CHANGE_FLAG = "layer.changeFlag";
6817
6651
  var TRACE_INITIALIZE = "layer.initialize";
6818
6652
  var TRACE_UPDATE = "layer.update";
@@ -6875,7 +6709,7 @@ var defaultProps = {
6875
6709
  }
6876
6710
  let inResourceManager = resourceManager.contains(url);
6877
6711
  if (!inResourceManager && !loadOptions) {
6878
- resourceManager.add({ resourceId: url, data: (0, import_core13.load)(url, loaders), persistent: false });
6712
+ resourceManager.add({ resourceId: url, data: (0, import_core11.load)(url, loaders), persistent: false });
6879
6713
  inResourceManager = true;
6880
6714
  }
6881
6715
  if (inResourceManager) {
@@ -6889,7 +6723,7 @@ var defaultProps = {
6889
6723
  requestId: propName
6890
6724
  });
6891
6725
  }
6892
- return (0, import_core13.load)(url, loaders, loadOptions);
6726
+ return (0, import_core11.load)(url, loaders, loadOptions);
6893
6727
  }
6894
6728
  },
6895
6729
  updateTriggers: {},
@@ -6959,7 +6793,7 @@ var Layer = class extends Component {
6959
6793
  coordinateOrigin: this.props.coordinateOrigin,
6960
6794
  coordinateSystem: this.props.coordinateSystem
6961
6795
  });
6962
- const [x, y, z] = (0, import_web_mercator6.worldToPixels)(worldPosition, viewport.pixelProjectionMatrix);
6796
+ const [x, y, z] = (0, import_web_mercator4.worldToPixels)(worldPosition, viewport.pixelProjectionMatrix);
6963
6797
  return xyz.length === 2 ? [x, y] : [x, y, z];
6964
6798
  }
6965
6799
  /** Unprojects a screen pixel to the current view's default coordinate system
@@ -7370,7 +7204,7 @@ var Layer = class extends Component {
7370
7204
  const values = changedAttributes[name].getValue();
7371
7205
  for (const attributeName in values) {
7372
7206
  const value = values[attributeName];
7373
- if (value instanceof import_core12.Buffer) {
7207
+ if (value instanceof import_core10.Buffer) {
7374
7208
  if (changedAttributes[name].settings.isIndexed) {
7375
7209
  model.setIndexBuffer(value);
7376
7210
  } else {
@@ -7405,7 +7239,7 @@ var Layer = class extends Component {
7405
7239
  const values = attribute.getValue();
7406
7240
  for (const attributeName in values) {
7407
7241
  const value = values[attributeName];
7408
- if (value instanceof import_core12.Buffer) {
7242
+ if (value instanceof import_core10.Buffer) {
7409
7243
  if (attribute.settings.isIndexed) {
7410
7244
  model.setIndexBuffer(value);
7411
7245
  } else {
@@ -8057,6 +7891,275 @@ var CompositeLayer = class extends Layer {
8057
7891
  };
8058
7892
  CompositeLayer.layerName = "CompositeLayer";
8059
7893
 
7894
+ // ../core/src/viewports/globe-viewport.ts
7895
+ var import_core12 = require("@math.gl/core");
7896
+ var import_web_mercator5 = require("@math.gl/web-mercator");
7897
+ var DEGREES_TO_RADIANS2 = Math.PI / 180;
7898
+ var RADIANS_TO_DEGREES = 180 / Math.PI;
7899
+ var NORTH_UP_BEARING_THRESHOLD = 1;
7900
+ var EARTH_RADIUS = 6370972;
7901
+ var GLOBE_RADIUS = 256;
7902
+ var GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO = 0.75;
7903
+ var GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO = 1.15;
7904
+ function isGlobeNorthUp(bearing) {
7905
+ const normalizedBearing = mod(bearing + 180, 360) - 180;
7906
+ return Math.abs(normalizedBearing) < NORTH_UP_BEARING_THRESHOLD;
7907
+ }
7908
+ function getDistanceScales2() {
7909
+ const unitsPerMeter2 = GLOBE_RADIUS / EARTH_RADIUS;
7910
+ const unitsPerDegree = Math.PI / 180 * GLOBE_RADIUS;
7911
+ return {
7912
+ unitsPerMeter: [unitsPerMeter2, unitsPerMeter2, unitsPerMeter2],
7913
+ unitsPerMeter2: [0, 0, 0],
7914
+ metersPerUnit: [1 / unitsPerMeter2, 1 / unitsPerMeter2, 1 / unitsPerMeter2],
7915
+ unitsPerDegree: [unitsPerDegree, unitsPerDegree, unitsPerMeter2],
7916
+ unitsPerDegree2: [0, 0, 0],
7917
+ degreesPerUnit: [1 / unitsPerDegree, 1 / unitsPerDegree, 1 / unitsPerMeter2]
7918
+ };
7919
+ }
7920
+ var GlobeViewport = class extends Viewport {
7921
+ constructor(opts = {}) {
7922
+ const {
7923
+ longitude = 0,
7924
+ bearing = 0,
7925
+ pitch = 0,
7926
+ zoom = 0,
7927
+ // Matches Maplibre defaults
7928
+ // https://github.com/maplibre/maplibre-gl-js/blob/f8ab4b48d59ab8fe7b068b102538793bbdd4c848/src/geo/projection/globe_transform.ts#L632-L633
7929
+ nearZMultiplier = 0.5,
7930
+ farZMultiplier = 1,
7931
+ resolution = 10
7932
+ } = opts;
7933
+ let { latitude = 0, height, altitude = 1.5, fovy } = opts;
7934
+ latitude = Math.max(Math.min(latitude, 90), -90);
7935
+ height = height || 1;
7936
+ if (fovy) {
7937
+ altitude = (0, import_web_mercator5.fovyToAltitude)(fovy);
7938
+ } else {
7939
+ fovy = (0, import_web_mercator5.altitudeToFovy)(altitude);
7940
+ }
7941
+ const scaleLatitude = Math.max(Math.min(latitude, import_web_mercator5.MAX_LATITUDE), -import_web_mercator5.MAX_LATITUDE);
7942
+ const scale = Math.pow(2, zoom - zoomAdjust(scaleLatitude));
7943
+ const pitchRadians = pitch * DEGREES_TO_RADIANS2;
7944
+ const nearZ = opts.nearZ ?? nearZMultiplier;
7945
+ const farZ = opts.farZ ?? (altitude + GLOBE_RADIUS * 2 * scale / height / Math.max(Math.cos(pitchRadians), 0.1)) * farZMultiplier;
7946
+ const viewMatrix = new import_core12.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);
7947
+ super({
7948
+ ...opts,
7949
+ // x, y, width,
7950
+ height,
7951
+ // view matrix
7952
+ viewMatrix,
7953
+ longitude,
7954
+ latitude,
7955
+ zoom,
7956
+ // projection matrix parameters
7957
+ distanceScales: getDistanceScales2(),
7958
+ fovy,
7959
+ focalDistance: altitude,
7960
+ near: nearZ,
7961
+ far: farZ
7962
+ });
7963
+ this.scale = scale;
7964
+ this.latitude = latitude;
7965
+ this.longitude = longitude;
7966
+ this.bearing = bearing;
7967
+ this.pitch = pitch;
7968
+ this.fovy = fovy;
7969
+ this.resolution = resolution;
7970
+ }
7971
+ get projectionMode() {
7972
+ return PROJECTION_MODE.GLOBE;
7973
+ }
7974
+ getDistanceScales() {
7975
+ return this.distanceScales;
7976
+ }
7977
+ getBounds(options = {}) {
7978
+ const unprojectOption = { targetZ: options.z || 0 };
7979
+ const left = this.unproject([0, this.height / 2], unprojectOption);
7980
+ const top = this.unproject([this.width / 2, 0], unprojectOption);
7981
+ const right = this.unproject([this.width, this.height / 2], unprojectOption);
7982
+ const bottom = this.unproject([this.width / 2, this.height], unprojectOption);
7983
+ if (right[0] < this.longitude) right[0] += 360;
7984
+ if (left[0] > this.longitude) left[0] -= 360;
7985
+ return [
7986
+ Math.min(left[0], right[0], top[0], bottom[0]),
7987
+ Math.min(left[1], right[1], top[1], bottom[1]),
7988
+ Math.max(left[0], right[0], top[0], bottom[0]),
7989
+ Math.max(left[1], right[1], top[1], bottom[1])
7990
+ ];
7991
+ }
7992
+ /**
7993
+ * Builds the screen-pixel → globe-center ray and the intermediate ray/sphere
7994
+ * math reused by `unproject` and anchored zoom. One function so the same
7995
+ * pixelUnprojectionMatrix work isn't duplicated.
7996
+ */
7997
+ _getRayToGlobe(screenPosition, { topLeft = true, targetZ } = {}) {
7998
+ const [screenX, screenY] = screenPosition;
7999
+ const adjustedScreenY = topLeft ? screenY : this.height - screenY;
8000
+ const { pixelUnprojectionMatrix } = this;
8001
+ const rayStartPosition = transformVector(pixelUnprojectionMatrix, [
8002
+ screenX,
8003
+ adjustedScreenY,
8004
+ -1,
8005
+ 1
8006
+ ]);
8007
+ const rayEndPosition = transformVector(pixelUnprojectionMatrix, [
8008
+ screenX,
8009
+ adjustedScreenY,
8010
+ 1,
8011
+ 1
8012
+ ]);
8013
+ const radius = ((targetZ || 0) / EARTH_RADIUS + 1) * GLOBE_RADIUS;
8014
+ const rayLengthSquared = import_core12.vec3.sqrLen(import_core12.vec3.sub([], rayStartPosition, rayEndPosition));
8015
+ const rayStartDistanceSquared = import_core12.vec3.sqrLen(rayStartPosition);
8016
+ const rayEndDistanceSquared = import_core12.vec3.sqrLen(rayEndPosition);
8017
+ const triangleAreaSquared = (4 * rayStartDistanceSquared * rayEndDistanceSquared - (rayLengthSquared - rayStartDistanceSquared - rayEndDistanceSquared) ** 2) / 16;
8018
+ const distanceToCenterSquared = 4 * triangleAreaSquared / rayLengthSquared;
8019
+ return {
8020
+ rayStartPosition,
8021
+ rayEndPosition,
8022
+ radius,
8023
+ rayLengthSquared,
8024
+ rayStartDistanceSquared,
8025
+ distanceToCenterSquared
8026
+ };
8027
+ }
8028
+ _getRayDistanceToGlobeCenterRatio(screenPosition, options) {
8029
+ const { distanceToCenterSquared, radius } = this._getRayToGlobe(screenPosition, options);
8030
+ return Math.sqrt(Math.max(0, distanceToCenterSquared)) / radius;
8031
+ }
8032
+ /**
8033
+ * Returns how strongly a screen position should anchor zoom on the visible globe.
8034
+ * A value of `0` means that the controller should fall back to center zoom.
8035
+ * @param screenPosition - Screen position to evaluate.
8036
+ * @returns Anchor strength from `0` to `1`.
8037
+ */
8038
+ getZoomAnchorStrength(screenPosition) {
8039
+ const distanceRatio = this._getRayDistanceToGlobeCenterRatio(screenPosition);
8040
+ if (distanceRatio >= GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO) {
8041
+ return 0;
8042
+ }
8043
+ const edgeProgress = Math.max(
8044
+ 0,
8045
+ Math.min(
8046
+ 1,
8047
+ (distanceRatio - GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO) / (GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO - GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO)
8048
+ )
8049
+ );
8050
+ const smoothProgress = edgeProgress * edgeProgress * (3 - 2 * edgeProgress);
8051
+ return 1 - smoothProgress;
8052
+ }
8053
+ unproject(xyz, { topLeft = true, targetZ } = {}) {
8054
+ const [x, y, z] = xyz;
8055
+ const y2 = topLeft ? y : this.height - y;
8056
+ const { pixelUnprojectionMatrix } = this;
8057
+ let coord;
8058
+ if (Number.isFinite(z)) {
8059
+ coord = transformVector(pixelUnprojectionMatrix, [x, y2, z, 1]);
8060
+ } else {
8061
+ const {
8062
+ rayStartPosition,
8063
+ rayEndPosition,
8064
+ radius,
8065
+ rayLengthSquared,
8066
+ rayStartDistanceSquared,
8067
+ distanceToCenterSquared
8068
+ } = this._getRayToGlobe(xyz, { topLeft, targetZ });
8069
+ const rayStartToClosestApproach = Math.sqrt(
8070
+ rayStartDistanceSquared - distanceToCenterSquared
8071
+ );
8072
+ const closestApproachToIntersection = Math.sqrt(
8073
+ Math.max(0, radius * radius - distanceToCenterSquared)
8074
+ );
8075
+ const intersectionRatio = (rayStartToClosestApproach - closestApproachToIntersection) / Math.sqrt(rayLengthSquared);
8076
+ coord = import_core12.vec3.lerp([], rayStartPosition, rayEndPosition, intersectionRatio);
8077
+ }
8078
+ const [X, Y, Z] = this.unprojectPosition(coord);
8079
+ if (Number.isFinite(z)) {
8080
+ return [X, Y, Z];
8081
+ }
8082
+ return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
8083
+ }
8084
+ projectPosition(xyz) {
8085
+ const [lng, lat, Z = 0] = xyz;
8086
+ const lambda = lng * DEGREES_TO_RADIANS2;
8087
+ const phi = lat * DEGREES_TO_RADIANS2;
8088
+ const cosPhi = Math.cos(phi);
8089
+ const D = (Z / EARTH_RADIUS + 1) * GLOBE_RADIUS;
8090
+ return [Math.sin(lambda) * cosPhi * D, -Math.cos(lambda) * cosPhi * D, Math.sin(phi) * D];
8091
+ }
8092
+ unprojectPosition(xyz) {
8093
+ const [x, y, z] = xyz;
8094
+ const D = import_core12.vec3.len(xyz);
8095
+ const phi = Math.asin(z / D);
8096
+ const lambda = Math.atan2(x, -y);
8097
+ const lng = lambda * RADIANS_TO_DEGREES;
8098
+ const lat = phi * RADIANS_TO_DEGREES;
8099
+ const Z = (D / GLOBE_RADIUS - 1) * EARTH_RADIUS;
8100
+ return [lng, lat, Z];
8101
+ }
8102
+ projectFlat(xyz) {
8103
+ return xyz;
8104
+ }
8105
+ unprojectFlat(xyz) {
8106
+ return xyz;
8107
+ }
8108
+ /**
8109
+ * Pan the globe to place geographic coordinates at a screen pixel.
8110
+ * When `dragStartPosition` is supplied, applies the delta-based movement used by globe dragging.
8111
+ * @param coordinates - Geographic anchor, or the starting longitude, latitude and zoom.
8112
+ * @param screenPosition - Current screen position.
8113
+ * @param dragStartPosition - Screen position where a drag started.
8114
+ * @returns Updated viewport options.
8115
+ */
8116
+ panByPosition(coordinates, screenPosition, dragStartPosition) {
8117
+ if (!dragStartPosition) {
8118
+ let anchorStrength = this.getZoomAnchorStrength(screenPosition);
8119
+ if (anchorStrength === 0) {
8120
+ return { longitude: this.longitude, latitude: this.latitude };
8121
+ }
8122
+ const currentCoordinates = this.unproject(screenPosition);
8123
+ const longitudeDelta = mod(coordinates[0] - currentCoordinates[0] + 180, 360) - 180;
8124
+ const latitudeDelta = coordinates[1] - currentCoordinates[1];
8125
+ const crossesPole = Math.abs(currentCoordinates[1]) > import_web_mercator5.MAX_LATITUDE || Math.abs(longitudeDelta) > 90;
8126
+ if (isGlobeNorthUp(this.bearing) && crossesPole) {
8127
+ return { longitude: this.longitude, latitude: this.latitude };
8128
+ }
8129
+ if (isGlobeNorthUp(this.bearing) && latitudeDelta !== 0) {
8130
+ const latitudeLimit = latitudeDelta > 0 ? import_web_mercator5.MAX_LATITUDE : -import_web_mercator5.MAX_LATITUDE;
8131
+ const latitudeConstraintStrength = (latitudeLimit - this.latitude) / latitudeDelta;
8132
+ anchorStrength = Math.min(anchorStrength, Math.max(0, latitudeConstraintStrength));
8133
+ }
8134
+ const longitude2 = this.longitude + longitudeDelta * anchorStrength;
8135
+ const latitude2 = Math.max(Math.min(this.latitude + latitudeDelta * anchorStrength, 90), -90);
8136
+ return { longitude: longitude2, latitude: latitude2 };
8137
+ }
8138
+ const [startLongitude, startLatitude, startZoom] = coordinates;
8139
+ const scale = Math.pow(2, this.zoom - zoomAdjust(this.latitude));
8140
+ const rotationSpeed = 0.25 / scale;
8141
+ const longitude = startLongitude + rotationSpeed * (dragStartPosition[0] - screenPosition[0]);
8142
+ let latitude = startLatitude - rotationSpeed * (dragStartPosition[1] - screenPosition[1]);
8143
+ latitude = Math.max(Math.min(latitude, 90), -90);
8144
+ const nextViewState = { longitude, latitude, zoom: startZoom - zoomAdjust(startLatitude) };
8145
+ nextViewState.zoom += zoomAdjust(nextViewState.latitude);
8146
+ return nextViewState;
8147
+ }
8148
+ };
8149
+ GlobeViewport.displayName = "GlobeViewport";
8150
+ function zoomAdjust(latitude, clampToPoles) {
8151
+ if (clampToPoles) {
8152
+ latitude = Math.max(Math.min(latitude, import_web_mercator5.MAX_LATITUDE), -import_web_mercator5.MAX_LATITUDE);
8153
+ }
8154
+ const scaleAdjust = Math.PI * Math.cos(latitude * Math.PI / 180);
8155
+ return Math.log2(scaleAdjust);
8156
+ }
8157
+ function transformVector(matrix, vector) {
8158
+ const result = import_core12.vec4.transformMat4([], vector, matrix);
8159
+ import_core12.vec4.scale(result, result, 1 / result[3]);
8160
+ return result;
8161
+ }
8162
+
8060
8163
  // ../core/src/lib/layer-extension.ts
8061
8164
  var LayerExtension = class {
8062
8165
  static get componentName() {
@@ -8118,7 +8221,7 @@ LayerExtension.defaultProps = {};
8118
8221
  LayerExtension.extensionName = "LayerExtension";
8119
8222
 
8120
8223
  // ../core/src/utils/tesselator.ts
8121
- var import_core14 = require("@luma.gl/core");
8224
+ var import_core13 = require("@luma.gl/core");
8122
8225
  var Tesselator = class {
8123
8226
  constructor(opts) {
8124
8227
  this.indexStarts = [0];
@@ -8237,7 +8340,7 @@ var Tesselator = class {
8237
8340
  instanceCount = vertexStarts[data.length] || 0;
8238
8341
  if (ArrayBuffer.isView(geometryBuffer)) {
8239
8342
  instanceCount = instanceCount || geometryBuffer.length / this.positionSize;
8240
- } else if (geometryBuffer instanceof import_core14.Buffer) {
8343
+ } else if (geometryBuffer instanceof import_core13.Buffer) {
8241
8344
  const byteStride = this.positionSize * 4;
8242
8345
  instanceCount = instanceCount || geometryBuffer.byteLength / byteStride;
8243
8346
  } else if (geometryBuffer.buffer) {
@@ -9111,10 +9214,10 @@ ArcLayer.defaultProps = defaultProps2;
9111
9214
 
9112
9215
  // ../layers/src/bitmap-layer/bitmap-layer.ts
9113
9216
  var import_engine5 = require("@luma.gl/engine");
9114
- var import_web_mercator7 = require("@math.gl/web-mercator");
9217
+ var import_web_mercator6 = require("@math.gl/web-mercator");
9115
9218
 
9116
9219
  // ../layers/src/bitmap-layer/create-mesh.ts
9117
- var import_core16 = require("@math.gl/core");
9220
+ var import_core15 = require("@math.gl/core");
9118
9221
  var DEFAULT_INDICES = new Uint32Array([0, 2, 1, 0, 3, 2]);
9119
9222
  var DEFAULT_TEX_COORDS = new Float32Array([0, 1, 0, 0, 1, 0, 1, 1]);
9120
9223
  function createMesh(bounds, resolution) {
@@ -9180,7 +9283,7 @@ function createQuad(bounds) {
9180
9283
  };
9181
9284
  }
9182
9285
  function interpolateQuad(quad, ut, vt) {
9183
- return (0, import_core16.lerp)((0, import_core16.lerp)(quad[0], quad[1], vt), (0, import_core16.lerp)(quad[3], quad[2], vt), ut);
9286
+ return (0, import_core15.lerp)((0, import_core15.lerp)(quad[0], quad[1], vt), (0, import_core15.lerp)(quad[3], quad[2], vt), ut);
9184
9287
  }
9185
9288
 
9186
9289
  // ../layers/src/bitmap-layer/bitmap-layer-uniforms.ts
@@ -9665,8 +9768,8 @@ var BitmapLayer = class extends Layer {
9665
9768
  return { coordinateConversion: -1, bounds };
9666
9769
  }
9667
9770
  if (imageCoordinateSystem === "cartesian" && defaultImageCoordinateSystem === "lnglat") {
9668
- const bottomLeft = (0, import_web_mercator7.lngLatToWorld)([bounds[0], bounds[1]]);
9669
- const topRight = (0, import_web_mercator7.lngLatToWorld)([bounds[2], bounds[3]]);
9771
+ const bottomLeft = (0, import_web_mercator6.lngLatToWorld)([bounds[0], bounds[1]]);
9772
+ const topRight = (0, import_web_mercator6.lngLatToWorld)([bounds[2], bounds[3]]);
9670
9773
  return {
9671
9774
  coordinateConversion: 1,
9672
9775
  bounds: [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]]
@@ -10007,7 +10110,7 @@ function getShaderWGSL(useRowIndexes) {
10007
10110
  }
10008
10111
 
10009
10112
  // ../layers/src/icon-layer/icon-manager.ts
10010
- var import_core18 = require("@loaders.gl/core");
10113
+ var import_core17 = require("@loaders.gl/core");
10011
10114
  var DEFAULT_CANVAS_WIDTH = 1024;
10012
10115
  var DEFAULT_BUFFER = 4;
10013
10116
  var noop = () => {
@@ -10266,7 +10369,7 @@ var IconManager = class {
10266
10369
  });
10267
10370
  for (const icon of icons) {
10268
10371
  this._pendingCount++;
10269
- (0, import_core18.load)(icon.url, this._loadOptions).then((imageData) => {
10372
+ (0, import_core17.load)(icon.url, this._loadOptions).then((imageData) => {
10270
10373
  var _a;
10271
10374
  const id = getIconId(icon);
10272
10375
  const iconDef = this._mapping[id];
@@ -12338,6 +12441,25 @@ var PathTesselator = class extends Tesselator {
12338
12441
  get(attributeName) {
12339
12442
  return this.attributes[attributeName];
12340
12443
  }
12444
+ /** Get rendered segment indexes in source traversal order. */
12445
+ getPathSegmentIndices(dataIndex) {
12446
+ const segmentTypes = this.attributes.segmentTypes;
12447
+ const rowStart = this.vertexStarts[dataIndex];
12448
+ const rowEnd = Math.min(
12449
+ this.vertexStarts[dataIndex + 1] ?? this.instanceCount,
12450
+ this.instanceCount
12451
+ );
12452
+ const result = [];
12453
+ for (let segmentIndex = rowStart; segmentIndex < rowEnd - 1; segmentIndex++) {
12454
+ if ((segmentTypes[segmentIndex] & INVALID) === 0) {
12455
+ result.push(segmentIndex);
12456
+ }
12457
+ }
12458
+ if (result.length && (segmentTypes[rowStart] & INVALID) !== 0) {
12459
+ result.unshift(result.pop());
12460
+ }
12461
+ return result;
12462
+ }
12341
12463
  /* Implement base Tesselator interface */
12342
12464
  getGeometryFromBuffer(buffer) {
12343
12465
  if (this.normalize || this.opts.isWebGPU) {
@@ -12533,6 +12655,9 @@ struct Varyings {
12533
12655
  @location(5) vJointType: f32,
12534
12656
  // Location 6 is reserved for TripsLayer's injected vTime varying.
12535
12657
  @location(7) clipCoordinates: vec2<f32>,
12658
+ #ifdef DASH_ENABLED
12659
+ @location(8) vPathBounds: vec2<f32>,
12660
+ #endif
12536
12661
  };
12537
12662
 
12538
12663
  fn flipIfTrue(flag: bool) -> f32 {
@@ -12547,11 +12672,34 @@ fn clipLine(position: vec4<f32>, refPosition: vec4<f32>) -> vec4<f32> {
12547
12672
  return position;
12548
12673
  }
12549
12674
 
12675
+ #ifdef DASH_ENABLED
12676
+ // Return the visible interval of the original segment before clipLine moves either endpoint.
12677
+ fn getClippedPathRange(startW: f32, endW: f32) -> vec2<f32> {
12678
+ let startClipped = startW < EPSILON;
12679
+ let endClipped = endW < EPSILON;
12680
+ if (startClipped && endClipped) {
12681
+ return vec2<f32>(0.0, 0.0);
12682
+ }
12683
+ if (startClipped || endClipped) {
12684
+ let intersection = clamp((EPSILON - startW) / (endW - startW), 0.0, 1.0);
12685
+ if (startClipped) {
12686
+ return vec2<f32>(intersection, 1.0);
12687
+ }
12688
+ return vec2<f32>(0.0, intersection);
12689
+ }
12690
+ return vec2<f32>(0.0, 1.0);
12691
+ }
12692
+ #endif
12693
+
12550
12694
  fn getLineJoinOffset(
12551
12695
  prevPoint: vec3<f32>,
12552
12696
  currPoint: vec3<f32>,
12553
12697
  nextPoint: vec3<f32>,
12554
12698
  width: vec2<f32>,
12699
+ #ifdef DASH_ENABLED
12700
+ sourcePathLength: f32,
12701
+ sourcePathRange: vec2<f32>,
12702
+ #endif
12555
12703
  #ifdef ANTIALIASING
12556
12704
  coverageScale: f32,
12557
12705
  #endif
@@ -12588,7 +12736,43 @@ fn getLineJoinOffset(
12588
12736
  let miterVec = vec2<f32>(-tangent.y, tangent.x);
12589
12737
  let dir = select(dirB, dirA, isEnd);
12590
12738
  let perp = select(perpB, perpA, isEnd);
12739
+ #ifdef DASH_ENABLED
12740
+ let segmentLength2D = select(lenB, lenA, isEnd);
12741
+
12742
+ // Extrusion happens in the XY plane, so segmentLength2D is a 2D length and pathPosition.y
12743
+ // below measures 2D distance along the segment. For a path that also moves in Z the true
12744
+ // arc length is longer by this ratio. Scaling pathLength and pathPosition.y by it makes
12745
+ // the coordinate measure real 3D distance while leaving the joint tests unchanged, since
12746
+ // they compare the two against each other and both are scaled alike. Billboard mode
12747
+ // extrudes in clip space, where the perspective divide has already reduced the segment to
12748
+ // its screen projection, so its complete common-space length is supplied by the caller.
12749
+ // Mirrors path-layer-vertex.glsl.ts.
12750
+ let currDelta3 = select(deltaB3, deltaA3, isEnd);
12751
+ let currLength2D = length(currDelta3.xy);
12752
+ // Do not clamp a valid denominator to EPSILON: high-zoom Web Mercator deltas are often
12753
+ // smaller than that in common space, and changing their scale corrupts even flat paths.
12754
+ let safeLength2D = select(1.0, currLength2D, currLength2D > 0.0);
12755
+ var arcLengthRatio = 1.0;
12756
+ var pathPositionOffset = 0.0;
12757
+ var pathLength = segmentLength2D;
12758
+ if (path.billboard != 0.0) {
12759
+ // clipLine may shorten the visible screen-space segment. Preserve the corresponding interval
12760
+ // of the complete common-space arclength instead of compressing the full dash period into the
12761
+ // visible span. Keep pathLength complete so justification is stable as the camera clips it.
12762
+ let visiblePathLength = sourcePathLength * (sourcePathRange.y - sourcePathRange.x);
12763
+ arcLengthRatio = 0.0;
12764
+ if (segmentLength2D > 0.0) {
12765
+ arcLengthRatio = visiblePathLength / segmentLength2D;
12766
+ }
12767
+ pathPositionOffset = sourcePathLength * sourcePathRange.x;
12768
+ pathLength = sourcePathLength;
12769
+ } else if (currLength2D > 0.0) {
12770
+ arcLengthRatio = length(currDelta3) / safeLength2D;
12771
+ pathLength = segmentLength2D * arcLengthRatio;
12772
+ }
12773
+ #else
12591
12774
  let pathLength = select(lenB, lenA, isEnd);
12775
+ #endif
12592
12776
 
12593
12777
  let sinHalfA = abs(dot(miterVec, perp));
12594
12778
  let cosHalfA = abs(dot(dirA, miterVec));
@@ -12635,7 +12819,11 @@ fn getLineJoinOffset(
12635
12819
  #endif
12636
12820
  let pathPosition = vec2<f32>(
12637
12821
  dot(offsetFromStartOfPath, perp),
12822
+ #ifdef DASH_ENABLED
12823
+ pathPositionOffset + dot(offsetFromStartOfPath, dir) * arcLengthRatio
12824
+ #else
12638
12825
  dot(offsetFromStartOfPath, dir)
12826
+ #endif
12639
12827
  );
12640
12828
  let isValid = step(f32(instanceTypes), 3.5);
12641
12829
  #ifdef ANTIALIASING
@@ -12694,15 +12882,38 @@ fn vertexMain(attributes: Attributes) -> Varyings {
12694
12882
  ) / 2.0;
12695
12883
 
12696
12884
  if (path.billboard != 0.0) {
12697
- var prevPositionScreen = project_position_to_clipspace(prevPosition, prevPosition64Low, ZERO_OFFSET);
12885
+ #ifdef DASH_ENABLED
12886
+ let prevProjection = project_position_to_clipspace_and_commonspace(
12887
+ prevPosition, prevPosition64Low, ZERO_OFFSET
12888
+ );
12889
+ let nextProjection = project_position_to_clipspace_and_commonspace(
12890
+ nextPosition, nextPosition64Low, ZERO_OFFSET
12891
+ );
12892
+ let prevPositionCommon = prevProjection.commonPosition.xyz;
12893
+ let nextPositionCommon = nextProjection.commonPosition.xyz;
12894
+ var prevPositionScreen = prevProjection.clipPosition;
12895
+ var nextPositionScreen = nextProjection.clipPosition;
12896
+ #else
12897
+ var prevPositionScreen = project_position_to_clipspace(
12898
+ prevPosition, prevPosition64Low, ZERO_OFFSET
12899
+ );
12900
+ var nextPositionScreen = project_position_to_clipspace(
12901
+ nextPosition, nextPosition64Low, ZERO_OFFSET
12902
+ );
12903
+ #endif
12698
12904
  let currProjection = project_position_to_clipspace_and_commonspace(
12699
- currPosition,
12700
- currPosition64Low,
12701
- ZERO_OFFSET
12905
+ currPosition, currPosition64Low, ZERO_OFFSET
12702
12906
  );
12703
12907
  geometry.position = currProjection.commonPosition;
12704
12908
  var currPositionScreen = currProjection.clipPosition;
12705
- var nextPositionScreen = project_position_to_clipspace(nextPosition, nextPosition64Low, ZERO_OFFSET);
12909
+ #ifdef DASH_ENABLED
12910
+ let currPositionCommon = currProjection.commonPosition.xyz;
12911
+ let sourcePathStartScreen = mix(currPositionScreen, prevPositionScreen, isEnd);
12912
+ let sourcePathEndScreen = mix(nextPositionScreen, currPositionScreen, isEnd);
12913
+ let billboardPathRange = getClippedPathRange(
12914
+ sourcePathStartScreen.w, sourcePathEndScreen.w
12915
+ );
12916
+ #endif
12706
12917
 
12707
12918
  prevPositionScreen = clipLine(prevPositionScreen, currPositionScreen);
12708
12919
  nextPositionScreen = clipLine(nextPositionScreen, currPositionScreen);
@@ -12714,18 +12925,39 @@ fn vertexMain(attributes: Attributes) -> Varyings {
12714
12925
  (widthPixels + 0.5 / project.devicePixelRatio) / max(widthPixels, 1e-6),
12715
12926
  widthPixels > 0.0
12716
12927
  );
12928
+ #endif
12929
+ #ifdef DASH_ENABLED
12930
+ let currentDeltaCommon = select(
12931
+ nextPositionCommon - currPositionCommon,
12932
+ currPositionCommon - prevPositionCommon,
12933
+ isEnd > 0.0
12934
+ );
12935
+ let billboardPathLength = select(
12936
+ 0.0,
12937
+ length(currentDeltaCommon) * project.scale / (widthPixels * project.focalDistance),
12938
+ widthPixels > 0.0
12939
+ );
12717
12940
  #endif
12718
12941
  let join = getLineJoinOffset(
12719
12942
  prevPositionScreen.xyz / prevPositionScreen.w,
12720
12943
  currPositionScreen.xyz / currPositionScreen.w,
12721
12944
  nextPositionScreen.xyz / nextPositionScreen.w,
12722
12945
  project_pixel_size_to_clipspace(vec2<f32>(widthPixels, widthPixels)),
12946
+ #ifdef DASH_ENABLED
12947
+ billboardPathLength,
12948
+ billboardPathRange,
12949
+ #endif
12723
12950
  #ifdef ANTIALIASING
12724
12951
  coverageScale,
12725
12952
  #endif
12726
12953
  attributes.positions,
12727
12954
  attributes.instanceTypes
12728
12955
  );
12956
+ #ifdef DASH_ENABLED
12957
+ // Phase and justification use the complete source segment, while cap and joint coverage
12958
+ // must still recognize the endpoints moved by clipLine.
12959
+ varyings.vPathBounds = billboardPathLength * billboardPathRange;
12960
+ #endif
12729
12961
 
12730
12962
  geometry.uv = join.pathPosition;
12731
12963
  varyings.position = vec4<f32>(
@@ -12758,12 +12990,19 @@ fn vertexMain(attributes: Attributes) -> Varyings {
12758
12990
  currPositionCommon,
12759
12991
  nextPositionCommon,
12760
12992
  width,
12993
+ #ifdef DASH_ENABLED
12994
+ 1.0,
12995
+ vec2<f32>(0.0, 1.0),
12996
+ #endif
12761
12997
  #ifdef ANTIALIASING
12762
12998
  coverageScale,
12763
12999
  #endif
12764
13000
  attributes.positions,
12765
13001
  attributes.instanceTypes
12766
13002
  );
13003
+ #ifdef DASH_ENABLED
13004
+ varyings.vPathBounds = vec2<f32>(0.0, join.pathLength);
13005
+ #endif
12767
13006
 
12768
13007
  geometry.position = vec4<f32>(currPositionCommon + join.offset, 1.0);
12769
13008
  geometry.uv = join.pathPosition;
@@ -12794,7 +13033,13 @@ fn fragmentMain(varyings: Varyings) -> @location(0) vec4<f32> {
12794
13033
  // bounded by the corner offset, everywhere else by the edge of the stroke. Dividing by the
12795
13034
  // screen-space derivative converts the distance to the boundary into device pixels, which stays
12796
13035
  // correct under perspective foreshortening and under extensions that rescale the stroke.
13036
+ #ifdef DASH_ENABLED
13037
+ let isCorner =
13038
+ varyings.vPathPosition.y < varyings.vPathBounds.x ||
13039
+ varyings.vPathPosition.y > varyings.vPathBounds.y;
13040
+ #else
12797
13041
  let isCorner = varyings.vPathPosition.y < 0.0 || varyings.vPathPosition.y > varyings.vPathLength;
13042
+ #endif
12798
13043
  let isRound = varyings.vJointType > 0.5;
12799
13044
 
12800
13045
  // Distance to the silhouette in device pixels, from the derivative of the coordinate that
@@ -12805,7 +13050,13 @@ fn fragmentMain(varyings: Varyings) -> @location(0) vec4<f32> {
12805
13050
  // Both evaluated so each derivative stays on one field across the corner/body boundary
12806
13051
  let bodyPixels = (1.0 - bodyCoord) / max(fwidth(bodyCoord), 1e-6);
12807
13052
  let cornerPixels = (1.0 - cornerCoord) / max(fwidth(cornerCoord), 1e-6);
13053
+ #ifdef PATH_STYLE_OFFSET
13054
+ // Rounded corners still intersect the stroke-width envelope. Extensions may remap
13055
+ // vPathPosition.x independently of vCornerOffset, as PathStyleExtension does for offsets.
13056
+ let edgePixels = select(bodyPixels, min(cornerPixels, bodyPixels), isRound && isCorner);
13057
+ #else
12808
13058
  let edgePixels = select(bodyPixels, cornerPixels, isRound && isCorner);
13059
+ #endif
12809
13060
 
12810
13061
  // Fragments outside the coverage ramp must not write depth or picking colors.
12811
13062
  if (edgePixels <= -SMOOTH_EDGE_RADIUS) {
@@ -12823,11 +13074,18 @@ fn fragmentMain(varyings: Varyings) -> @location(0) vec4<f32> {
12823
13074
  // Feather one device pixel across the width only, before premultiplication. edgePixels is a
12824
13075
  // signed device-pixel distance and SMOOTH_EDGE_RADIUS is 0.5, so this ramps across one pixel.
12825
13076
  color.a *= smoothedge(0.0, edgePixels);
13077
+ #else
13078
+ #ifdef DASH_ENABLED
13079
+ if (
13080
+ varyings.vPathPosition.y < varyings.vPathBounds.x ||
13081
+ varyings.vPathPosition.y > varyings.vPathBounds.y
13082
+ ) {
12826
13083
  #else
12827
13084
  if (
12828
13085
  varyings.vPathPosition.y < 0.0 ||
12829
13086
  varyings.vPathPosition.y > varyings.vPathLength
12830
13087
  ) {
13088
+ #endif
12831
13089
  if (varyings.vJointType > 0.5 && length(varyings.vCornerOffset) > 1.0) {
12832
13090
  discard;
12833
13091
  }
@@ -12880,6 +13138,9 @@ out float vMiterLength;
12880
13138
  out vec2 vPathPosition;
12881
13139
  out float vPathLength;
12882
13140
  out float vJointType;
13141
+ #ifdef DASH_ENABLED
13142
+ out vec2 vPathBounds;
13143
+ #endif
12883
13144
 
12884
13145
  const float EPSILON = 0.001;
12885
13146
  const vec3 ZERO_OFFSET = vec3(0.0);
@@ -12892,6 +13153,9 @@ float flipIfTrue(bool flag) {
12892
13153
  vec3 getLineJoinOffset(
12893
13154
  vec3 prevPoint, vec3 currPoint, vec3 nextPoint,
12894
13155
  vec2 width
13156
+ #ifdef DASH_ENABLED
13157
+ , float sourcePathLength, vec2 sourcePathRange
13158
+ #endif
12895
13159
  #ifdef ANTIALIASING
12896
13160
  , float coverageScale
12897
13161
  #endif
@@ -12934,6 +13198,37 @@ vec3 getLineJoinOffset(
12934
13198
  // length of the segment
12935
13199
  float L = isEnd ? lenA : lenB;
12936
13200
 
13201
+ #ifdef DASH_ENABLED
13202
+ // Extrusion happens in the XY plane, so L above is a 2D length and vPathPosition.y below
13203
+ // measures 2D distance along the segment. For a path that also moves in Z the true arc
13204
+ // length is longer by this ratio. Scaling vPathLength and vPathPosition.y by it makes the
13205
+ // coordinate measure real 3D distance - which is what CPU-side dash offsets accumulate -
13206
+ // while leaving the joint tests in the fragment shader unchanged, since they compare the
13207
+ // two against each other and both are scaled alike.
13208
+ // Billboard mode extrudes in clip space, where the perspective divide has already reduced
13209
+ // the segment to its screen projection, so its complete common-space length is supplied by
13210
+ // the caller.
13211
+ vec3 currDelta3 = isEnd ? deltaA3 : deltaB3;
13212
+ float currLength2D = length(currDelta3.xy);
13213
+ float arcLengthRatio = 1.0;
13214
+ float pathPositionOffset = 0.0;
13215
+ float pathLength = L;
13216
+ if (path.billboard) {
13217
+ // clipLine may shorten the visible screen-space segment. Preserve the corresponding interval
13218
+ // of the complete common-space arclength instead of compressing the full dash period into the
13219
+ // visible span. Keep pathLength complete so justification is stable as the camera clips it.
13220
+ float visiblePathLength = sourcePathLength * (sourcePathRange.y - sourcePathRange.x);
13221
+ arcLengthRatio = L > 0.0 ? visiblePathLength / L : 0.0;
13222
+ pathPositionOffset = sourcePathLength * sourcePathRange.x;
13223
+ pathLength = sourcePathLength;
13224
+ } else if (currLength2D > 0.0) {
13225
+ // Do not clamp a valid denominator to EPSILON: high-zoom Web Mercator deltas are often
13226
+ // smaller than that in common space, and changing their scale corrupts even flat paths.
13227
+ arcLengthRatio = length(currDelta3) / currLength2D;
13228
+ pathLength = L * arcLengthRatio;
13229
+ }
13230
+ #endif
13231
+
12937
13232
  // A = angle of the corner
12938
13233
  float sinHalfA = abs(dot(miterVec, perp));
12939
13234
  float cosHalfA = abs(dot(dirA, miterVec));
@@ -12975,7 +13270,11 @@ vec3 getLineJoinOffset(
12975
13270
  // The physical stroke still ends at offsetVec; the scaled coordinates and vertices only extend
12976
13271
  // its rasterized envelope to include the outer half of the centered coverage ramp.
12977
13272
  vec2 coverageOffsetVec = offsetVec * coverageScale;
13273
+ #ifdef DASH_ENABLED
13274
+ vPathLength = pathLength;
13275
+ #else
12978
13276
  vPathLength = L;
13277
+ #endif
12979
13278
  vCornerOffset = coverageOffsetVec;
12980
13279
  vMiterLength = dot(vCornerOffset, miterVec * turnDirection);
12981
13280
  vMiterLength = isCap ? isJoint : vMiterLength;
@@ -12983,7 +13282,11 @@ vec3 getLineJoinOffset(
12983
13282
  vec2 offsetFromStartOfPath = coverageOffsetVec + deltaA * float(isEnd);
12984
13283
  vPathPosition = vec2(
12985
13284
  dot(offsetFromStartOfPath, perp),
13285
+ #ifdef DASH_ENABLED
13286
+ pathPositionOffset + dot(offsetFromStartOfPath, dir) * arcLengthRatio
13287
+ #else
12986
13288
  dot(offsetFromStartOfPath, dir)
13289
+ #endif
12987
13290
  );
12988
13291
  geometry.uv = vPathPosition;
12989
13292
 
@@ -12991,7 +13294,11 @@ vec3 getLineJoinOffset(
12991
13294
  vec3 offset = vec3(coverageOffsetVec * width * isValid, 0.0);
12992
13295
  #else
12993
13296
  // Generate variables for fragment shader
13297
+ #ifdef DASH_ENABLED
13298
+ vPathLength = pathLength;
13299
+ #else
12994
13300
  vPathLength = L;
13301
+ #endif
12995
13302
  vCornerOffset = offsetVec;
12996
13303
  vMiterLength = dot(vCornerOffset, miterVec * turnDirection);
12997
13304
  vMiterLength = isCap ? isJoint : vMiterLength;
@@ -12999,7 +13306,11 @@ vec3 getLineJoinOffset(
12999
13306
  vec2 offsetFromStartOfPath = vCornerOffset + deltaA * float(isEnd);
13000
13307
  vPathPosition = vec2(
13001
13308
  dot(offsetFromStartOfPath, perp),
13309
+ #ifdef DASH_ENABLED
13310
+ pathPositionOffset + dot(offsetFromStartOfPath, dir) * arcLengthRatio
13311
+ #else
13002
13312
  dot(offsetFromStartOfPath, dir)
13313
+ #endif
13003
13314
  );
13004
13315
  geometry.uv = vPathPosition;
13005
13316
 
@@ -13021,6 +13332,22 @@ void clipLine(inout vec4 position, vec4 refPosition) {
13021
13332
  }
13022
13333
  }
13023
13334
 
13335
+ #ifdef DASH_ENABLED
13336
+ // Return the visible interval of the original segment before clipLine moves either endpoint.
13337
+ vec2 getClippedPathRange(float startW, float endW) {
13338
+ bool startClipped = startW < EPSILON;
13339
+ bool endClipped = endW < EPSILON;
13340
+ if (startClipped && endClipped) {
13341
+ return vec2(0.0);
13342
+ }
13343
+ if (startClipped || endClipped) {
13344
+ float intersection = clamp((EPSILON - startW) / (endW - startW), 0.0, 1.0);
13345
+ return startClipped ? vec2(intersection, 1.0) : vec2(0.0, intersection);
13346
+ }
13347
+ return vec2(0.0, 1.0);
13348
+ }
13349
+ #endif
13350
+
13024
13351
  void main() {
13025
13352
  geometry.pickingColor = picking_getPickingColorFromIndex(rowIndexes);
13026
13353
 
@@ -13045,9 +13372,35 @@ void main() {
13045
13372
 
13046
13373
  if (path.billboard) {
13047
13374
  // Extrude in clipspace
13048
- vec4 prevPositionScreen = project_position_to_clipspace(prevPosition, prevPosition64Low, ZERO_OFFSET);
13375
+ #ifdef DASH_ENABLED
13376
+ vec4 prevPositionCommon;
13377
+ vec4 nextPositionCommon;
13378
+ vec4 prevPositionScreen = project_position_to_clipspace(
13379
+ prevPosition, prevPosition64Low, ZERO_OFFSET, prevPositionCommon
13380
+ );
13381
+ #else
13382
+ vec4 prevPositionScreen = project_position_to_clipspace(
13383
+ prevPosition, prevPosition64Low, ZERO_OFFSET
13384
+ );
13385
+ #endif
13049
13386
  vec4 currPositionScreen = project_position_to_clipspace(currPosition, currPosition64Low, ZERO_OFFSET, geometry.position);
13050
- vec4 nextPositionScreen = project_position_to_clipspace(nextPosition, nextPosition64Low, ZERO_OFFSET);
13387
+ #ifdef DASH_ENABLED
13388
+ vec4 nextPositionScreen = project_position_to_clipspace(
13389
+ nextPosition, nextPosition64Low, ZERO_OFFSET, nextPositionCommon
13390
+ );
13391
+ #else
13392
+ vec4 nextPositionScreen = project_position_to_clipspace(
13393
+ nextPosition, nextPosition64Low, ZERO_OFFSET
13394
+ );
13395
+ #endif
13396
+
13397
+ #ifdef DASH_ENABLED
13398
+ vec4 sourcePathStartScreen = mix(currPositionScreen, prevPositionScreen, isEnd);
13399
+ vec4 sourcePathEndScreen = mix(nextPositionScreen, currPositionScreen, isEnd);
13400
+ vec2 billboardPathRange = getClippedPathRange(
13401
+ sourcePathStartScreen.w, sourcePathEndScreen.w
13402
+ );
13403
+ #endif
13051
13404
 
13052
13405
  clipLine(prevPositionScreen, currPositionScreen);
13053
13406
  clipLine(nextPositionScreen, currPositionScreen);
@@ -13062,16 +13415,34 @@ void main() {
13062
13415
  : 1.0;
13063
13416
  #endif
13064
13417
 
13418
+ #ifdef DASH_ENABLED
13419
+ vec3 currentDeltaCommon = isEnd > 0.0
13420
+ ? geometry.position.xyz - prevPositionCommon.xyz
13421
+ : nextPositionCommon.xyz - geometry.position.xyz;
13422
+ float billboardPathLength = width.x > 0.0
13423
+ ? length(currentDeltaCommon) * project.scale / (width.x * project.focalDistance)
13424
+ : 0.0;
13425
+ #endif
13426
+
13065
13427
  vec3 offset = getLineJoinOffset(
13066
13428
  prevPositionScreen.xyz / prevPositionScreen.w,
13067
13429
  currPositionScreen.xyz / currPositionScreen.w,
13068
13430
  nextPositionScreen.xyz / nextPositionScreen.w,
13069
13431
  project_pixel_size_to_clipspace(width.xy)
13432
+ #ifdef DASH_ENABLED
13433
+ ,
13434
+ billboardPathLength, billboardPathRange
13435
+ #endif
13070
13436
  #ifdef ANTIALIASING
13071
13437
  ,
13072
13438
  coverageScale
13073
13439
  #endif
13074
13440
  );
13441
+ #ifdef DASH_ENABLED
13442
+ // Phase and justification use the complete source segment, while cap and joint coverage
13443
+ // must still recognize the endpoints moved by clipLine.
13444
+ vPathBounds = billboardPathLength * billboardPathRange;
13445
+ #endif
13075
13446
 
13076
13447
  DECKGL_FILTER_GL_POSITION(currPositionScreen, geometry);
13077
13448
  gl_Position = vec4(currPositionScreen.xyz + offset * currPositionScreen.w, currPositionScreen.w);
@@ -13092,10 +13463,16 @@ void main() {
13092
13463
 
13093
13464
  vec3 offset = getLineJoinOffset(
13094
13465
  prevPosition, currPosition, nextPosition, width.xy
13466
+ #ifdef DASH_ENABLED
13467
+ , 1.0, vec2(0.0, 1.0)
13468
+ #endif
13095
13469
  #ifdef ANTIALIASING
13096
13470
  , coverageScale
13097
13471
  #endif
13098
13472
  );
13473
+ #ifdef DASH_ENABLED
13474
+ vPathBounds = vec2(0.0, vPathLength);
13475
+ #endif
13099
13476
  geometry.position = vec4(currPosition + offset, 1.0);
13100
13477
  gl_Position = project_common_position_to_clipspace(geometry.position);
13101
13478
  DECKGL_FILTER_GL_POSITION(gl_Position, geometry);
@@ -13121,6 +13498,9 @@ in float vMiterLength;
13121
13498
  in vec2 vPathPosition;
13122
13499
  in float vPathLength;
13123
13500
  in float vJointType;
13501
+ #ifdef DASH_ENABLED
13502
+ in vec2 vPathBounds;
13503
+ #endif
13124
13504
 
13125
13505
  out vec4 fragColor;
13126
13506
 
@@ -13128,7 +13508,11 @@ void main(void) {
13128
13508
  geometry.uv = vPathPosition;
13129
13509
 
13130
13510
  #ifdef ANTIALIASING
13511
+ #ifdef DASH_ENABLED
13512
+ bool isCorner = vPathPosition.y < vPathBounds.x || vPathPosition.y > vPathBounds.y;
13513
+ #else
13131
13514
  bool isCorner = vPathPosition.y < 0.0 || vPathPosition.y > vPathLength;
13515
+ #endif
13132
13516
  bool isRound = vJointType > 0.5;
13133
13517
 
13134
13518
  // Distance to the silhouette in device pixels, from the derivative of the coordinate that
@@ -13139,7 +13523,13 @@ void main(void) {
13139
13523
  // Both evaluated so each derivative stays on one field across the corner/body boundary
13140
13524
  float bodyPixels = (1.0 - bodyCoord) / max(fwidth(bodyCoord), 1e-6);
13141
13525
  float cornerPixels = (1.0 - cornerCoord) / max(fwidth(cornerCoord), 1e-6);
13526
+ #ifdef PATH_STYLE_OFFSET
13527
+ // Rounded corners still intersect the stroke-width envelope. Extensions may remap
13528
+ // vPathPosition.x independently of vCornerOffset, as PathStyleExtension does for offsets.
13529
+ float edgePixels = isRound && isCorner ? min(cornerPixels, bodyPixels) : bodyPixels;
13530
+ #else
13142
13531
  float edgePixels = isRound && isCorner ? cornerPixels : bodyPixels;
13532
+ #endif
13143
13533
 
13144
13534
  // Fragments outside the coverage ramp must not write depth or picking colors.
13145
13535
  if (edgePixels <= -SMOOTH_EDGE_RADIUS) {
@@ -13158,8 +13548,12 @@ void main(void) {
13158
13548
  // edgePixels is a signed device-pixel distance, and SMOOTH_EDGE_RADIUS is 0.5, so smoothedge
13159
13549
  // ramps across exactly one pixel centered on the edge.
13160
13550
  fragColor.a *= smoothedge(0.0, edgePixels);
13551
+ #else
13552
+ #ifdef DASH_ENABLED
13553
+ if (vPathPosition.y < vPathBounds.x || vPathPosition.y > vPathBounds.y) {
13161
13554
  #else
13162
13555
  if (vPathPosition.y < 0.0 || vPathPosition.y > vPathLength) {
13556
+ #endif
13163
13557
  // if joint is rounded, test distance from the corner
13164
13558
  if (vJointType > 0.5 && length(vCornerOffset) > 1.0) {
13165
13559
  discard;
@@ -13200,6 +13594,18 @@ var ATTRIBUTE_TRANSITION = {
13200
13594
  return chunk.length ? chunk.subarray(chunk.length - value.length) : value;
13201
13595
  }
13202
13596
  };
13597
+ function getPathProjectionScale(viewport) {
13598
+ if (viewport.isGeospatial) {
13599
+ return null;
13600
+ }
13601
+ const { unitsPerMeter: unitsPerMeter2 } = viewport.distanceScales;
13602
+ return [unitsPerMeter2[0], unitsPerMeter2[1], unitsPerMeter2[2]];
13603
+ }
13604
+ function pathProjectionScalesEqual(left, right) {
13605
+ return left === right || Boolean(
13606
+ left && right && left.length === right.length && left.every((value, index) => value === right[index])
13607
+ );
13608
+ }
13203
13609
  var PathLayer = class extends Layer {
13204
13610
  getShaders() {
13205
13611
  const { antialiasing } = this.props;
@@ -13227,6 +13633,43 @@ var PathLayer = class extends Layer {
13227
13633
  }
13228
13634
  return (_a = this.getAttributeManager()) == null ? void 0 : _a.getBounds(["vertexPositions"]);
13229
13635
  }
13636
+ getPathProjectionScale(viewport) {
13637
+ var _a;
13638
+ const coordinateSystem = this.props.coordinateSystem;
13639
+ const hasDashMetrics = Boolean((_a = this.getAttributeManager()) == null ? void 0 : _a.getAttributes().instanceDashOffsets);
13640
+ if (!hasDashMetrics) {
13641
+ return null;
13642
+ }
13643
+ const trackViewportScale = viewport instanceof WebMercatorViewport && viewport.zoom >= 12 && // Offset coordinate systems use their fixed coordinateOrigin as the projection origin.
13644
+ // Default/lnglat and preprojected Cartesian paths follow the viewport center instead.
13645
+ (coordinateSystem === "default" || coordinateSystem === "lnglat" || coordinateSystem === "cartesian");
13646
+ if (trackViewportScale) {
13647
+ const projectUniforms = project_default.getUniforms({
13648
+ viewport,
13649
+ coordinateSystem,
13650
+ coordinateOrigin: this.props.coordinateOrigin,
13651
+ autoWrapLongitude: this.wrapLongitude
13652
+ });
13653
+ return [
13654
+ viewport.projectionMode,
13655
+ projectUniforms.coordinateOrigin[1],
13656
+ projectUniforms.commonOrigin[1],
13657
+ ...projectUniforms.commonUnitsPerWorldUnit,
13658
+ ...projectUniforms.commonUnitsPerWorldUnit2,
13659
+ projectUniforms.commonUnitsPerMeter[2]
13660
+ ];
13661
+ }
13662
+ const projectionScale = getPathProjectionScale(viewport);
13663
+ return projectionScale ? [viewport.projectionMode, ...projectionScale] : [viewport.projectionMode];
13664
+ }
13665
+ shouldUpdateState(params) {
13666
+ var _a, _b;
13667
+ const { viewport } = this.context;
13668
+ return super.shouldUpdateState(params) || ((_a = this.state) == null ? void 0 : _a.tessellationResolution) !== viewport.resolution || !pathProjectionScalesEqual(
13669
+ (_b = this.state) == null ? void 0 : _b.pathProjectionScale,
13670
+ this.getPathProjectionScale(viewport)
13671
+ );
13672
+ }
13230
13673
  initializeState() {
13231
13674
  const noAlloc = true;
13232
13675
  const isWebGPU = this.context.device.type === "webgpu";
@@ -13317,7 +13760,9 @@ var PathLayer = class extends Layer {
13317
13760
  pathTesselator: new PathTesselator({
13318
13761
  fp64: this.use64bitPositions(),
13319
13762
  isWebGPU
13320
- })
13763
+ }),
13764
+ tessellationResolution: this.context.viewport.resolution,
13765
+ pathProjectionScale: this.getPathProjectionScale(this.context.viewport)
13321
13766
  });
13322
13767
  }
13323
13768
  updateState(params) {
@@ -13325,7 +13770,16 @@ var PathLayer = class extends Layer {
13325
13770
  super.updateState(params);
13326
13771
  const { props, oldProps, changeFlags } = params;
13327
13772
  const attributeManager = this.getAttributeManager();
13328
- const geometryChanged = changeFlags.dataChanged || changeFlags.updateTriggersChanged && (changeFlags.updateTriggersChanged.all || changeFlags.updateTriggersChanged.getPath);
13773
+ const { viewport } = this.context;
13774
+ const tessellationResolutionChanged = this.state.tessellationResolution !== viewport.resolution;
13775
+ const pathProjectionScale = this.getPathProjectionScale(viewport);
13776
+ const pathProjectionScaleChanged = !pathProjectionScalesEqual(
13777
+ this.state.pathProjectionScale,
13778
+ pathProjectionScale
13779
+ );
13780
+ const getPathChanged = changeFlags.updateTriggersChanged && (changeFlags.updateTriggersChanged.all || changeFlags.updateTriggersChanged.getPath);
13781
+ const geometryConfigurationChanged = getPathChanged || props._pathType !== oldProps._pathType || props.positionFormat !== oldProps.positionFormat || props.wrapLongitude !== oldProps.wrapLongitude || tessellationResolutionChanged;
13782
+ const geometryChanged = changeFlags.dataChanged || geometryConfigurationChanged;
13329
13783
  if (geometryChanged) {
13330
13784
  const { pathTesselator } = this.state;
13331
13785
  const buffers = props.data.attributes || {};
@@ -13339,16 +13793,24 @@ var PathLayer = class extends Layer {
13339
13793
  positionFormat: props.positionFormat,
13340
13794
  wrapLongitude: props.wrapLongitude,
13341
13795
  // TODO - move the flag out of the viewport
13342
- resolution: this.context.viewport.resolution,
13343
- dataChanged: changeFlags.dataChanged
13796
+ resolution: viewport.resolution,
13797
+ // A partial data diff is only valid while normalization inputs remain unchanged.
13798
+ dataChanged: geometryConfigurationChanged ? void 0 : changeFlags.dataChanged
13344
13799
  });
13345
13800
  this.setState({
13346
13801
  numInstances: pathTesselator.instanceCount,
13347
- startIndices: pathTesselator.vertexStarts
13802
+ startIndices: pathTesselator.vertexStarts,
13803
+ tessellationResolution: viewport.resolution,
13804
+ pathProjectionScale
13348
13805
  });
13349
- if (!changeFlags.dataChanged) {
13806
+ if (!changeFlags.dataChanged || geometryConfigurationChanged) {
13350
13807
  attributeManager.invalidateAll();
13808
+ } else if (pathProjectionScaleChanged) {
13809
+ attributeManager.invalidate("instanceDashOffsets");
13351
13810
  }
13811
+ } else if (pathProjectionScaleChanged) {
13812
+ this.setState({ pathProjectionScale });
13813
+ attributeManager.invalidate("instanceDashOffsets");
13352
13814
  }
13353
13815
  if (changeFlags.extensionsChanged || props.antialiasing !== oldProps.antialiasing) {
13354
13816
  (_a = this.state.model) == null ? void 0 : _a.destroy();
@@ -17837,7 +18299,7 @@ var import_a5_js = require("a5-js");
17837
18299
 
17838
18300
  // dist/h3-layers/h3-utils.js
17839
18301
  var import_h3_js = require("h3-js");
17840
- var import_core39 = require("@math.gl/core");
18302
+ var import_core38 = require("@math.gl/core");
17841
18303
  function normalizeLongitudes(vertices, refLng) {
17842
18304
  refLng = refLng === void 0 ? vertices[0][0] : refLng;
17843
18305
  for (const pt of vertices) {
@@ -17855,8 +18317,8 @@ function scalePolygon(hexId, vertices, factor) {
17855
18317
  normalizeLongitudes(vertices, lng);
17856
18318
  const vertexCount = vertices[0] === vertices[actualCount - 1] ? actualCount - 1 : actualCount;
17857
18319
  for (let i = 0; i < vertexCount; i++) {
17858
- vertices[i][0] = (0, import_core39.lerp)(lng, vertices[i][0], factor);
17859
- vertices[i][1] = (0, import_core39.lerp)(lat, vertices[i][1], factor);
18320
+ vertices[i][0] = (0, import_core38.lerp)(lng, vertices[i][0], factor);
18321
+ vertices[i][1] = (0, import_core38.lerp)(lat, vertices[i][1], factor);
17860
18322
  }
17861
18323
  }
17862
18324
  function getHexagonCentroid(getHexagon, object, objectInfo) {
@@ -17908,14 +18370,14 @@ A5Layer.defaultProps = defaultProps16;
17908
18370
  var a5_layer_default = A5Layer;
17909
18371
 
17910
18372
  // dist/wms-layer/wms-layer.js
17911
- var import_core41 = require("@loaders.gl/core");
18373
+ var import_core40 = require("@loaders.gl/core");
17912
18374
  var import_wms = require("@loaders.gl/wms");
17913
18375
 
17914
18376
  // dist/wms-layer/utils.js
17915
- var import_web_mercator8 = require("@math.gl/web-mercator");
18377
+ var import_web_mercator7 = require("@math.gl/web-mercator");
17916
18378
  var HALF_EARTH_CIRCUMFERENCE = 6378137 * Math.PI;
17917
18379
  function WGS84ToPseudoMercator(coord) {
17918
- const mercator = (0, import_web_mercator8.lngLatToWorld)(coord);
18380
+ const mercator = (0, import_web_mercator7.lngLatToWorld)(coord);
17919
18381
  mercator[0] = (mercator[0] / 256 - 1) * HALF_EARTH_CIRCUMFERENCE;
17920
18382
  mercator[1] = (mercator[1] / 256 - 1) * HALF_EARTH_CIRCUMFERENCE;
17921
18383
  return mercator;
@@ -18001,7 +18463,7 @@ var WMSLayer = class extends CompositeLayer {
18001
18463
  return props.data;
18002
18464
  }
18003
18465
  if (typeof props.data === "string") {
18004
- return (0, import_core41.createDataSource)(props.data, [import_wms.WMSSource], {
18466
+ return (0, import_core40.createDataSource)(props.data, [import_wms.WMSSource], {
18005
18467
  core: {
18006
18468
  type: props.serviceType,
18007
18469
  loadOptions: props.loadOptions
@@ -18282,7 +18744,7 @@ S2Layer.defaultProps = defaultProps19;
18282
18744
  var s2_layer_default = S2Layer;
18283
18745
 
18284
18746
  // dist/quadkey-layer/quadkey-utils.js
18285
- var import_web_mercator9 = require("@math.gl/web-mercator");
18747
+ var import_web_mercator8 = require("@math.gl/web-mercator");
18286
18748
  var TILE_SIZE = 512;
18287
18749
  function quadkeyToWorldBounds(quadkey, coverage) {
18288
18750
  let x = 0;
@@ -18304,8 +18766,8 @@ function quadkeyToWorldBounds(quadkey, coverage) {
18304
18766
  }
18305
18767
  function getQuadkeyPolygon(quadkey, coverage = 1) {
18306
18768
  const [topLeft, bottomRight] = quadkeyToWorldBounds(quadkey, coverage);
18307
- const [w, n] = (0, import_web_mercator9.worldToLngLat)(topLeft);
18308
- const [e, s] = (0, import_web_mercator9.worldToLngLat)(bottomRight);
18769
+ const [w, n] = (0, import_web_mercator8.worldToLngLat)(topLeft);
18770
+ const [e, s] = (0, import_web_mercator8.worldToLngLat)(bottomRight);
18309
18771
  return [e, n, e, s, w, s, w, n, e, n];
18310
18772
  }
18311
18773
 
@@ -18332,7 +18794,7 @@ var quadkey_layer_default = QuadkeyLayer;
18332
18794
 
18333
18795
  // dist/tileset-2d/tileset-2d.js
18334
18796
  var import_loader_utils = require("@loaders.gl/loader-utils");
18335
- var import_core43 = require("@math.gl/core");
18797
+ var import_core42 = require("@math.gl/core");
18336
18798
 
18337
18799
  // dist/tileset-2d/tile-2d-header.js
18338
18800
  var Tile2DHeader = class {
@@ -18458,7 +18920,7 @@ var Tile2DHeader = class {
18458
18920
 
18459
18921
  // dist/tileset-2d/tile-2d-traversal.js
18460
18922
  var import_culling = require("@math.gl/culling");
18461
- var import_web_mercator10 = require("@math.gl/web-mercator");
18923
+ var import_web_mercator9 = require("@math.gl/web-mercator");
18462
18924
  var TILE_SIZE2 = 512;
18463
18925
  var MAX_MAPS = 3;
18464
18926
  var REF_POINTS_5 = [
@@ -18599,8 +19061,8 @@ function getOSMTileIndices(viewport, maxZ, zRange, bounds) {
18599
19061
  const minZ = viewport instanceof WebMercatorViewport && viewport.pitch <= 60 ? maxZ : 0;
18600
19062
  if (bounds) {
18601
19063
  const [minLng, minLat, maxLng, maxLat] = bounds;
18602
- const topLeft = (0, import_web_mercator10.lngLatToWorld)([minLng, maxLat]);
18603
- const bottomRight = (0, import_web_mercator10.lngLatToWorld)([maxLng, minLat]);
19064
+ const topLeft = (0, import_web_mercator9.lngLatToWorld)([minLng, maxLat]);
19065
+ const bottomRight = (0, import_web_mercator9.lngLatToWorld)([maxLng, minLat]);
18604
19066
  bounds = [topLeft[0], TILE_SIZE2 - topLeft[1], bottomRight[0], TILE_SIZE2 - bottomRight[1]];
18605
19067
  }
18606
19068
  const root = new OSMNode(0, 0, 0);
@@ -18942,8 +19404,8 @@ var Tileset2D = class {
18942
19404
  this._zRange = null;
18943
19405
  this._selectedTiles = null;
18944
19406
  this._frameNumber = 0;
18945
- this._modelMatrix = new import_core43.Matrix4();
18946
- this._modelMatrixInverse = new import_core43.Matrix4();
19407
+ this._modelMatrix = new import_core42.Matrix4();
19408
+ this._modelMatrixInverse = new import_core42.Matrix4();
18947
19409
  }
18948
19410
  /* Public API */
18949
19411
  get tiles() {
@@ -18996,9 +19458,9 @@ var Tileset2D = class {
18996
19458
  zRange: null,
18997
19459
  modelMatrix: null
18998
19460
  }) {
18999
- const modelMatrixAsMatrix4 = modelMatrix2 ? new import_core43.Matrix4(modelMatrix2) : new import_core43.Matrix4();
19461
+ const modelMatrixAsMatrix4 = modelMatrix2 ? new import_core42.Matrix4(modelMatrix2) : new import_core42.Matrix4();
19000
19462
  const isModelMatrixNew = !modelMatrixAsMatrix4.equals(this._modelMatrix);
19001
- if (!this._viewport || !viewport.equals(this._viewport) || !(0, import_core43.equals)(this._zRange, zRange) || isModelMatrixNew) {
19463
+ if (!this._viewport || !viewport.equals(this._viewport) || !(0, import_core42.equals)(this._zRange, zRange) || isModelMatrixNew) {
19002
19464
  if (isModelMatrixNew) {
19003
19465
  this._modelMatrixInverse = modelMatrixAsMatrix4.clone().invert();
19004
19466
  this._modelMatrix = modelMatrixAsMatrix4;
@@ -19047,7 +19509,7 @@ var Tileset2D = class {
19047
19509
  if ("west" in bbox) {
19048
19510
  overlaps = bbox.west < maxX && bbox.east > minX && bbox.south < maxY && bbox.north > minY;
19049
19511
  } else {
19050
- if (modelMatrix2 && !import_core43.Matrix4.IDENTITY.equals(modelMatrix2)) {
19512
+ if (modelMatrix2 && !import_core42.Matrix4.IDENTITY.equals(modelMatrix2)) {
19051
19513
  const [left, top, right, bottom] = transformBox([bbox.left, bbox.top, bbox.right, bbox.bottom], modelMatrix2);
19052
19514
  bbox = { left, top, right, bottom };
19053
19515
  }
@@ -19355,7 +19817,7 @@ function getPlaceholderInChildren(tile) {
19355
19817
  }
19356
19818
 
19357
19819
  // dist/tile-layer/tile-layer.js
19358
- var import_core45 = require("@math.gl/core");
19820
+ var import_core44 = require("@math.gl/core");
19359
19821
  var defaultProps21 = {
19360
19822
  TilesetClass: Tileset2D,
19361
19823
  data: { type: "data", value: [] },
@@ -19552,7 +20014,7 @@ var TileLayer = class extends CompositeLayer {
19552
20014
  filterSubLayer({ layer, cullRect }) {
19553
20015
  const { tile } = layer.props;
19554
20016
  const { modelMatrix: modelMatrix2 } = this.props;
19555
- return this.state.tileset.isTileVisible(tile, cullRect, modelMatrix2 ? new import_core45.Matrix4(modelMatrix2) : null);
20017
+ return this.state.tileset.isTileVisible(tile, cullRect, modelMatrix2 ? new import_core44.Matrix4(modelMatrix2) : null);
19556
20018
  }
19557
20019
  };
19558
20020
  TileLayer.defaultProps = defaultProps21;
@@ -19903,7 +20365,7 @@ var h3_cluster_layer_default = H3ClusterLayer;
19903
20365
  var import_engine19 = require("@luma.gl/engine");
19904
20366
 
19905
20367
  // ../mesh-layers/src/simple-mesh-layer/simple-mesh-layer.ts
19906
- var import_core51 = require("@luma.gl/core");
20368
+ var import_core50 = require("@luma.gl/core");
19907
20369
  var import_engine16 = require("@luma.gl/engine");
19908
20370
 
19909
20371
  // ../mesh-layers/src/utils/matrix.ts
@@ -20432,7 +20894,7 @@ var SimpleMeshLayer = class extends Layer {
20432
20894
  }
20433
20895
  this.getAttributeManager().invalidateAll();
20434
20896
  }
20435
- if (props.texture !== oldProps.texture && props.texture instanceof import_core51.Texture) {
20897
+ if (props.texture !== oldProps.texture && props.texture instanceof import_core50.Texture) {
20436
20898
  this.setTexture(props.texture);
20437
20899
  }
20438
20900
  if (this.state.model) {
@@ -21373,7 +21835,7 @@ MeshLayer.defaultProps = defaultProps27;
21373
21835
  var mesh_layer_default = MeshLayer;
21374
21836
 
21375
21837
  // dist/tile-3d-layer/tile-3d-layer.js
21376
- var import_core54 = require("@loaders.gl/core");
21838
+ var import_core53 = require("@loaders.gl/core");
21377
21839
  var import_tiles = require("@loaders.gl/tiles");
21378
21840
  var import_d_tiles = require("@loaders.gl/3d-tiles");
21379
21841
  var SINGLE_DATA = [0];
@@ -21510,7 +21972,7 @@ var Tile3DLayer = class extends CompositeLayer {
21510
21972
  }
21511
21973
  Object.assign(options, preloadOptions);
21512
21974
  }
21513
- const tilesetJson = await (0, import_core54.load)(actualTilesetUrl, loader, options.loadOptions);
21975
+ const tilesetJson = await (0, import_core53.load)(actualTilesetUrl, loader, options.loadOptions);
21514
21976
  const tileset3d = new import_tiles.Tileset3D(tilesetJson, {
21515
21977
  onTileLoad: this._onTileLoad.bind(this),
21516
21978
  onTileUnload: this._onTileUnload.bind(this),
@@ -22060,12 +22522,12 @@ ClipExtension.defaultProps = defaultProps30;
22060
22522
  ClipExtension.extensionName = "ClipExtension";
22061
22523
 
22062
22524
  // dist/mvt-layer/mvt-layer.js
22063
- var import_core59 = require("@math.gl/core");
22525
+ var import_core58 = require("@math.gl/core");
22064
22526
  var import_mvt = require("@loaders.gl/mvt");
22065
22527
  var import_gis = require("@loaders.gl/gis");
22066
22528
 
22067
22529
  // dist/mvt-layer/coordinate-transform.js
22068
- var import_core57 = require("@math.gl/core");
22530
+ var import_core56 = require("@math.gl/core");
22069
22531
  var availableTransformations = {
22070
22532
  Point,
22071
22533
  MultiPoint,
@@ -22075,8 +22537,8 @@ var availableTransformations = {
22075
22537
  MultiPolygon
22076
22538
  };
22077
22539
  function Point([pointX, pointY], [nw, se], viewport) {
22078
- const x = (0, import_core57.lerp)(nw[0], se[0], pointX);
22079
- const y = (0, import_core57.lerp)(nw[1], se[1], pointY);
22540
+ const x = (0, import_core56.lerp)(nw[0], se[0], pointX);
22541
+ const y = (0, import_core56.lerp)(nw[1], se[1], pointY);
22080
22542
  return viewport.unprojectFlat([x, y]);
22081
22543
  }
22082
22544
  function getPoints(geometry, bbox, viewport) {
@@ -22304,7 +22766,7 @@ var MVTLayer = class extends tile_layer_default {
22304
22766
  const yScale = -xScale;
22305
22767
  const xOffset = WORLD_SIZE * x / worldScale;
22306
22768
  const yOffset = WORLD_SIZE * (1 - y / worldScale);
22307
- const modelMatrix2 = new import_core59.Matrix4().scale([xScale, yScale, 1]);
22769
+ const modelMatrix2 = new import_core58.Matrix4().scale([xScale, yScale, 1]);
22308
22770
  props.autoHighlight = false;
22309
22771
  if (!this.context.viewport.resolution) {
22310
22772
  props.modelMatrix = modelMatrix2;