@deck.gl/carto 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.
package/dist/index.cjs CHANGED
@@ -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 = {};
@@ -790,7 +790,7 @@ fn project_mercator_(lnglat: vec2<f32>) -> vec2<f32> {
790
790
  let y = clamp(lnglat.y, -89.9, 89.9);
791
791
  return vec2<f32>(
792
792
  radians(x) + PI,
793
- PI + log(tan(PI * 0.25 + radians(y) * 0.5))
793
+ PI + log(tan_fp32(PI * 0.25 + radians(y) * 0.5))
794
794
  ) * WORLD_SCALE;
795
795
  }
796
796
 
@@ -2012,6 +2012,10 @@ var import_core2 = require("@math.gl/core");
2012
2012
  function createMat4() {
2013
2013
  return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
2014
2014
  }
2015
+ function mod(value, divisor) {
2016
+ const modulus = value % divisor;
2017
+ return modulus < 0 ? divisor + modulus : modulus;
2018
+ }
2015
2019
  function getCameraPosition(viewMatrixInverse) {
2016
2020
  return [viewMatrixInverse[12], viewMatrixInverse[13], viewMatrixInverse[14]];
2017
2021
  }
@@ -2179,8 +2183,8 @@ var _Viewport = class _Viewport {
2179
2183
  }
2180
2184
  return PROJECTION_MODE.IDENTITY;
2181
2185
  }
2182
- // Two viewports are equal if width and height are identical, and if
2183
- // their view and projection matrices are (approximately) equal.
2186
+ // Two viewports are equal if their dimensions, common-space scale, and
2187
+ // view and projection matrices are (approximately) equal.
2184
2188
  equals(viewport) {
2185
2189
  if (!(viewport instanceof _Viewport)) {
2186
2190
  return false;
@@ -2188,7 +2192,7 @@ var _Viewport = class _Viewport {
2188
2192
  if (this === viewport) {
2189
2193
  return true;
2190
2194
  }
2191
- 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);
2195
+ 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);
2192
2196
  }
2193
2197
  /**
2194
2198
  * Projects xyz (possibly latitude and longitude) to pixel coordinates in window
@@ -2525,6 +2529,10 @@ var _WebMercatorViewport = class _WebMercatorViewport extends Viewport {
2525
2529
  }
2526
2530
  return this._subViewports;
2527
2531
  }
2532
+ /** Returns whether two Web Mercator viewports use the same projection settings. */
2533
+ equals(viewport) {
2534
+ return viewport instanceof _WebMercatorViewport && viewport._pseudoMeters === this._pseudoMeters && super.equals(viewport);
2535
+ }
2528
2536
  projectPosition(xyz) {
2529
2537
  if (this._pseudoMeters) {
2530
2538
  return super.projectPosition(xyz);
@@ -3046,182 +3054,6 @@ function assert(condition, message) {
3046
3054
  }
3047
3055
  }
3048
3056
 
3049
- // ../core/src/viewports/globe-viewport.ts
3050
- var import_core6 = require("@math.gl/core");
3051
- var import_web_mercator4 = require("@math.gl/web-mercator");
3052
- var import_core7 = require("@math.gl/core");
3053
- var import_web_mercator5 = require("@math.gl/web-mercator");
3054
- var DEGREES_TO_RADIANS2 = Math.PI / 180;
3055
- var RADIANS_TO_DEGREES = 180 / Math.PI;
3056
- var EARTH_RADIUS = 6370972;
3057
- var GLOBE_RADIUS = 256;
3058
- function getDistanceScales2() {
3059
- const unitsPerMeter2 = GLOBE_RADIUS / EARTH_RADIUS;
3060
- const unitsPerDegree = Math.PI / 180 * GLOBE_RADIUS;
3061
- return {
3062
- unitsPerMeter: [unitsPerMeter2, unitsPerMeter2, unitsPerMeter2],
3063
- unitsPerMeter2: [0, 0, 0],
3064
- metersPerUnit: [1 / unitsPerMeter2, 1 / unitsPerMeter2, 1 / unitsPerMeter2],
3065
- unitsPerDegree: [unitsPerDegree, unitsPerDegree, unitsPerMeter2],
3066
- unitsPerDegree2: [0, 0, 0],
3067
- degreesPerUnit: [1 / unitsPerDegree, 1 / unitsPerDegree, 1 / unitsPerMeter2]
3068
- };
3069
- }
3070
- var GlobeViewport = class extends Viewport {
3071
- constructor(opts = {}) {
3072
- const {
3073
- longitude = 0,
3074
- bearing = 0,
3075
- pitch = 0,
3076
- zoom = 0,
3077
- // Matches Maplibre defaults
3078
- // https://github.com/maplibre/maplibre-gl-js/blob/f8ab4b48d59ab8fe7b068b102538793bbdd4c848/src/geo/projection/globe_transform.ts#L632-L633
3079
- nearZMultiplier = 0.5,
3080
- farZMultiplier = 1,
3081
- resolution = 10
3082
- } = opts;
3083
- let { latitude = 0, height, altitude = 1.5, fovy } = opts;
3084
- latitude = Math.max(Math.min(latitude, 90), -90);
3085
- height = height || 1;
3086
- if (fovy) {
3087
- altitude = (0, import_web_mercator4.fovyToAltitude)(fovy);
3088
- } else {
3089
- fovy = (0, import_web_mercator4.altitudeToFovy)(altitude);
3090
- }
3091
- const scaleLatitude = Math.max(Math.min(latitude, import_web_mercator5.MAX_LATITUDE), -import_web_mercator5.MAX_LATITUDE);
3092
- const scale = Math.pow(2, zoom - zoomAdjust(scaleLatitude));
3093
- const pitchRadians = pitch * DEGREES_TO_RADIANS2;
3094
- const nearZ = opts.nearZ ?? nearZMultiplier;
3095
- const farZ = opts.farZ ?? (altitude + GLOBE_RADIUS * 2 * scale / height / Math.max(Math.cos(pitchRadians), 0.1)) * farZMultiplier;
3096
- 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);
3097
- super({
3098
- ...opts,
3099
- // x, y, width,
3100
- height,
3101
- // view matrix
3102
- viewMatrix,
3103
- longitude,
3104
- latitude,
3105
- zoom,
3106
- // projection matrix parameters
3107
- distanceScales: getDistanceScales2(),
3108
- fovy,
3109
- focalDistance: altitude,
3110
- near: nearZ,
3111
- far: farZ
3112
- });
3113
- this.scale = scale;
3114
- this.latitude = latitude;
3115
- this.longitude = longitude;
3116
- this.bearing = bearing;
3117
- this.pitch = pitch;
3118
- this.fovy = fovy;
3119
- this.resolution = resolution;
3120
- }
3121
- get projectionMode() {
3122
- return PROJECTION_MODE.GLOBE;
3123
- }
3124
- getDistanceScales() {
3125
- return this.distanceScales;
3126
- }
3127
- getBounds(options = {}) {
3128
- const unprojectOption = { targetZ: options.z || 0 };
3129
- const left = this.unproject([0, this.height / 2], unprojectOption);
3130
- const top = this.unproject([this.width / 2, 0], unprojectOption);
3131
- const right = this.unproject([this.width, this.height / 2], unprojectOption);
3132
- const bottom = this.unproject([this.width / 2, this.height], unprojectOption);
3133
- if (right[0] < this.longitude) right[0] += 360;
3134
- if (left[0] > this.longitude) left[0] -= 360;
3135
- return [
3136
- Math.min(left[0], right[0], top[0], bottom[0]),
3137
- Math.min(left[1], right[1], top[1], bottom[1]),
3138
- Math.max(left[0], right[0], top[0], bottom[0]),
3139
- Math.max(left[1], right[1], top[1], bottom[1])
3140
- ];
3141
- }
3142
- unproject(xyz, { topLeft = true, targetZ } = {}) {
3143
- const [x, y, z] = xyz;
3144
- const y2 = topLeft ? y : this.height - y;
3145
- const { pixelUnprojectionMatrix } = this;
3146
- let coord;
3147
- if (Number.isFinite(z)) {
3148
- coord = transformVector(pixelUnprojectionMatrix, [x, y2, z, 1]);
3149
- } else {
3150
- const coord0 = transformVector(pixelUnprojectionMatrix, [x, y2, -1, 1]);
3151
- const coord1 = transformVector(pixelUnprojectionMatrix, [x, y2, 1, 1]);
3152
- const lt = ((targetZ || 0) / EARTH_RADIUS + 1) * GLOBE_RADIUS;
3153
- const lSqr = import_core7.vec3.sqrLen(import_core7.vec3.sub([], coord0, coord1));
3154
- const l0Sqr = import_core7.vec3.sqrLen(coord0);
3155
- const l1Sqr = import_core7.vec3.sqrLen(coord1);
3156
- const sSqr = (4 * l0Sqr * l1Sqr - (lSqr - l0Sqr - l1Sqr) ** 2) / 16;
3157
- const dSqr = 4 * sSqr / lSqr;
3158
- const r0 = Math.sqrt(l0Sqr - dSqr);
3159
- const dr = Math.sqrt(Math.max(0, lt * lt - dSqr));
3160
- const t = (r0 - dr) / Math.sqrt(lSqr);
3161
- coord = import_core7.vec3.lerp([], coord0, coord1, t);
3162
- }
3163
- const [X, Y, Z] = this.unprojectPosition(coord);
3164
- if (Number.isFinite(z)) {
3165
- return [X, Y, Z];
3166
- }
3167
- return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
3168
- }
3169
- projectPosition(xyz) {
3170
- const [lng, lat, Z = 0] = xyz;
3171
- const lambda = lng * DEGREES_TO_RADIANS2;
3172
- const phi = lat * DEGREES_TO_RADIANS2;
3173
- const cosPhi = Math.cos(phi);
3174
- const D = (Z / EARTH_RADIUS + 1) * GLOBE_RADIUS;
3175
- return [Math.sin(lambda) * cosPhi * D, -Math.cos(lambda) * cosPhi * D, Math.sin(phi) * D];
3176
- }
3177
- unprojectPosition(xyz) {
3178
- const [x, y, z] = xyz;
3179
- const D = import_core7.vec3.len(xyz);
3180
- const phi = Math.asin(z / D);
3181
- const lambda = Math.atan2(x, -y);
3182
- const lng = lambda * RADIANS_TO_DEGREES;
3183
- const lat = phi * RADIANS_TO_DEGREES;
3184
- const Z = (D / GLOBE_RADIUS - 1) * EARTH_RADIUS;
3185
- return [lng, lat, Z];
3186
- }
3187
- projectFlat(xyz) {
3188
- return xyz;
3189
- }
3190
- unprojectFlat(xyz) {
3191
- return xyz;
3192
- }
3193
- /**
3194
- * Pan the globe using delta-based movement
3195
- * @param coords - the geographic coordinates where the pan started
3196
- * @param pixel - the current screen position
3197
- * @param startPixel - the screen position where the pan started
3198
- * @returns updated viewport options with new longitude/latitude
3199
- */
3200
- panByPosition([startLng, startLat, startZoom], pixel, startPixel) {
3201
- const scale = Math.pow(2, this.zoom - zoomAdjust(this.latitude));
3202
- const rotationSpeed = 0.25 / scale;
3203
- const longitude = startLng + rotationSpeed * (startPixel[0] - pixel[0]);
3204
- let latitude = startLat - rotationSpeed * (startPixel[1] - pixel[1]);
3205
- latitude = Math.max(Math.min(latitude, 90), -90);
3206
- const out = { longitude, latitude, zoom: startZoom - zoomAdjust(startLat) };
3207
- out.zoom += zoomAdjust(out.latitude);
3208
- return out;
3209
- }
3210
- };
3211
- GlobeViewport.displayName = "GlobeViewport";
3212
- function zoomAdjust(latitude, clampToPoles) {
3213
- if (clampToPoles) {
3214
- latitude = Math.max(Math.min(latitude, import_web_mercator5.MAX_LATITUDE), -import_web_mercator5.MAX_LATITUDE);
3215
- }
3216
- const scaleAdjust = Math.PI * Math.cos(latitude * Math.PI / 180);
3217
- return Math.log2(scaleAdjust);
3218
- }
3219
- function transformVector(matrix, vector) {
3220
- const result = import_core7.vec4.transformMat4([], vector, matrix);
3221
- import_core7.vec4.scale(result, result, 1 / result[3]);
3222
- return result;
3223
- }
3224
-
3225
3057
  // ../../node_modules/@luma.gl/webgl/dist/constants/webgl-constants.js
3226
3058
  var GLEnum;
3227
3059
  (function(GLEnum2) {
@@ -3874,10 +3706,10 @@ var GLEnum;
3874
3706
  })(GLEnum || (GLEnum = {}));
3875
3707
 
3876
3708
  // ../core/src/lib/attribute/data-column.ts
3877
- var import_core9 = require("@luma.gl/core");
3709
+ var import_core7 = require("@luma.gl/core");
3878
3710
 
3879
3711
  // ../core/src/lib/attribute/gl-utils.ts
3880
- var import_core8 = require("@luma.gl/core");
3712
+ var import_core6 = require("@luma.gl/core");
3881
3713
  function typedArrayFromDataType(type) {
3882
3714
  switch (type) {
3883
3715
  case "float64":
@@ -3886,10 +3718,10 @@ function typedArrayFromDataType(type) {
3886
3718
  case "unorm8":
3887
3719
  return Uint8ClampedArray;
3888
3720
  default:
3889
- return (0, import_core8.getTypedArrayConstructor)(type);
3721
+ return (0, import_core6.getTypedArrayConstructor)(type);
3890
3722
  }
3891
3723
  }
3892
- var dataTypeFromTypedArray = import_core8.dataTypeDecoder.getDataType.bind(import_core8.dataTypeDecoder);
3724
+ var dataTypeFromTypedArray = import_core6.dataTypeDecoder.getDataType.bind(import_core6.dataTypeDecoder);
3893
3725
  function getBufferAttributeLayout(name, accessor, deviceType) {
3894
3726
  if (accessor.size > 4) {
3895
3727
  return null;
@@ -4020,6 +3852,7 @@ var DataColumn = class {
4020
3852
  this._buffer = null;
4021
3853
  }
4022
3854
  typed_array_manager_default.release(this.state.allocatedValue);
3855
+ this.state.allocatedValue = null;
4023
3856
  }
4024
3857
  getBuffer() {
4025
3858
  if (this.state.constant && this.device.type !== "webgpu") {
@@ -4139,7 +3972,7 @@ var DataColumn = class {
4139
3972
  let opts;
4140
3973
  if (ArrayBuffer.isView(data)) {
4141
3974
  opts = { value: data };
4142
- } else if (data instanceof import_core9.Buffer) {
3975
+ } else if (data instanceof import_core7.Buffer) {
4143
3976
  opts = { buffer: data };
4144
3977
  } else {
4145
3978
  opts = data;
@@ -4339,7 +4172,7 @@ var DataColumn = class {
4339
4172
  this._buffer.destroy();
4340
4173
  }
4341
4174
  const { isIndexed, type } = this.settings;
4342
- 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;
4175
+ 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;
4343
4176
  this._buffer = this.device.createBuffer({
4344
4177
  ...(_a = this._buffer) == null ? void 0 : _a.props,
4345
4178
  id: this.id,
@@ -4969,6 +4802,7 @@ ${getComputeBlock(module2.name, inputEntries, output, elementWise, expression)}
4969
4802
  );
4970
4803
  const computation = new import_engine2.Computation(outputBuffer.device, {
4971
4804
  source: source4,
4805
+ modules: module2.dependencies,
4972
4806
  shaderAssembler: GPGPU_SHADER_ASSEMBLER,
4973
4807
  shaderLayout: {
4974
4808
  bindings: [
@@ -6360,11 +6194,11 @@ var AttributeManager = class {
6360
6194
  };
6361
6195
 
6362
6196
  // ../core/src/lib/layer.ts
6363
- var import_core12 = require("@luma.gl/core");
6197
+ var import_core10 = require("@luma.gl/core");
6364
6198
  var import_webgl = require("@luma.gl/webgl");
6365
6199
 
6366
6200
  // ../core/src/transitions/cpu-interpolation-transition.ts
6367
- var import_core10 = require("@math.gl/core");
6201
+ var import_core8 = require("@math.gl/core");
6368
6202
  var CPUInterpolationTransition = class extends Transition {
6369
6203
  get value() {
6370
6204
  return this._value;
@@ -6375,7 +6209,7 @@ var CPUInterpolationTransition = class extends Transition {
6375
6209
  settings: { fromValue, toValue, duration, easing }
6376
6210
  } = this;
6377
6211
  const t = easing(time / duration);
6378
- this._value = (0, import_core10.lerp)(fromValue, toValue, t);
6212
+ this._value = (0, import_core8.lerp)(fromValue, toValue, t);
6379
6213
  }
6380
6214
  };
6381
6215
 
@@ -6735,7 +6569,7 @@ function mergeShaders(target, source4) {
6735
6569
  }
6736
6570
 
6737
6571
  // ../core/src/utils/texture.ts
6738
- var import_core11 = require("@luma.gl/core");
6572
+ var import_core9 = require("@luma.gl/core");
6739
6573
  var DEFAULT_TEXTURE_PARAMETERS = {
6740
6574
  minFilter: "linear",
6741
6575
  mipmapFilter: "linear",
@@ -6745,7 +6579,7 @@ var DEFAULT_TEXTURE_PARAMETERS = {
6745
6579
  };
6746
6580
  var internalTextures = {};
6747
6581
  function createTexture(owner, device, image, sampler) {
6748
- if (image instanceof import_core11.Texture) {
6582
+ if (image instanceof import_core9.Texture) {
6749
6583
  return image;
6750
6584
  } else if (image.constructor && image.constructor.name !== "Object") {
6751
6585
  image = { data: image };
@@ -6776,7 +6610,7 @@ function createTexture(owner, device, image, sampler) {
6776
6610
  return texture;
6777
6611
  }
6778
6612
  function destroyTexture(owner, texture) {
6779
- if (!texture || !(texture instanceof import_core11.Texture)) {
6613
+ if (!texture || !(texture instanceof import_core9.Texture)) {
6780
6614
  return;
6781
6615
  }
6782
6616
  if (internalTextures[texture.id] === owner) {
@@ -7404,8 +7238,8 @@ var LayerState = class extends ComponentState {
7404
7238
  };
7405
7239
 
7406
7240
  // ../core/src/lib/layer.ts
7407
- var import_web_mercator6 = require("@math.gl/web-mercator");
7408
- var import_core13 = require("@loaders.gl/core");
7241
+ var import_web_mercator4 = require("@math.gl/web-mercator");
7242
+ var import_core11 = require("@loaders.gl/core");
7409
7243
  var TRACE_CHANGE_FLAG = "layer.changeFlag";
7410
7244
  var TRACE_INITIALIZE = "layer.initialize";
7411
7245
  var TRACE_UPDATE = "layer.update";
@@ -7468,7 +7302,7 @@ var defaultProps = {
7468
7302
  }
7469
7303
  let inResourceManager = resourceManager.contains(url);
7470
7304
  if (!inResourceManager && !loadOptions) {
7471
- resourceManager.add({ resourceId: url, data: (0, import_core13.load)(url, loaders), persistent: false });
7305
+ resourceManager.add({ resourceId: url, data: (0, import_core11.load)(url, loaders), persistent: false });
7472
7306
  inResourceManager = true;
7473
7307
  }
7474
7308
  if (inResourceManager) {
@@ -7482,7 +7316,7 @@ var defaultProps = {
7482
7316
  requestId: propName
7483
7317
  });
7484
7318
  }
7485
- return (0, import_core13.load)(url, loaders, loadOptions);
7319
+ return (0, import_core11.load)(url, loaders, loadOptions);
7486
7320
  }
7487
7321
  },
7488
7322
  updateTriggers: {},
@@ -7552,7 +7386,7 @@ var Layer = class extends Component {
7552
7386
  coordinateOrigin: this.props.coordinateOrigin,
7553
7387
  coordinateSystem: this.props.coordinateSystem
7554
7388
  });
7555
- const [x, y, z] = (0, import_web_mercator6.worldToPixels)(worldPosition, viewport.pixelProjectionMatrix);
7389
+ const [x, y, z] = (0, import_web_mercator4.worldToPixels)(worldPosition, viewport.pixelProjectionMatrix);
7556
7390
  return xyz.length === 2 ? [x, y] : [x, y, z];
7557
7391
  }
7558
7392
  /** Unprojects a screen pixel to the current view's default coordinate system
@@ -7963,7 +7797,7 @@ var Layer = class extends Component {
7963
7797
  const values = changedAttributes[name].getValue();
7964
7798
  for (const attributeName in values) {
7965
7799
  const value = values[attributeName];
7966
- if (value instanceof import_core12.Buffer) {
7800
+ if (value instanceof import_core10.Buffer) {
7967
7801
  if (changedAttributes[name].settings.isIndexed) {
7968
7802
  model.setIndexBuffer(value);
7969
7803
  } else {
@@ -7998,7 +7832,7 @@ var Layer = class extends Component {
7998
7832
  const values = attribute.getValue();
7999
7833
  for (const attributeName in values) {
8000
7834
  const value = values[attributeName];
8001
- if (value instanceof import_core12.Buffer) {
7835
+ if (value instanceof import_core10.Buffer) {
8002
7836
  if (attribute.settings.isIndexed) {
8003
7837
  model.setIndexBuffer(value);
8004
7838
  } else {
@@ -8650,6 +8484,275 @@ var CompositeLayer = class extends Layer {
8650
8484
  };
8651
8485
  CompositeLayer.layerName = "CompositeLayer";
8652
8486
 
8487
+ // ../core/src/viewports/globe-viewport.ts
8488
+ var import_core12 = require("@math.gl/core");
8489
+ var import_web_mercator5 = require("@math.gl/web-mercator");
8490
+ var DEGREES_TO_RADIANS2 = Math.PI / 180;
8491
+ var RADIANS_TO_DEGREES = 180 / Math.PI;
8492
+ var NORTH_UP_BEARING_THRESHOLD = 1;
8493
+ var EARTH_RADIUS = 6370972;
8494
+ var GLOBE_RADIUS = 256;
8495
+ var GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO = 0.75;
8496
+ var GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO = 1.15;
8497
+ function isGlobeNorthUp(bearing) {
8498
+ const normalizedBearing = mod(bearing + 180, 360) - 180;
8499
+ return Math.abs(normalizedBearing) < NORTH_UP_BEARING_THRESHOLD;
8500
+ }
8501
+ function getDistanceScales2() {
8502
+ const unitsPerMeter2 = GLOBE_RADIUS / EARTH_RADIUS;
8503
+ const unitsPerDegree = Math.PI / 180 * GLOBE_RADIUS;
8504
+ return {
8505
+ unitsPerMeter: [unitsPerMeter2, unitsPerMeter2, unitsPerMeter2],
8506
+ unitsPerMeter2: [0, 0, 0],
8507
+ metersPerUnit: [1 / unitsPerMeter2, 1 / unitsPerMeter2, 1 / unitsPerMeter2],
8508
+ unitsPerDegree: [unitsPerDegree, unitsPerDegree, unitsPerMeter2],
8509
+ unitsPerDegree2: [0, 0, 0],
8510
+ degreesPerUnit: [1 / unitsPerDegree, 1 / unitsPerDegree, 1 / unitsPerMeter2]
8511
+ };
8512
+ }
8513
+ var GlobeViewport = class extends Viewport {
8514
+ constructor(opts = {}) {
8515
+ const {
8516
+ longitude = 0,
8517
+ bearing = 0,
8518
+ pitch = 0,
8519
+ zoom = 0,
8520
+ // Matches Maplibre defaults
8521
+ // https://github.com/maplibre/maplibre-gl-js/blob/f8ab4b48d59ab8fe7b068b102538793bbdd4c848/src/geo/projection/globe_transform.ts#L632-L633
8522
+ nearZMultiplier = 0.5,
8523
+ farZMultiplier = 1,
8524
+ resolution = 10
8525
+ } = opts;
8526
+ let { latitude = 0, height, altitude = 1.5, fovy } = opts;
8527
+ latitude = Math.max(Math.min(latitude, 90), -90);
8528
+ height = height || 1;
8529
+ if (fovy) {
8530
+ altitude = (0, import_web_mercator5.fovyToAltitude)(fovy);
8531
+ } else {
8532
+ fovy = (0, import_web_mercator5.altitudeToFovy)(altitude);
8533
+ }
8534
+ const scaleLatitude = Math.max(Math.min(latitude, import_web_mercator5.MAX_LATITUDE), -import_web_mercator5.MAX_LATITUDE);
8535
+ const scale = Math.pow(2, zoom - zoomAdjust(scaleLatitude));
8536
+ const pitchRadians = pitch * DEGREES_TO_RADIANS2;
8537
+ const nearZ = opts.nearZ ?? nearZMultiplier;
8538
+ const farZ = opts.farZ ?? (altitude + GLOBE_RADIUS * 2 * scale / height / Math.max(Math.cos(pitchRadians), 0.1)) * farZMultiplier;
8539
+ 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);
8540
+ super({
8541
+ ...opts,
8542
+ // x, y, width,
8543
+ height,
8544
+ // view matrix
8545
+ viewMatrix,
8546
+ longitude,
8547
+ latitude,
8548
+ zoom,
8549
+ // projection matrix parameters
8550
+ distanceScales: getDistanceScales2(),
8551
+ fovy,
8552
+ focalDistance: altitude,
8553
+ near: nearZ,
8554
+ far: farZ
8555
+ });
8556
+ this.scale = scale;
8557
+ this.latitude = latitude;
8558
+ this.longitude = longitude;
8559
+ this.bearing = bearing;
8560
+ this.pitch = pitch;
8561
+ this.fovy = fovy;
8562
+ this.resolution = resolution;
8563
+ }
8564
+ get projectionMode() {
8565
+ return PROJECTION_MODE.GLOBE;
8566
+ }
8567
+ getDistanceScales() {
8568
+ return this.distanceScales;
8569
+ }
8570
+ getBounds(options = {}) {
8571
+ const unprojectOption = { targetZ: options.z || 0 };
8572
+ const left = this.unproject([0, this.height / 2], unprojectOption);
8573
+ const top = this.unproject([this.width / 2, 0], unprojectOption);
8574
+ const right = this.unproject([this.width, this.height / 2], unprojectOption);
8575
+ const bottom = this.unproject([this.width / 2, this.height], unprojectOption);
8576
+ if (right[0] < this.longitude) right[0] += 360;
8577
+ if (left[0] > this.longitude) left[0] -= 360;
8578
+ return [
8579
+ Math.min(left[0], right[0], top[0], bottom[0]),
8580
+ Math.min(left[1], right[1], top[1], bottom[1]),
8581
+ Math.max(left[0], right[0], top[0], bottom[0]),
8582
+ Math.max(left[1], right[1], top[1], bottom[1])
8583
+ ];
8584
+ }
8585
+ /**
8586
+ * Builds the screen-pixel → globe-center ray and the intermediate ray/sphere
8587
+ * math reused by `unproject` and anchored zoom. One function so the same
8588
+ * pixelUnprojectionMatrix work isn't duplicated.
8589
+ */
8590
+ _getRayToGlobe(screenPosition, { topLeft = true, targetZ } = {}) {
8591
+ const [screenX, screenY] = screenPosition;
8592
+ const adjustedScreenY = topLeft ? screenY : this.height - screenY;
8593
+ const { pixelUnprojectionMatrix } = this;
8594
+ const rayStartPosition = transformVector(pixelUnprojectionMatrix, [
8595
+ screenX,
8596
+ adjustedScreenY,
8597
+ -1,
8598
+ 1
8599
+ ]);
8600
+ const rayEndPosition = transformVector(pixelUnprojectionMatrix, [
8601
+ screenX,
8602
+ adjustedScreenY,
8603
+ 1,
8604
+ 1
8605
+ ]);
8606
+ const radius = ((targetZ || 0) / EARTH_RADIUS + 1) * GLOBE_RADIUS;
8607
+ const rayLengthSquared = import_core12.vec3.sqrLen(import_core12.vec3.sub([], rayStartPosition, rayEndPosition));
8608
+ const rayStartDistanceSquared = import_core12.vec3.sqrLen(rayStartPosition);
8609
+ const rayEndDistanceSquared = import_core12.vec3.sqrLen(rayEndPosition);
8610
+ const triangleAreaSquared = (4 * rayStartDistanceSquared * rayEndDistanceSquared - (rayLengthSquared - rayStartDistanceSquared - rayEndDistanceSquared) ** 2) / 16;
8611
+ const distanceToCenterSquared = 4 * triangleAreaSquared / rayLengthSquared;
8612
+ return {
8613
+ rayStartPosition,
8614
+ rayEndPosition,
8615
+ radius,
8616
+ rayLengthSquared,
8617
+ rayStartDistanceSquared,
8618
+ distanceToCenterSquared
8619
+ };
8620
+ }
8621
+ _getRayDistanceToGlobeCenterRatio(screenPosition, options) {
8622
+ const { distanceToCenterSquared, radius } = this._getRayToGlobe(screenPosition, options);
8623
+ return Math.sqrt(Math.max(0, distanceToCenterSquared)) / radius;
8624
+ }
8625
+ /**
8626
+ * Returns how strongly a screen position should anchor zoom on the visible globe.
8627
+ * A value of `0` means that the controller should fall back to center zoom.
8628
+ * @param screenPosition - Screen position to evaluate.
8629
+ * @returns Anchor strength from `0` to `1`.
8630
+ */
8631
+ getZoomAnchorStrength(screenPosition) {
8632
+ const distanceRatio = this._getRayDistanceToGlobeCenterRatio(screenPosition);
8633
+ if (distanceRatio >= GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO) {
8634
+ return 0;
8635
+ }
8636
+ const edgeProgress = Math.max(
8637
+ 0,
8638
+ Math.min(
8639
+ 1,
8640
+ (distanceRatio - GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO) / (GLOBE_ZOOM_ANCHOR_MAX_DISTANCE_RATIO - GLOBE_ZOOM_ANCHOR_DAMPING_START_RATIO)
8641
+ )
8642
+ );
8643
+ const smoothProgress = edgeProgress * edgeProgress * (3 - 2 * edgeProgress);
8644
+ return 1 - smoothProgress;
8645
+ }
8646
+ unproject(xyz, { topLeft = true, targetZ } = {}) {
8647
+ const [x, y, z] = xyz;
8648
+ const y2 = topLeft ? y : this.height - y;
8649
+ const { pixelUnprojectionMatrix } = this;
8650
+ let coord;
8651
+ if (Number.isFinite(z)) {
8652
+ coord = transformVector(pixelUnprojectionMatrix, [x, y2, z, 1]);
8653
+ } else {
8654
+ const {
8655
+ rayStartPosition,
8656
+ rayEndPosition,
8657
+ radius,
8658
+ rayLengthSquared,
8659
+ rayStartDistanceSquared,
8660
+ distanceToCenterSquared
8661
+ } = this._getRayToGlobe(xyz, { topLeft, targetZ });
8662
+ const rayStartToClosestApproach = Math.sqrt(
8663
+ rayStartDistanceSquared - distanceToCenterSquared
8664
+ );
8665
+ const closestApproachToIntersection = Math.sqrt(
8666
+ Math.max(0, radius * radius - distanceToCenterSquared)
8667
+ );
8668
+ const intersectionRatio = (rayStartToClosestApproach - closestApproachToIntersection) / Math.sqrt(rayLengthSquared);
8669
+ coord = import_core12.vec3.lerp([], rayStartPosition, rayEndPosition, intersectionRatio);
8670
+ }
8671
+ const [X, Y, Z] = this.unprojectPosition(coord);
8672
+ if (Number.isFinite(z)) {
8673
+ return [X, Y, Z];
8674
+ }
8675
+ return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
8676
+ }
8677
+ projectPosition(xyz) {
8678
+ const [lng, lat, Z = 0] = xyz;
8679
+ const lambda = lng * DEGREES_TO_RADIANS2;
8680
+ const phi = lat * DEGREES_TO_RADIANS2;
8681
+ const cosPhi = Math.cos(phi);
8682
+ const D = (Z / EARTH_RADIUS + 1) * GLOBE_RADIUS;
8683
+ return [Math.sin(lambda) * cosPhi * D, -Math.cos(lambda) * cosPhi * D, Math.sin(phi) * D];
8684
+ }
8685
+ unprojectPosition(xyz) {
8686
+ const [x, y, z] = xyz;
8687
+ const D = import_core12.vec3.len(xyz);
8688
+ const phi = Math.asin(z / D);
8689
+ const lambda = Math.atan2(x, -y);
8690
+ const lng = lambda * RADIANS_TO_DEGREES;
8691
+ const lat = phi * RADIANS_TO_DEGREES;
8692
+ const Z = (D / GLOBE_RADIUS - 1) * EARTH_RADIUS;
8693
+ return [lng, lat, Z];
8694
+ }
8695
+ projectFlat(xyz) {
8696
+ return xyz;
8697
+ }
8698
+ unprojectFlat(xyz) {
8699
+ return xyz;
8700
+ }
8701
+ /**
8702
+ * Pan the globe to place geographic coordinates at a screen pixel.
8703
+ * When `dragStartPosition` is supplied, applies the delta-based movement used by globe dragging.
8704
+ * @param coordinates - Geographic anchor, or the starting longitude, latitude and zoom.
8705
+ * @param screenPosition - Current screen position.
8706
+ * @param dragStartPosition - Screen position where a drag started.
8707
+ * @returns Updated viewport options.
8708
+ */
8709
+ panByPosition(coordinates, screenPosition, dragStartPosition) {
8710
+ if (!dragStartPosition) {
8711
+ let anchorStrength = this.getZoomAnchorStrength(screenPosition);
8712
+ if (anchorStrength === 0) {
8713
+ return { longitude: this.longitude, latitude: this.latitude };
8714
+ }
8715
+ const currentCoordinates = this.unproject(screenPosition);
8716
+ const longitudeDelta = mod(coordinates[0] - currentCoordinates[0] + 180, 360) - 180;
8717
+ const latitudeDelta = coordinates[1] - currentCoordinates[1];
8718
+ const crossesPole = Math.abs(currentCoordinates[1]) > import_web_mercator5.MAX_LATITUDE || Math.abs(longitudeDelta) > 90;
8719
+ if (isGlobeNorthUp(this.bearing) && crossesPole) {
8720
+ return { longitude: this.longitude, latitude: this.latitude };
8721
+ }
8722
+ if (isGlobeNorthUp(this.bearing) && latitudeDelta !== 0) {
8723
+ const latitudeLimit = latitudeDelta > 0 ? import_web_mercator5.MAX_LATITUDE : -import_web_mercator5.MAX_LATITUDE;
8724
+ const latitudeConstraintStrength = (latitudeLimit - this.latitude) / latitudeDelta;
8725
+ anchorStrength = Math.min(anchorStrength, Math.max(0, latitudeConstraintStrength));
8726
+ }
8727
+ const longitude2 = this.longitude + longitudeDelta * anchorStrength;
8728
+ const latitude2 = Math.max(Math.min(this.latitude + latitudeDelta * anchorStrength, 90), -90);
8729
+ return { longitude: longitude2, latitude: latitude2 };
8730
+ }
8731
+ const [startLongitude, startLatitude, startZoom] = coordinates;
8732
+ const scale = Math.pow(2, this.zoom - zoomAdjust(this.latitude));
8733
+ const rotationSpeed = 0.25 / scale;
8734
+ const longitude = startLongitude + rotationSpeed * (dragStartPosition[0] - screenPosition[0]);
8735
+ let latitude = startLatitude - rotationSpeed * (dragStartPosition[1] - screenPosition[1]);
8736
+ latitude = Math.max(Math.min(latitude, 90), -90);
8737
+ const nextViewState = { longitude, latitude, zoom: startZoom - zoomAdjust(startLatitude) };
8738
+ nextViewState.zoom += zoomAdjust(nextViewState.latitude);
8739
+ return nextViewState;
8740
+ }
8741
+ };
8742
+ GlobeViewport.displayName = "GlobeViewport";
8743
+ function zoomAdjust(latitude, clampToPoles) {
8744
+ if (clampToPoles) {
8745
+ latitude = Math.max(Math.min(latitude, import_web_mercator5.MAX_LATITUDE), -import_web_mercator5.MAX_LATITUDE);
8746
+ }
8747
+ const scaleAdjust = Math.PI * Math.cos(latitude * Math.PI / 180);
8748
+ return Math.log2(scaleAdjust);
8749
+ }
8750
+ function transformVector(matrix, vector) {
8751
+ const result = import_core12.vec4.transformMat4([], vector, matrix);
8752
+ import_core12.vec4.scale(result, result, 1 / result[3]);
8753
+ return result;
8754
+ }
8755
+
8653
8756
  // ../core/src/lib/layer-extension.ts
8654
8757
  var LayerExtension = class {
8655
8758
  static get componentName() {
@@ -8711,7 +8814,7 @@ LayerExtension.defaultProps = {};
8711
8814
  LayerExtension.extensionName = "LayerExtension";
8712
8815
 
8713
8816
  // ../core/src/utils/tesselator.ts
8714
- var import_core14 = require("@luma.gl/core");
8817
+ var import_core13 = require("@luma.gl/core");
8715
8818
  var Tesselator = class {
8716
8819
  constructor(opts) {
8717
8820
  this.indexStarts = [0];
@@ -8830,7 +8933,7 @@ var Tesselator = class {
8830
8933
  instanceCount = vertexStarts[data.length] || 0;
8831
8934
  if (ArrayBuffer.isView(geometryBuffer)) {
8832
8935
  instanceCount = instanceCount || geometryBuffer.length / this.positionSize;
8833
- } else if (geometryBuffer instanceof import_core14.Buffer) {
8936
+ } else if (geometryBuffer instanceof import_core13.Buffer) {
8834
8937
  const byteStride = this.positionSize * 4;
8835
8938
  instanceCount = instanceCount || geometryBuffer.byteLength / byteStride;
8836
8939
  } else if (geometryBuffer.buffer) {
@@ -9183,7 +9286,7 @@ function getShaderWGSL(useRowIndexes) {
9183
9286
  }
9184
9287
 
9185
9288
  // ../layers/src/icon-layer/icon-manager.ts
9186
- var import_core15 = require("@loaders.gl/core");
9289
+ var import_core14 = require("@loaders.gl/core");
9187
9290
  var DEFAULT_CANVAS_WIDTH = 1024;
9188
9291
  var DEFAULT_BUFFER = 4;
9189
9292
  var noop = () => {
@@ -9442,7 +9545,7 @@ var IconManager = class {
9442
9545
  });
9443
9546
  for (const icon of icons) {
9444
9547
  this._pendingCount++;
9445
- (0, import_core15.load)(icon.url, this._loadOptions).then((imageData) => {
9548
+ (0, import_core14.load)(icon.url, this._loadOptions).then((imageData) => {
9446
9549
  var _a;
9447
9550
  const id5 = getIconId(icon);
9448
9551
  const iconDef = this._mapping[id5];
@@ -11156,6 +11259,25 @@ var PathTesselator = class extends Tesselator {
11156
11259
  get(attributeName) {
11157
11260
  return this.attributes[attributeName];
11158
11261
  }
11262
+ /** Get rendered segment indexes in source traversal order. */
11263
+ getPathSegmentIndices(dataIndex) {
11264
+ const segmentTypes = this.attributes.segmentTypes;
11265
+ const rowStart = this.vertexStarts[dataIndex];
11266
+ const rowEnd = Math.min(
11267
+ this.vertexStarts[dataIndex + 1] ?? this.instanceCount,
11268
+ this.instanceCount
11269
+ );
11270
+ const result = [];
11271
+ for (let segmentIndex = rowStart; segmentIndex < rowEnd - 1; segmentIndex++) {
11272
+ if ((segmentTypes[segmentIndex] & INVALID) === 0) {
11273
+ result.push(segmentIndex);
11274
+ }
11275
+ }
11276
+ if (result.length && (segmentTypes[rowStart] & INVALID) !== 0) {
11277
+ result.unshift(result.pop());
11278
+ }
11279
+ return result;
11280
+ }
11159
11281
  /* Implement base Tesselator interface */
11160
11282
  getGeometryFromBuffer(buffer) {
11161
11283
  if (this.normalize || this.opts.isWebGPU) {
@@ -11351,6 +11473,9 @@ struct Varyings {
11351
11473
  @location(5) vJointType: f32,
11352
11474
  // Location 6 is reserved for TripsLayer's injected vTime varying.
11353
11475
  @location(7) clipCoordinates: vec2<f32>,
11476
+ #ifdef DASH_ENABLED
11477
+ @location(8) vPathBounds: vec2<f32>,
11478
+ #endif
11354
11479
  };
11355
11480
 
11356
11481
  fn flipIfTrue(flag: bool) -> f32 {
@@ -11365,11 +11490,34 @@ fn clipLine(position: vec4<f32>, refPosition: vec4<f32>) -> vec4<f32> {
11365
11490
  return position;
11366
11491
  }
11367
11492
 
11493
+ #ifdef DASH_ENABLED
11494
+ // Return the visible interval of the original segment before clipLine moves either endpoint.
11495
+ fn getClippedPathRange(startW: f32, endW: f32) -> vec2<f32> {
11496
+ let startClipped = startW < EPSILON;
11497
+ let endClipped = endW < EPSILON;
11498
+ if (startClipped && endClipped) {
11499
+ return vec2<f32>(0.0, 0.0);
11500
+ }
11501
+ if (startClipped || endClipped) {
11502
+ let intersection = clamp((EPSILON - startW) / (endW - startW), 0.0, 1.0);
11503
+ if (startClipped) {
11504
+ return vec2<f32>(intersection, 1.0);
11505
+ }
11506
+ return vec2<f32>(0.0, intersection);
11507
+ }
11508
+ return vec2<f32>(0.0, 1.0);
11509
+ }
11510
+ #endif
11511
+
11368
11512
  fn getLineJoinOffset(
11369
11513
  prevPoint: vec3<f32>,
11370
11514
  currPoint: vec3<f32>,
11371
11515
  nextPoint: vec3<f32>,
11372
11516
  width: vec2<f32>,
11517
+ #ifdef DASH_ENABLED
11518
+ sourcePathLength: f32,
11519
+ sourcePathRange: vec2<f32>,
11520
+ #endif
11373
11521
  #ifdef ANTIALIASING
11374
11522
  coverageScale: f32,
11375
11523
  #endif
@@ -11406,7 +11554,43 @@ fn getLineJoinOffset(
11406
11554
  let miterVec = vec2<f32>(-tangent.y, tangent.x);
11407
11555
  let dir = select(dirB, dirA, isEnd);
11408
11556
  let perp = select(perpB, perpA, isEnd);
11557
+ #ifdef DASH_ENABLED
11558
+ let segmentLength2D = select(lenB, lenA, isEnd);
11559
+
11560
+ // Extrusion happens in the XY plane, so segmentLength2D is a 2D length and pathPosition.y
11561
+ // below measures 2D distance along the segment. For a path that also moves in Z the true
11562
+ // arc length is longer by this ratio. Scaling pathLength and pathPosition.y by it makes
11563
+ // the coordinate measure real 3D distance while leaving the joint tests unchanged, since
11564
+ // they compare the two against each other and both are scaled alike. Billboard mode
11565
+ // extrudes in clip space, where the perspective divide has already reduced the segment to
11566
+ // its screen projection, so its complete common-space length is supplied by the caller.
11567
+ // Mirrors path-layer-vertex.glsl.ts.
11568
+ let currDelta3 = select(deltaB3, deltaA3, isEnd);
11569
+ let currLength2D = length(currDelta3.xy);
11570
+ // Do not clamp a valid denominator to EPSILON: high-zoom Web Mercator deltas are often
11571
+ // smaller than that in common space, and changing their scale corrupts even flat paths.
11572
+ let safeLength2D = select(1.0, currLength2D, currLength2D > 0.0);
11573
+ var arcLengthRatio = 1.0;
11574
+ var pathPositionOffset = 0.0;
11575
+ var pathLength = segmentLength2D;
11576
+ if (path.billboard != 0.0) {
11577
+ // clipLine may shorten the visible screen-space segment. Preserve the corresponding interval
11578
+ // of the complete common-space arclength instead of compressing the full dash period into the
11579
+ // visible span. Keep pathLength complete so justification is stable as the camera clips it.
11580
+ let visiblePathLength = sourcePathLength * (sourcePathRange.y - sourcePathRange.x);
11581
+ arcLengthRatio = 0.0;
11582
+ if (segmentLength2D > 0.0) {
11583
+ arcLengthRatio = visiblePathLength / segmentLength2D;
11584
+ }
11585
+ pathPositionOffset = sourcePathLength * sourcePathRange.x;
11586
+ pathLength = sourcePathLength;
11587
+ } else if (currLength2D > 0.0) {
11588
+ arcLengthRatio = length(currDelta3) / safeLength2D;
11589
+ pathLength = segmentLength2D * arcLengthRatio;
11590
+ }
11591
+ #else
11409
11592
  let pathLength = select(lenB, lenA, isEnd);
11593
+ #endif
11410
11594
 
11411
11595
  let sinHalfA = abs(dot(miterVec, perp));
11412
11596
  let cosHalfA = abs(dot(dirA, miterVec));
@@ -11453,7 +11637,11 @@ fn getLineJoinOffset(
11453
11637
  #endif
11454
11638
  let pathPosition = vec2<f32>(
11455
11639
  dot(offsetFromStartOfPath, perp),
11640
+ #ifdef DASH_ENABLED
11641
+ pathPositionOffset + dot(offsetFromStartOfPath, dir) * arcLengthRatio
11642
+ #else
11456
11643
  dot(offsetFromStartOfPath, dir)
11644
+ #endif
11457
11645
  );
11458
11646
  let isValid = step(f32(instanceTypes), 3.5);
11459
11647
  #ifdef ANTIALIASING
@@ -11512,15 +11700,38 @@ fn vertexMain(attributes: Attributes) -> Varyings {
11512
11700
  ) / 2.0;
11513
11701
 
11514
11702
  if (path.billboard != 0.0) {
11515
- var prevPositionScreen = project_position_to_clipspace(prevPosition, prevPosition64Low, ZERO_OFFSET);
11703
+ #ifdef DASH_ENABLED
11704
+ let prevProjection = project_position_to_clipspace_and_commonspace(
11705
+ prevPosition, prevPosition64Low, ZERO_OFFSET
11706
+ );
11707
+ let nextProjection = project_position_to_clipspace_and_commonspace(
11708
+ nextPosition, nextPosition64Low, ZERO_OFFSET
11709
+ );
11710
+ let prevPositionCommon = prevProjection.commonPosition.xyz;
11711
+ let nextPositionCommon = nextProjection.commonPosition.xyz;
11712
+ var prevPositionScreen = prevProjection.clipPosition;
11713
+ var nextPositionScreen = nextProjection.clipPosition;
11714
+ #else
11715
+ var prevPositionScreen = project_position_to_clipspace(
11716
+ prevPosition, prevPosition64Low, ZERO_OFFSET
11717
+ );
11718
+ var nextPositionScreen = project_position_to_clipspace(
11719
+ nextPosition, nextPosition64Low, ZERO_OFFSET
11720
+ );
11721
+ #endif
11516
11722
  let currProjection = project_position_to_clipspace_and_commonspace(
11517
- currPosition,
11518
- currPosition64Low,
11519
- ZERO_OFFSET
11723
+ currPosition, currPosition64Low, ZERO_OFFSET
11520
11724
  );
11521
11725
  geometry.position = currProjection.commonPosition;
11522
11726
  var currPositionScreen = currProjection.clipPosition;
11523
- var nextPositionScreen = project_position_to_clipspace(nextPosition, nextPosition64Low, ZERO_OFFSET);
11727
+ #ifdef DASH_ENABLED
11728
+ let currPositionCommon = currProjection.commonPosition.xyz;
11729
+ let sourcePathStartScreen = mix(currPositionScreen, prevPositionScreen, isEnd);
11730
+ let sourcePathEndScreen = mix(nextPositionScreen, currPositionScreen, isEnd);
11731
+ let billboardPathRange = getClippedPathRange(
11732
+ sourcePathStartScreen.w, sourcePathEndScreen.w
11733
+ );
11734
+ #endif
11524
11735
 
11525
11736
  prevPositionScreen = clipLine(prevPositionScreen, currPositionScreen);
11526
11737
  nextPositionScreen = clipLine(nextPositionScreen, currPositionScreen);
@@ -11532,18 +11743,39 @@ fn vertexMain(attributes: Attributes) -> Varyings {
11532
11743
  (widthPixels + 0.5 / project.devicePixelRatio) / max(widthPixels, 1e-6),
11533
11744
  widthPixels > 0.0
11534
11745
  );
11746
+ #endif
11747
+ #ifdef DASH_ENABLED
11748
+ let currentDeltaCommon = select(
11749
+ nextPositionCommon - currPositionCommon,
11750
+ currPositionCommon - prevPositionCommon,
11751
+ isEnd > 0.0
11752
+ );
11753
+ let billboardPathLength = select(
11754
+ 0.0,
11755
+ length(currentDeltaCommon) * project.scale / (widthPixels * project.focalDistance),
11756
+ widthPixels > 0.0
11757
+ );
11535
11758
  #endif
11536
11759
  let join = getLineJoinOffset(
11537
11760
  prevPositionScreen.xyz / prevPositionScreen.w,
11538
11761
  currPositionScreen.xyz / currPositionScreen.w,
11539
11762
  nextPositionScreen.xyz / nextPositionScreen.w,
11540
11763
  project_pixel_size_to_clipspace(vec2<f32>(widthPixels, widthPixels)),
11764
+ #ifdef DASH_ENABLED
11765
+ billboardPathLength,
11766
+ billboardPathRange,
11767
+ #endif
11541
11768
  #ifdef ANTIALIASING
11542
11769
  coverageScale,
11543
11770
  #endif
11544
11771
  attributes.positions,
11545
11772
  attributes.instanceTypes
11546
11773
  );
11774
+ #ifdef DASH_ENABLED
11775
+ // Phase and justification use the complete source segment, while cap and joint coverage
11776
+ // must still recognize the endpoints moved by clipLine.
11777
+ varyings.vPathBounds = billboardPathLength * billboardPathRange;
11778
+ #endif
11547
11779
 
11548
11780
  geometry.uv = join.pathPosition;
11549
11781
  varyings.position = vec4<f32>(
@@ -11576,12 +11808,19 @@ fn vertexMain(attributes: Attributes) -> Varyings {
11576
11808
  currPositionCommon,
11577
11809
  nextPositionCommon,
11578
11810
  width,
11811
+ #ifdef DASH_ENABLED
11812
+ 1.0,
11813
+ vec2<f32>(0.0, 1.0),
11814
+ #endif
11579
11815
  #ifdef ANTIALIASING
11580
11816
  coverageScale,
11581
11817
  #endif
11582
11818
  attributes.positions,
11583
11819
  attributes.instanceTypes
11584
11820
  );
11821
+ #ifdef DASH_ENABLED
11822
+ varyings.vPathBounds = vec2<f32>(0.0, join.pathLength);
11823
+ #endif
11585
11824
 
11586
11825
  geometry.position = vec4<f32>(currPositionCommon + join.offset, 1.0);
11587
11826
  geometry.uv = join.pathPosition;
@@ -11612,7 +11851,13 @@ fn fragmentMain(varyings: Varyings) -> @location(0) vec4<f32> {
11612
11851
  // bounded by the corner offset, everywhere else by the edge of the stroke. Dividing by the
11613
11852
  // screen-space derivative converts the distance to the boundary into device pixels, which stays
11614
11853
  // correct under perspective foreshortening and under extensions that rescale the stroke.
11854
+ #ifdef DASH_ENABLED
11855
+ let isCorner =
11856
+ varyings.vPathPosition.y < varyings.vPathBounds.x ||
11857
+ varyings.vPathPosition.y > varyings.vPathBounds.y;
11858
+ #else
11615
11859
  let isCorner = varyings.vPathPosition.y < 0.0 || varyings.vPathPosition.y > varyings.vPathLength;
11860
+ #endif
11616
11861
  let isRound = varyings.vJointType > 0.5;
11617
11862
 
11618
11863
  // Distance to the silhouette in device pixels, from the derivative of the coordinate that
@@ -11623,7 +11868,13 @@ fn fragmentMain(varyings: Varyings) -> @location(0) vec4<f32> {
11623
11868
  // Both evaluated so each derivative stays on one field across the corner/body boundary
11624
11869
  let bodyPixels = (1.0 - bodyCoord) / max(fwidth(bodyCoord), 1e-6);
11625
11870
  let cornerPixels = (1.0 - cornerCoord) / max(fwidth(cornerCoord), 1e-6);
11871
+ #ifdef PATH_STYLE_OFFSET
11872
+ // Rounded corners still intersect the stroke-width envelope. Extensions may remap
11873
+ // vPathPosition.x independently of vCornerOffset, as PathStyleExtension does for offsets.
11874
+ let edgePixels = select(bodyPixels, min(cornerPixels, bodyPixels), isRound && isCorner);
11875
+ #else
11626
11876
  let edgePixels = select(bodyPixels, cornerPixels, isRound && isCorner);
11877
+ #endif
11627
11878
 
11628
11879
  // Fragments outside the coverage ramp must not write depth or picking colors.
11629
11880
  if (edgePixels <= -SMOOTH_EDGE_RADIUS) {
@@ -11641,11 +11892,18 @@ fn fragmentMain(varyings: Varyings) -> @location(0) vec4<f32> {
11641
11892
  // Feather one device pixel across the width only, before premultiplication. edgePixels is a
11642
11893
  // signed device-pixel distance and SMOOTH_EDGE_RADIUS is 0.5, so this ramps across one pixel.
11643
11894
  color.a *= smoothedge(0.0, edgePixels);
11895
+ #else
11896
+ #ifdef DASH_ENABLED
11897
+ if (
11898
+ varyings.vPathPosition.y < varyings.vPathBounds.x ||
11899
+ varyings.vPathPosition.y > varyings.vPathBounds.y
11900
+ ) {
11644
11901
  #else
11645
11902
  if (
11646
11903
  varyings.vPathPosition.y < 0.0 ||
11647
11904
  varyings.vPathPosition.y > varyings.vPathLength
11648
11905
  ) {
11906
+ #endif
11649
11907
  if (varyings.vJointType > 0.5 && length(varyings.vCornerOffset) > 1.0) {
11650
11908
  discard;
11651
11909
  }
@@ -11698,6 +11956,9 @@ out float vMiterLength;
11698
11956
  out vec2 vPathPosition;
11699
11957
  out float vPathLength;
11700
11958
  out float vJointType;
11959
+ #ifdef DASH_ENABLED
11960
+ out vec2 vPathBounds;
11961
+ #endif
11701
11962
 
11702
11963
  const float EPSILON = 0.001;
11703
11964
  const vec3 ZERO_OFFSET = vec3(0.0);
@@ -11710,6 +11971,9 @@ float flipIfTrue(bool flag) {
11710
11971
  vec3 getLineJoinOffset(
11711
11972
  vec3 prevPoint, vec3 currPoint, vec3 nextPoint,
11712
11973
  vec2 width
11974
+ #ifdef DASH_ENABLED
11975
+ , float sourcePathLength, vec2 sourcePathRange
11976
+ #endif
11713
11977
  #ifdef ANTIALIASING
11714
11978
  , float coverageScale
11715
11979
  #endif
@@ -11752,6 +12016,37 @@ vec3 getLineJoinOffset(
11752
12016
  // length of the segment
11753
12017
  float L = isEnd ? lenA : lenB;
11754
12018
 
12019
+ #ifdef DASH_ENABLED
12020
+ // Extrusion happens in the XY plane, so L above is a 2D length and vPathPosition.y below
12021
+ // measures 2D distance along the segment. For a path that also moves in Z the true arc
12022
+ // length is longer by this ratio. Scaling vPathLength and vPathPosition.y by it makes the
12023
+ // coordinate measure real 3D distance - which is what CPU-side dash offsets accumulate -
12024
+ // while leaving the joint tests in the fragment shader unchanged, since they compare the
12025
+ // two against each other and both are scaled alike.
12026
+ // Billboard mode extrudes in clip space, where the perspective divide has already reduced
12027
+ // the segment to its screen projection, so its complete common-space length is supplied by
12028
+ // the caller.
12029
+ vec3 currDelta3 = isEnd ? deltaA3 : deltaB3;
12030
+ float currLength2D = length(currDelta3.xy);
12031
+ float arcLengthRatio = 1.0;
12032
+ float pathPositionOffset = 0.0;
12033
+ float pathLength = L;
12034
+ if (path.billboard) {
12035
+ // clipLine may shorten the visible screen-space segment. Preserve the corresponding interval
12036
+ // of the complete common-space arclength instead of compressing the full dash period into the
12037
+ // visible span. Keep pathLength complete so justification is stable as the camera clips it.
12038
+ float visiblePathLength = sourcePathLength * (sourcePathRange.y - sourcePathRange.x);
12039
+ arcLengthRatio = L > 0.0 ? visiblePathLength / L : 0.0;
12040
+ pathPositionOffset = sourcePathLength * sourcePathRange.x;
12041
+ pathLength = sourcePathLength;
12042
+ } else if (currLength2D > 0.0) {
12043
+ // Do not clamp a valid denominator to EPSILON: high-zoom Web Mercator deltas are often
12044
+ // smaller than that in common space, and changing their scale corrupts even flat paths.
12045
+ arcLengthRatio = length(currDelta3) / currLength2D;
12046
+ pathLength = L * arcLengthRatio;
12047
+ }
12048
+ #endif
12049
+
11755
12050
  // A = angle of the corner
11756
12051
  float sinHalfA = abs(dot(miterVec, perp));
11757
12052
  float cosHalfA = abs(dot(dirA, miterVec));
@@ -11793,7 +12088,11 @@ vec3 getLineJoinOffset(
11793
12088
  // The physical stroke still ends at offsetVec; the scaled coordinates and vertices only extend
11794
12089
  // its rasterized envelope to include the outer half of the centered coverage ramp.
11795
12090
  vec2 coverageOffsetVec = offsetVec * coverageScale;
12091
+ #ifdef DASH_ENABLED
12092
+ vPathLength = pathLength;
12093
+ #else
11796
12094
  vPathLength = L;
12095
+ #endif
11797
12096
  vCornerOffset = coverageOffsetVec;
11798
12097
  vMiterLength = dot(vCornerOffset, miterVec * turnDirection);
11799
12098
  vMiterLength = isCap ? isJoint : vMiterLength;
@@ -11801,7 +12100,11 @@ vec3 getLineJoinOffset(
11801
12100
  vec2 offsetFromStartOfPath = coverageOffsetVec + deltaA * float(isEnd);
11802
12101
  vPathPosition = vec2(
11803
12102
  dot(offsetFromStartOfPath, perp),
12103
+ #ifdef DASH_ENABLED
12104
+ pathPositionOffset + dot(offsetFromStartOfPath, dir) * arcLengthRatio
12105
+ #else
11804
12106
  dot(offsetFromStartOfPath, dir)
12107
+ #endif
11805
12108
  );
11806
12109
  geometry.uv = vPathPosition;
11807
12110
 
@@ -11809,7 +12112,11 @@ vec3 getLineJoinOffset(
11809
12112
  vec3 offset = vec3(coverageOffsetVec * width * isValid, 0.0);
11810
12113
  #else
11811
12114
  // Generate variables for fragment shader
12115
+ #ifdef DASH_ENABLED
12116
+ vPathLength = pathLength;
12117
+ #else
11812
12118
  vPathLength = L;
12119
+ #endif
11813
12120
  vCornerOffset = offsetVec;
11814
12121
  vMiterLength = dot(vCornerOffset, miterVec * turnDirection);
11815
12122
  vMiterLength = isCap ? isJoint : vMiterLength;
@@ -11817,7 +12124,11 @@ vec3 getLineJoinOffset(
11817
12124
  vec2 offsetFromStartOfPath = vCornerOffset + deltaA * float(isEnd);
11818
12125
  vPathPosition = vec2(
11819
12126
  dot(offsetFromStartOfPath, perp),
12127
+ #ifdef DASH_ENABLED
12128
+ pathPositionOffset + dot(offsetFromStartOfPath, dir) * arcLengthRatio
12129
+ #else
11820
12130
  dot(offsetFromStartOfPath, dir)
12131
+ #endif
11821
12132
  );
11822
12133
  geometry.uv = vPathPosition;
11823
12134
 
@@ -11839,6 +12150,22 @@ void clipLine(inout vec4 position, vec4 refPosition) {
11839
12150
  }
11840
12151
  }
11841
12152
 
12153
+ #ifdef DASH_ENABLED
12154
+ // Return the visible interval of the original segment before clipLine moves either endpoint.
12155
+ vec2 getClippedPathRange(float startW, float endW) {
12156
+ bool startClipped = startW < EPSILON;
12157
+ bool endClipped = endW < EPSILON;
12158
+ if (startClipped && endClipped) {
12159
+ return vec2(0.0);
12160
+ }
12161
+ if (startClipped || endClipped) {
12162
+ float intersection = clamp((EPSILON - startW) / (endW - startW), 0.0, 1.0);
12163
+ return startClipped ? vec2(intersection, 1.0) : vec2(0.0, intersection);
12164
+ }
12165
+ return vec2(0.0, 1.0);
12166
+ }
12167
+ #endif
12168
+
11842
12169
  void main() {
11843
12170
  geometry.pickingColor = picking_getPickingColorFromIndex(rowIndexes);
11844
12171
 
@@ -11863,9 +12190,35 @@ void main() {
11863
12190
 
11864
12191
  if (path.billboard) {
11865
12192
  // Extrude in clipspace
11866
- vec4 prevPositionScreen = project_position_to_clipspace(prevPosition, prevPosition64Low, ZERO_OFFSET);
12193
+ #ifdef DASH_ENABLED
12194
+ vec4 prevPositionCommon;
12195
+ vec4 nextPositionCommon;
12196
+ vec4 prevPositionScreen = project_position_to_clipspace(
12197
+ prevPosition, prevPosition64Low, ZERO_OFFSET, prevPositionCommon
12198
+ );
12199
+ #else
12200
+ vec4 prevPositionScreen = project_position_to_clipspace(
12201
+ prevPosition, prevPosition64Low, ZERO_OFFSET
12202
+ );
12203
+ #endif
11867
12204
  vec4 currPositionScreen = project_position_to_clipspace(currPosition, currPosition64Low, ZERO_OFFSET, geometry.position);
11868
- vec4 nextPositionScreen = project_position_to_clipspace(nextPosition, nextPosition64Low, ZERO_OFFSET);
12205
+ #ifdef DASH_ENABLED
12206
+ vec4 nextPositionScreen = project_position_to_clipspace(
12207
+ nextPosition, nextPosition64Low, ZERO_OFFSET, nextPositionCommon
12208
+ );
12209
+ #else
12210
+ vec4 nextPositionScreen = project_position_to_clipspace(
12211
+ nextPosition, nextPosition64Low, ZERO_OFFSET
12212
+ );
12213
+ #endif
12214
+
12215
+ #ifdef DASH_ENABLED
12216
+ vec4 sourcePathStartScreen = mix(currPositionScreen, prevPositionScreen, isEnd);
12217
+ vec4 sourcePathEndScreen = mix(nextPositionScreen, currPositionScreen, isEnd);
12218
+ vec2 billboardPathRange = getClippedPathRange(
12219
+ sourcePathStartScreen.w, sourcePathEndScreen.w
12220
+ );
12221
+ #endif
11869
12222
 
11870
12223
  clipLine(prevPositionScreen, currPositionScreen);
11871
12224
  clipLine(nextPositionScreen, currPositionScreen);
@@ -11880,16 +12233,34 @@ void main() {
11880
12233
  : 1.0;
11881
12234
  #endif
11882
12235
 
12236
+ #ifdef DASH_ENABLED
12237
+ vec3 currentDeltaCommon = isEnd > 0.0
12238
+ ? geometry.position.xyz - prevPositionCommon.xyz
12239
+ : nextPositionCommon.xyz - geometry.position.xyz;
12240
+ float billboardPathLength = width.x > 0.0
12241
+ ? length(currentDeltaCommon) * project.scale / (width.x * project.focalDistance)
12242
+ : 0.0;
12243
+ #endif
12244
+
11883
12245
  vec3 offset = getLineJoinOffset(
11884
12246
  prevPositionScreen.xyz / prevPositionScreen.w,
11885
12247
  currPositionScreen.xyz / currPositionScreen.w,
11886
12248
  nextPositionScreen.xyz / nextPositionScreen.w,
11887
12249
  project_pixel_size_to_clipspace(width.xy)
12250
+ #ifdef DASH_ENABLED
12251
+ ,
12252
+ billboardPathLength, billboardPathRange
12253
+ #endif
11888
12254
  #ifdef ANTIALIASING
11889
12255
  ,
11890
12256
  coverageScale
11891
12257
  #endif
11892
12258
  );
12259
+ #ifdef DASH_ENABLED
12260
+ // Phase and justification use the complete source segment, while cap and joint coverage
12261
+ // must still recognize the endpoints moved by clipLine.
12262
+ vPathBounds = billboardPathLength * billboardPathRange;
12263
+ #endif
11893
12264
 
11894
12265
  DECKGL_FILTER_GL_POSITION(currPositionScreen, geometry);
11895
12266
  gl_Position = vec4(currPositionScreen.xyz + offset * currPositionScreen.w, currPositionScreen.w);
@@ -11910,10 +12281,16 @@ void main() {
11910
12281
 
11911
12282
  vec3 offset = getLineJoinOffset(
11912
12283
  prevPosition, currPosition, nextPosition, width.xy
12284
+ #ifdef DASH_ENABLED
12285
+ , 1.0, vec2(0.0, 1.0)
12286
+ #endif
11913
12287
  #ifdef ANTIALIASING
11914
12288
  , coverageScale
11915
12289
  #endif
11916
12290
  );
12291
+ #ifdef DASH_ENABLED
12292
+ vPathBounds = vec2(0.0, vPathLength);
12293
+ #endif
11917
12294
  geometry.position = vec4(currPosition + offset, 1.0);
11918
12295
  gl_Position = project_common_position_to_clipspace(geometry.position);
11919
12296
  DECKGL_FILTER_GL_POSITION(gl_Position, geometry);
@@ -11939,6 +12316,9 @@ in float vMiterLength;
11939
12316
  in vec2 vPathPosition;
11940
12317
  in float vPathLength;
11941
12318
  in float vJointType;
12319
+ #ifdef DASH_ENABLED
12320
+ in vec2 vPathBounds;
12321
+ #endif
11942
12322
 
11943
12323
  out vec4 fragColor;
11944
12324
 
@@ -11946,7 +12326,11 @@ void main(void) {
11946
12326
  geometry.uv = vPathPosition;
11947
12327
 
11948
12328
  #ifdef ANTIALIASING
12329
+ #ifdef DASH_ENABLED
12330
+ bool isCorner = vPathPosition.y < vPathBounds.x || vPathPosition.y > vPathBounds.y;
12331
+ #else
11949
12332
  bool isCorner = vPathPosition.y < 0.0 || vPathPosition.y > vPathLength;
12333
+ #endif
11950
12334
  bool isRound = vJointType > 0.5;
11951
12335
 
11952
12336
  // Distance to the silhouette in device pixels, from the derivative of the coordinate that
@@ -11957,7 +12341,13 @@ void main(void) {
11957
12341
  // Both evaluated so each derivative stays on one field across the corner/body boundary
11958
12342
  float bodyPixels = (1.0 - bodyCoord) / max(fwidth(bodyCoord), 1e-6);
11959
12343
  float cornerPixels = (1.0 - cornerCoord) / max(fwidth(cornerCoord), 1e-6);
12344
+ #ifdef PATH_STYLE_OFFSET
12345
+ // Rounded corners still intersect the stroke-width envelope. Extensions may remap
12346
+ // vPathPosition.x independently of vCornerOffset, as PathStyleExtension does for offsets.
12347
+ float edgePixels = isRound && isCorner ? min(cornerPixels, bodyPixels) : bodyPixels;
12348
+ #else
11960
12349
  float edgePixels = isRound && isCorner ? cornerPixels : bodyPixels;
12350
+ #endif
11961
12351
 
11962
12352
  // Fragments outside the coverage ramp must not write depth or picking colors.
11963
12353
  if (edgePixels <= -SMOOTH_EDGE_RADIUS) {
@@ -11976,8 +12366,12 @@ void main(void) {
11976
12366
  // edgePixels is a signed device-pixel distance, and SMOOTH_EDGE_RADIUS is 0.5, so smoothedge
11977
12367
  // ramps across exactly one pixel centered on the edge.
11978
12368
  fragColor.a *= smoothedge(0.0, edgePixels);
12369
+ #else
12370
+ #ifdef DASH_ENABLED
12371
+ if (vPathPosition.y < vPathBounds.x || vPathPosition.y > vPathBounds.y) {
11979
12372
  #else
11980
12373
  if (vPathPosition.y < 0.0 || vPathPosition.y > vPathLength) {
12374
+ #endif
11981
12375
  // if joint is rounded, test distance from the corner
11982
12376
  if (vJointType > 0.5 && length(vCornerOffset) > 1.0) {
11983
12377
  discard;
@@ -12018,6 +12412,18 @@ var ATTRIBUTE_TRANSITION = {
12018
12412
  return chunk.length ? chunk.subarray(chunk.length - value.length) : value;
12019
12413
  }
12020
12414
  };
12415
+ function getPathProjectionScale(viewport) {
12416
+ if (viewport.isGeospatial) {
12417
+ return null;
12418
+ }
12419
+ const { unitsPerMeter: unitsPerMeter2 } = viewport.distanceScales;
12420
+ return [unitsPerMeter2[0], unitsPerMeter2[1], unitsPerMeter2[2]];
12421
+ }
12422
+ function pathProjectionScalesEqual(left, right) {
12423
+ return left === right || Boolean(
12424
+ left && right && left.length === right.length && left.every((value, index) => value === right[index])
12425
+ );
12426
+ }
12021
12427
  var PathLayer = class extends Layer {
12022
12428
  getShaders() {
12023
12429
  const { antialiasing } = this.props;
@@ -12045,6 +12451,43 @@ var PathLayer = class extends Layer {
12045
12451
  }
12046
12452
  return (_a = this.getAttributeManager()) == null ? void 0 : _a.getBounds(["vertexPositions"]);
12047
12453
  }
12454
+ getPathProjectionScale(viewport) {
12455
+ var _a;
12456
+ const coordinateSystem = this.props.coordinateSystem;
12457
+ const hasDashMetrics = Boolean((_a = this.getAttributeManager()) == null ? void 0 : _a.getAttributes().instanceDashOffsets);
12458
+ if (!hasDashMetrics) {
12459
+ return null;
12460
+ }
12461
+ const trackViewportScale = viewport instanceof WebMercatorViewport && viewport.zoom >= 12 && // Offset coordinate systems use their fixed coordinateOrigin as the projection origin.
12462
+ // Default/lnglat and preprojected Cartesian paths follow the viewport center instead.
12463
+ (coordinateSystem === "default" || coordinateSystem === "lnglat" || coordinateSystem === "cartesian");
12464
+ if (trackViewportScale) {
12465
+ const projectUniforms = project_default.getUniforms({
12466
+ viewport,
12467
+ coordinateSystem,
12468
+ coordinateOrigin: this.props.coordinateOrigin,
12469
+ autoWrapLongitude: this.wrapLongitude
12470
+ });
12471
+ return [
12472
+ viewport.projectionMode,
12473
+ projectUniforms.coordinateOrigin[1],
12474
+ projectUniforms.commonOrigin[1],
12475
+ ...projectUniforms.commonUnitsPerWorldUnit,
12476
+ ...projectUniforms.commonUnitsPerWorldUnit2,
12477
+ projectUniforms.commonUnitsPerMeter[2]
12478
+ ];
12479
+ }
12480
+ const projectionScale = getPathProjectionScale(viewport);
12481
+ return projectionScale ? [viewport.projectionMode, ...projectionScale] : [viewport.projectionMode];
12482
+ }
12483
+ shouldUpdateState(params) {
12484
+ var _a, _b;
12485
+ const { viewport } = this.context;
12486
+ return super.shouldUpdateState(params) || ((_a = this.state) == null ? void 0 : _a.tessellationResolution) !== viewport.resolution || !pathProjectionScalesEqual(
12487
+ (_b = this.state) == null ? void 0 : _b.pathProjectionScale,
12488
+ this.getPathProjectionScale(viewport)
12489
+ );
12490
+ }
12048
12491
  initializeState() {
12049
12492
  const noAlloc = true;
12050
12493
  const isWebGPU = this.context.device.type === "webgpu";
@@ -12135,7 +12578,9 @@ var PathLayer = class extends Layer {
12135
12578
  pathTesselator: new PathTesselator({
12136
12579
  fp64: this.use64bitPositions(),
12137
12580
  isWebGPU
12138
- })
12581
+ }),
12582
+ tessellationResolution: this.context.viewport.resolution,
12583
+ pathProjectionScale: this.getPathProjectionScale(this.context.viewport)
12139
12584
  });
12140
12585
  }
12141
12586
  updateState(params) {
@@ -12143,7 +12588,16 @@ var PathLayer = class extends Layer {
12143
12588
  super.updateState(params);
12144
12589
  const { props, oldProps, changeFlags } = params;
12145
12590
  const attributeManager = this.getAttributeManager();
12146
- const geometryChanged = changeFlags.dataChanged || changeFlags.updateTriggersChanged && (changeFlags.updateTriggersChanged.all || changeFlags.updateTriggersChanged.getPath);
12591
+ const { viewport } = this.context;
12592
+ const tessellationResolutionChanged = this.state.tessellationResolution !== viewport.resolution;
12593
+ const pathProjectionScale = this.getPathProjectionScale(viewport);
12594
+ const pathProjectionScaleChanged = !pathProjectionScalesEqual(
12595
+ this.state.pathProjectionScale,
12596
+ pathProjectionScale
12597
+ );
12598
+ const getPathChanged = changeFlags.updateTriggersChanged && (changeFlags.updateTriggersChanged.all || changeFlags.updateTriggersChanged.getPath);
12599
+ const geometryConfigurationChanged = getPathChanged || props._pathType !== oldProps._pathType || props.positionFormat !== oldProps.positionFormat || props.wrapLongitude !== oldProps.wrapLongitude || tessellationResolutionChanged;
12600
+ const geometryChanged = changeFlags.dataChanged || geometryConfigurationChanged;
12147
12601
  if (geometryChanged) {
12148
12602
  const { pathTesselator } = this.state;
12149
12603
  const buffers = props.data.attributes || {};
@@ -12157,16 +12611,24 @@ var PathLayer = class extends Layer {
12157
12611
  positionFormat: props.positionFormat,
12158
12612
  wrapLongitude: props.wrapLongitude,
12159
12613
  // TODO - move the flag out of the viewport
12160
- resolution: this.context.viewport.resolution,
12161
- dataChanged: changeFlags.dataChanged
12614
+ resolution: viewport.resolution,
12615
+ // A partial data diff is only valid while normalization inputs remain unchanged.
12616
+ dataChanged: geometryConfigurationChanged ? void 0 : changeFlags.dataChanged
12162
12617
  });
12163
12618
  this.setState({
12164
12619
  numInstances: pathTesselator.instanceCount,
12165
- startIndices: pathTesselator.vertexStarts
12620
+ startIndices: pathTesselator.vertexStarts,
12621
+ tessellationResolution: viewport.resolution,
12622
+ pathProjectionScale
12166
12623
  });
12167
- if (!changeFlags.dataChanged) {
12624
+ if (!changeFlags.dataChanged || geometryConfigurationChanged) {
12168
12625
  attributeManager.invalidateAll();
12626
+ } else if (pathProjectionScaleChanged) {
12627
+ attributeManager.invalidate("instanceDashOffsets");
12169
12628
  }
12629
+ } else if (pathProjectionScaleChanged) {
12630
+ this.setState({ pathProjectionScale });
12631
+ attributeManager.invalidate("instanceDashOffsets");
12170
12632
  }
12171
12633
  if (changeFlags.extensionsChanged || props.antialiasing !== oldProps.antialiasing) {
12172
12634
  (_a = this.state.model) == null ? void 0 : _a.destroy();
@@ -16673,7 +17135,7 @@ GeoCellLayer.defaultProps = defaultProps12;
16673
17135
 
16674
17136
  // ../geo-layers/src/h3-layers/h3-utils.ts
16675
17137
  var import_h3_js = require("h3-js");
16676
- var import_core34 = require("@math.gl/core");
17138
+ var import_core33 = require("@math.gl/core");
16677
17139
  function normalizeLongitudes(vertices, refLng) {
16678
17140
  refLng = refLng === void 0 ? vertices[0][0] : refLng;
16679
17141
  for (const pt of vertices) {
@@ -16691,8 +17153,8 @@ function scalePolygon(hexId, vertices, factor) {
16691
17153
  normalizeLongitudes(vertices, lng);
16692
17154
  const vertexCount = vertices[0] === vertices[actualCount - 1] ? actualCount - 1 : actualCount;
16693
17155
  for (let i = 0; i < vertexCount; i++) {
16694
- vertices[i][0] = (0, import_core34.lerp)(lng, vertices[i][0], factor);
16695
- vertices[i][1] = (0, import_core34.lerp)(lat, vertices[i][1], factor);
17156
+ vertices[i][0] = (0, import_core33.lerp)(lng, vertices[i][0], factor);
17157
+ vertices[i][1] = (0, import_core33.lerp)(lat, vertices[i][1], factor);
16696
17158
  }
16697
17159
  }
16698
17160
  function getHexagonCentroid(getHexagon, object, objectInfo) {
@@ -16721,7 +17183,7 @@ function flattenPolygon(vertices) {
16721
17183
 
16722
17184
  // ../geo-layers/src/tileset-2d/tileset-2d.ts
16723
17185
  var import_loader_utils = require("@loaders.gl/loader-utils");
16724
- var import_core36 = require("@math.gl/core");
17186
+ var import_core35 = require("@math.gl/core");
16725
17187
 
16726
17188
  // ../geo-layers/src/tileset-2d/tile-2d-header.ts
16727
17189
  var Tile2DHeader = class {
@@ -16852,7 +17314,7 @@ var Tile2DHeader = class {
16852
17314
 
16853
17315
  // ../geo-layers/src/tileset-2d/tile-2d-traversal.ts
16854
17316
  var import_culling = require("@math.gl/culling");
16855
- var import_web_mercator7 = require("@math.gl/web-mercator");
17317
+ var import_web_mercator6 = require("@math.gl/web-mercator");
16856
17318
  var TILE_SIZE = 512;
16857
17319
  var MAX_MAPS = 3;
16858
17320
  var REF_POINTS_5 = [
@@ -16998,8 +17460,8 @@ function getOSMTileIndices(viewport, maxZ, zRange, bounds) {
16998
17460
  const minZ = viewport instanceof WebMercatorViewport && viewport.pitch <= 60 ? maxZ : 0;
16999
17461
  if (bounds) {
17000
17462
  const [minLng, minLat, maxLng, maxLat] = bounds;
17001
- const topLeft = (0, import_web_mercator7.lngLatToWorld)([minLng, maxLat]);
17002
- const bottomRight = (0, import_web_mercator7.lngLatToWorld)([maxLng, minLat]);
17463
+ const topLeft = (0, import_web_mercator6.lngLatToWorld)([minLng, maxLat]);
17464
+ const bottomRight = (0, import_web_mercator6.lngLatToWorld)([maxLng, minLat]);
17003
17465
  bounds = [topLeft[0], TILE_SIZE - topLeft[1], bottomRight[0], TILE_SIZE - bottomRight[1]];
17004
17466
  }
17005
17467
  const root = new OSMNode(0, 0, 0);
@@ -17366,8 +17828,8 @@ var Tileset2D = class {
17366
17828
  this._zRange = null;
17367
17829
  this._selectedTiles = null;
17368
17830
  this._frameNumber = 0;
17369
- this._modelMatrix = new import_core36.Matrix4();
17370
- this._modelMatrixInverse = new import_core36.Matrix4();
17831
+ this._modelMatrix = new import_core35.Matrix4();
17832
+ this._modelMatrixInverse = new import_core35.Matrix4();
17371
17833
  }
17372
17834
  /* Public API */
17373
17835
  get tiles() {
@@ -17420,9 +17882,9 @@ var Tileset2D = class {
17420
17882
  zRange: null,
17421
17883
  modelMatrix: null
17422
17884
  }) {
17423
- const modelMatrixAsMatrix4 = modelMatrix ? new import_core36.Matrix4(modelMatrix) : new import_core36.Matrix4();
17885
+ const modelMatrixAsMatrix4 = modelMatrix ? new import_core35.Matrix4(modelMatrix) : new import_core35.Matrix4();
17424
17886
  const isModelMatrixNew = !modelMatrixAsMatrix4.equals(this._modelMatrix);
17425
- if (!this._viewport || !viewport.equals(this._viewport) || !(0, import_core36.equals)(this._zRange, zRange) || isModelMatrixNew) {
17887
+ if (!this._viewport || !viewport.equals(this._viewport) || !(0, import_core35.equals)(this._zRange, zRange) || isModelMatrixNew) {
17426
17888
  if (isModelMatrixNew) {
17427
17889
  this._modelMatrixInverse = modelMatrixAsMatrix4.clone().invert();
17428
17890
  this._modelMatrix = modelMatrixAsMatrix4;
@@ -17471,7 +17933,7 @@ var Tileset2D = class {
17471
17933
  if ("west" in bbox) {
17472
17934
  overlaps = bbox.west < maxX && bbox.east > minX && bbox.south < maxY && bbox.north > minY;
17473
17935
  } else {
17474
- if (modelMatrix && !import_core36.Matrix4.IDENTITY.equals(modelMatrix)) {
17936
+ if (modelMatrix && !import_core35.Matrix4.IDENTITY.equals(modelMatrix)) {
17475
17937
  const [left, top, right, bottom] = transformBox(
17476
17938
  [bbox.left, bbox.top, bbox.right, bbox.bottom],
17477
17939
  modelMatrix
@@ -17792,7 +18254,7 @@ function getPlaceholderInChildren(tile) {
17792
18254
  }
17793
18255
 
17794
18256
  // ../geo-layers/src/tile-layer/tile-layer.ts
17795
- var import_core38 = require("@math.gl/core");
18257
+ var import_core37 = require("@math.gl/core");
17796
18258
  var defaultProps13 = {
17797
18259
  TilesetClass: Tileset2D,
17798
18260
  data: { type: "data", value: [] },
@@ -18013,7 +18475,7 @@ var TileLayer = class extends CompositeLayer {
18013
18475
  return this.state.tileset.isTileVisible(
18014
18476
  tile,
18015
18477
  cullRect,
18016
- modelMatrix ? new import_core38.Matrix4(modelMatrix) : null
18478
+ modelMatrix ? new import_core37.Matrix4(modelMatrix) : null
18017
18479
  );
18018
18480
  }
18019
18481
  };
@@ -18468,7 +18930,7 @@ var shader_module_default = {
18468
18930
  };
18469
18931
 
18470
18932
  // ../extensions/src/collision-filter/collision-filter-effect.ts
18471
- var import_core43 = require("@math.gl/core");
18933
+ var import_core42 = require("@math.gl/core");
18472
18934
 
18473
18935
  // ../extensions/src/collision-filter/collision-filter-pass.ts
18474
18936
  var CollisionFilterPass = class extends LayersPass {
@@ -18579,7 +19041,7 @@ var CollisionFilterEffect = class {
18579
19041
  const needsRender = viewportChanged || // If render info is new
18580
19042
  renderInfo === oldRenderInfo || // If sublayers have changed
18581
19043
  !deepEqual(oldRenderInfo.layers, renderInfo.layers, 1) || // If a sublayer's bounds have been updated
18582
- renderInfo.layerBounds.some((b, i) => !(0, import_core43.equals)(b, oldRenderInfo.layerBounds[i])) || // If a sublayer's isLoaded state has been updated
19044
+ renderInfo.layerBounds.some((b, i) => !(0, import_core42.equals)(b, oldRenderInfo.layerBounds[i])) || // If a sublayer's isLoaded state has been updated
18583
19045
  renderInfo.allLayersLoaded !== oldRenderInfo.allLayersLoaded || // Some prop is in transition
18584
19046
  renderInfo.layers.some((layer) => layer.props.transitions);
18585
19047
  this.channels[collisionGroup] = renderInfo;
@@ -18745,12 +19207,12 @@ CollisionFilterExtension.defaultProps = defaultProps16;
18745
19207
  CollisionFilterExtension.extensionName = "CollisionFilterExtension";
18746
19208
 
18747
19209
  // ../geo-layers/src/mvt-layer/mvt-layer.ts
18748
- var import_core48 = require("@math.gl/core");
19210
+ var import_core47 = require("@math.gl/core");
18749
19211
  var import_mvt = require("@loaders.gl/mvt");
18750
19212
  var import_gis = require("@loaders.gl/gis");
18751
19213
 
18752
19214
  // ../geo-layers/src/mvt-layer/coordinate-transform.ts
18753
- var import_core46 = require("@math.gl/core");
19215
+ var import_core45 = require("@math.gl/core");
18754
19216
  var availableTransformations = {
18755
19217
  Point,
18756
19218
  MultiPoint,
@@ -18760,8 +19222,8 @@ var availableTransformations = {
18760
19222
  MultiPolygon
18761
19223
  };
18762
19224
  function Point([pointX, pointY], [nw, se], viewport) {
18763
- const x = (0, import_core46.lerp)(nw[0], se[0], pointX);
18764
- const y = (0, import_core46.lerp)(nw[1], se[1], pointY);
19225
+ const x = (0, import_core45.lerp)(nw[0], se[0], pointX);
19226
+ const y = (0, import_core45.lerp)(nw[1], se[1], pointY);
18765
19227
  return viewport.unprojectFlat([x, y]);
18766
19228
  }
18767
19229
  function getPoints(geometry, bbox, viewport) {
@@ -19004,7 +19466,7 @@ var MVTLayer = class extends TileLayer {
19004
19466
  const yScale = -xScale;
19005
19467
  const xOffset = WORLD_SIZE * x / worldScale;
19006
19468
  const yOffset = WORLD_SIZE * (1 - y / worldScale);
19007
- const modelMatrix = new import_core48.Matrix4().scale([xScale, yScale, 1]);
19469
+ const modelMatrix = new import_core47.Matrix4().scale([xScale, yScale, 1]);
19008
19470
  props.autoHighlight = false;
19009
19471
  if (!this.context.viewport.resolution) {
19010
19472
  props.modelMatrix = modelMatrix;
@@ -19180,7 +19642,7 @@ function transformTileCoordsToWGS84(object, bbox, viewport) {
19180
19642
  }
19181
19643
 
19182
19644
  // dist/layers/cluster-tile-layer.js
19183
- var import_core53 = require("@loaders.gl/core");
19645
+ var import_core52 = require("@loaders.gl/core");
19184
19646
  var import_gis2 = require("@loaders.gl/gis");
19185
19647
 
19186
19648
  // dist/layers/cluster-utils.js
@@ -19502,7 +19964,7 @@ var H3Tileset2D = class extends Tileset2D {
19502
19964
  };
19503
19965
 
19504
19966
  // dist/layers/quadbin-utils.js
19505
- var import_web_mercator8 = require("@math.gl/web-mercator");
19967
+ var import_web_mercator7 = require("@math.gl/web-mercator");
19506
19968
  var import_quadbin3 = require("quadbin");
19507
19969
  var TILE_SIZE3 = 512;
19508
19970
  function quadbinToOffset(quadbin) {
@@ -19519,8 +19981,8 @@ function quadbinToWorldBounds(quadbin, coverage) {
19519
19981
  }
19520
19982
  function getQuadbinPolygon(quadbin, coverage = 1) {
19521
19983
  const [topLeft, bottomRight] = quadbinToWorldBounds(quadbin, coverage);
19522
- const [w, n] = (0, import_web_mercator8.worldToLngLat)(topLeft);
19523
- const [e, s] = (0, import_web_mercator8.worldToLngLat)(bottomRight);
19984
+ const [w, n] = (0, import_web_mercator7.worldToLngLat)(topLeft);
19985
+ const [e, s] = (0, import_web_mercator7.worldToLngLat)(bottomRight);
19524
19986
  return [e, n, e, s, w, s, w, n, e, n];
19525
19987
  }
19526
19988
 
@@ -19789,7 +20251,7 @@ function binaryToSpatialjson(binary) {
19789
20251
  }
19790
20252
 
19791
20253
  // dist/layers/schema/carto-spatial-tile-loader.js
19792
- var VERSION = true ? "9.4.0-beta.1" : "latest";
20254
+ var VERSION = true ? "9.4.0-beta.3" : "latest";
19793
20255
  var id = "cartoSpatialTile";
19794
20256
  var DEFAULT_OPTIONS = {
19795
20257
  cartoSpatialTile: {
@@ -19902,7 +20364,7 @@ var TilejsonPropType = {
19902
20364
  };
19903
20365
 
19904
20366
  // dist/layers/cluster-tile-layer.js
19905
- (0, import_core53.registerLoaders)([carto_spatial_tile_loader_default]);
20367
+ (0, import_core52.registerLoaders)([carto_spatial_tile_loader_default]);
19906
20368
  function getScheme(tilesetClass) {
19907
20369
  if (tilesetClass === H3Tileset2D)
19908
20370
  return "h3";
@@ -20063,8 +20525,8 @@ ClusterTileLayer.defaultProps = defaultProps18;
20063
20525
  var cluster_tile_layer_default = ClusterTileLayer;
20064
20526
 
20065
20527
  // dist/layers/spatial-index-tile-layer.js
20066
- var import_core55 = require("@loaders.gl/core");
20067
- (0, import_core55.registerLoaders)([carto_spatial_tile_loader_default]);
20528
+ var import_core54 = require("@loaders.gl/core");
20529
+ (0, import_core54.registerLoaders)([carto_spatial_tile_loader_default]);
20068
20530
  function isFeatureIdDefined2(value) {
20069
20531
  return value !== void 0 && value !== null && value !== "";
20070
20532
  }
@@ -21079,7 +21541,7 @@ RasterLayer.defaultProps = defaultProps25;
21079
21541
  var raster_layer_default = RasterLayer;
21080
21542
 
21081
21543
  // dist/layers/raster-tile-layer.js
21082
- var import_core63 = require("@loaders.gl/core");
21544
+ var import_core62 = require("@loaders.gl/core");
21083
21545
 
21084
21546
  // dist/layers/schema/carto-raster-tile.js
21085
21547
  var ARRAY_TYPES = {
@@ -21127,7 +21589,7 @@ var TileReader3 = class _TileReader {
21127
21589
  };
21128
21590
 
21129
21591
  // dist/layers/schema/carto-raster-tile-loader.js
21130
- var VERSION2 = true ? "9.4.0-beta.1" : "latest";
21592
+ var VERSION2 = true ? "9.4.0-beta.3" : "latest";
21131
21593
  var id2 = "cartoRasterTile";
21132
21594
  var DEFAULT_OPTIONS2 = {
21133
21595
  cartoRasterTile: {
@@ -21166,7 +21628,7 @@ function parseCartoRasterTile(arrayBuffer, options) {
21166
21628
  var carto_raster_tile_loader_default = CartoRasterTileLoader;
21167
21629
 
21168
21630
  // dist/layers/raster-tile-layer.js
21169
- (0, import_core63.registerLoaders)([carto_raster_tile_loader_default]);
21631
+ (0, import_core62.registerLoaders)([carto_raster_tile_loader_default]);
21170
21632
  var renderSubLayers3 = (props) => {
21171
21633
  var _a, _b;
21172
21634
  const tileIndex = (_b = (_a = props.tile) == null ? void 0 : _a.index) == null ? void 0 : _b.q;
@@ -21222,7 +21684,7 @@ RasterTileLayer.defaultProps = defaultProps26;
21222
21684
  var raster_tile_layer_default = RasterTileLayer;
21223
21685
 
21224
21686
  // dist/layers/vector-tile-layer.js
21225
- var import_core64 = require("@loaders.gl/core");
21687
+ var import_core63 = require("@loaders.gl/core");
21226
21688
 
21227
21689
  // dist/layers/schema/carto-properties-tile.js
21228
21690
  var TileReader4 = class _TileReader {
@@ -21240,7 +21702,7 @@ var TileReader4 = class _TileReader {
21240
21702
  };
21241
21703
 
21242
21704
  // dist/layers/schema/carto-properties-tile-loader.js
21243
- var VERSION3 = true ? "9.4.0-beta.1" : "latest";
21705
+ var VERSION3 = true ? "9.4.0-beta.3" : "latest";
21244
21706
  var id3 = "cartoPropertiesTile";
21245
21707
  var DEFAULT_OPTIONS3 = {
21246
21708
  cartoPropertiesTile: {
@@ -21269,7 +21731,7 @@ var carto_properties_tile_loader_default = CartoPropertiesTileLoader;
21269
21731
 
21270
21732
  // dist/layers/schema/carto-vector-tile-loader.js
21271
21733
  var import_earcut2 = __toESM(require("earcut"), 1);
21272
- var VERSION4 = true ? "9.4.0-beta.1" : "latest";
21734
+ var VERSION4 = true ? "9.4.0-beta.3" : "latest";
21273
21735
  var id4 = "cartoVectorTile";
21274
21736
  var DEFAULT_OPTIONS4 = {
21275
21737
  cartoVectorTile: {
@@ -21569,7 +22031,7 @@ function getLineMidpoint(lines, index) {
21569
22031
  }
21570
22032
 
21571
22033
  // dist/layers/vector-tile-layer.js
21572
- (0, import_core64.registerLoaders)([carto_properties_tile_loader_default, carto_vector_tile_loader_default]);
22034
+ (0, import_core63.registerLoaders)([carto_properties_tile_loader_default, carto_vector_tile_loader_default]);
21573
22035
  var MVT_BBOX = { west: 0, east: 1, south: 0, north: 1 };
21574
22036
  var defaultProps27 = {
21575
22037
  ...MVTLayer.defaultProps,