maplibre-gl-layers 0.5.0 → 0.10.0

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.mjs CHANGED
@@ -1,11 +1,14 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
1
4
  /*!
2
5
  * name: maplibre-gl-layers
3
- * version: 0.5.0
6
+ * version: 0.10.0
4
7
  * description: MapLibre's layer extension library enabling the display, movement, and modification of large numbers of dynamic sprite images
5
8
  * author: Kouji Matsui (@kekyo@mi.kekyo.net)
6
9
  * license: MIT
7
10
  * repository.url: https://github.com/kekyo/maplibre-gl-layers.git
8
- * git.commit.hash: ce37eea48b788c36b4bf98cdfce83b95a85833ee
11
+ * git.commit.hash: 58b99588e56fc80c6874d78a17e91a50901abc17
9
12
  */
10
13
  const UNLIMITED_SPRITE_SCALING_OPTIONS = {
11
14
  metersPerPixel: 1,
@@ -25,6 +28,282 @@ const STANDARD_SPRITE_SCALING_OPTIONS = {
25
28
  spriteMinPixel: 24,
26
29
  spriteMaxPixel: 100
27
30
  };
31
+ const DEFAULT_TEXTURE_FILTERING_OPTIONS = {
32
+ minFilter: "linear",
33
+ magFilter: "linear",
34
+ generateMipmaps: false,
35
+ maxAnisotropy: 1
36
+ };
37
+ const BETTER_TEXTURE_FILTERING_OPTIONS = {
38
+ minFilter: "linear-mipmap-linear",
39
+ magFilter: "linear",
40
+ generateMipmaps: true,
41
+ maxAnisotropy: 8
42
+ };
43
+ class SvgSizeResolutionError extends Error {
44
+ constructor(message, code) {
45
+ super(message);
46
+ __publicField(this, "code");
47
+ this.name = "SvgSizeResolutionError";
48
+ this.code = code;
49
+ }
50
+ }
51
+ const parseNumericLength = (value) => {
52
+ if (!value) {
53
+ return void 0;
54
+ }
55
+ const trimmed = value.trim();
56
+ if (trimmed.length === 0) {
57
+ return void 0;
58
+ }
59
+ const parsed = Number.parseFloat(trimmed);
60
+ if (!Number.isFinite(parsed) || parsed <= 0) {
61
+ return void 0;
62
+ }
63
+ return parsed;
64
+ };
65
+ const extractStyleLength = (styleValue, property) => {
66
+ if (!styleValue) {
67
+ return void 0;
68
+ }
69
+ const declarations = styleValue.split(";").map((decl) => decl.trim()).filter((decl) => decl.length > 0);
70
+ for (const declaration of declarations) {
71
+ const [prop, rawValue] = declaration.split(":");
72
+ if (!prop || !rawValue) {
73
+ continue;
74
+ }
75
+ if (prop.trim().toLowerCase() === property) {
76
+ return parseNumericLength(rawValue);
77
+ }
78
+ }
79
+ return void 0;
80
+ };
81
+ const parseSvgSize = (svgText) => {
82
+ try {
83
+ const doc = new DOMParser().parseFromString(svgText, "image/svg+xml");
84
+ const svg = doc.documentElement;
85
+ if (!svg || svg.tagName.toLowerCase() !== "svg") {
86
+ return { hasViewBox: false };
87
+ }
88
+ const attrWidth = parseNumericLength(svg.getAttribute("width"));
89
+ const attrHeight = parseNumericLength(svg.getAttribute("height"));
90
+ const styleWidth = extractStyleLength(svg.getAttribute("style"), "width");
91
+ const styleHeight = extractStyleLength(svg.getAttribute("style"), "height");
92
+ const width = attrWidth != null ? attrWidth : styleWidth;
93
+ const height = attrHeight != null ? attrHeight : styleHeight;
94
+ let viewBoxWidth;
95
+ let viewBoxHeight;
96
+ let hasViewBox = false;
97
+ const viewBox = svg.getAttribute("viewBox");
98
+ if (viewBox) {
99
+ const parts = viewBox.split(/[\s,]+/).map((part) => Number.parseFloat(part)).filter((part) => Number.isFinite(part));
100
+ if (parts.length === 4) {
101
+ viewBoxWidth = parts[2];
102
+ viewBoxHeight = parts[3];
103
+ if (viewBoxWidth > 0 && viewBoxHeight > 0) {
104
+ hasViewBox = true;
105
+ } else {
106
+ viewBoxWidth = void 0;
107
+ viewBoxHeight = void 0;
108
+ }
109
+ }
110
+ }
111
+ return {
112
+ width: width !== void 0 && width > 0 ? width : void 0,
113
+ height: height !== void 0 && height > 0 ? height : void 0,
114
+ viewBoxWidth,
115
+ viewBoxHeight,
116
+ hasViewBox
117
+ };
118
+ } catch (e) {
119
+ return { hasViewBox: false };
120
+ }
121
+ };
122
+ const determineSvgRasterDimensions = (parsed, options) => {
123
+ var _a, _b;
124
+ const overrideWidth = options == null ? void 0 : options.width;
125
+ const overrideHeight = options == null ? void 0 : options.height;
126
+ if (overrideWidth !== void 0 && overrideHeight !== void 0 && overrideWidth > 0 && overrideHeight > 0) {
127
+ return {
128
+ width: Math.max(1, Math.round(overrideWidth)),
129
+ height: Math.max(1, Math.round(overrideHeight))
130
+ };
131
+ }
132
+ const intrinsicWidth = parsed == null ? void 0 : parsed.width;
133
+ const intrinsicHeight = parsed == null ? void 0 : parsed.height;
134
+ const hasValidViewBox = Boolean(
135
+ (parsed == null ? void 0 : parsed.hasViewBox) && parsed.viewBoxWidth !== void 0 && parsed.viewBoxHeight !== void 0 && parsed.viewBoxWidth > 0 && parsed.viewBoxHeight > 0
136
+ );
137
+ const viewBoxAspect = hasValidViewBox ? parsed.viewBoxWidth / parsed.viewBoxHeight : void 0;
138
+ let baseWidth;
139
+ let baseHeight;
140
+ let aspect = intrinsicWidth !== void 0 && intrinsicHeight !== void 0 && intrinsicHeight > 0 ? intrinsicWidth / intrinsicHeight : viewBoxAspect;
141
+ if (intrinsicWidth !== void 0 && intrinsicWidth > 0 && intrinsicHeight !== void 0 && intrinsicHeight > 0) {
142
+ baseWidth = intrinsicWidth;
143
+ baseHeight = intrinsicHeight;
144
+ } else if (intrinsicWidth !== void 0 && intrinsicWidth > 0 && aspect !== void 0) {
145
+ baseWidth = intrinsicWidth;
146
+ baseHeight = intrinsicWidth / aspect;
147
+ } else if (intrinsicHeight !== void 0 && intrinsicHeight > 0 && aspect !== void 0) {
148
+ baseHeight = intrinsicHeight;
149
+ baseWidth = intrinsicHeight * aspect;
150
+ } else if (hasValidViewBox && ((_a = options == null ? void 0 : options.svg) == null ? void 0 : _a.useViewBoxDimensions)) {
151
+ baseWidth = parsed.viewBoxWidth;
152
+ baseHeight = parsed.viewBoxHeight;
153
+ aspect = baseWidth / baseHeight;
154
+ }
155
+ if ((baseWidth === void 0 || baseHeight === void 0) && hasValidViewBox && !((_b = options == null ? void 0 : options.svg) == null ? void 0 : _b.useViewBoxDimensions)) {
156
+ throw new SvgSizeResolutionError(
157
+ "SVG width/height attributes are missing and useViewBoxDimensions option is disabled.",
158
+ "viewbox-disabled"
159
+ );
160
+ }
161
+ if (baseWidth === void 0 || baseHeight === void 0) {
162
+ throw new SvgSizeResolutionError(
163
+ "SVG image lacks sufficient sizing information.",
164
+ "size-missing"
165
+ );
166
+ }
167
+ aspect = aspect != null ? aspect : baseWidth / baseHeight;
168
+ let finalWidth = baseWidth;
169
+ let finalHeight = baseHeight;
170
+ if (overrideWidth !== void 0 && overrideWidth > 0) {
171
+ finalWidth = overrideWidth;
172
+ if (overrideHeight === void 0) {
173
+ if (aspect === void 0) {
174
+ throw new SvgSizeResolutionError(
175
+ "Unable to infer SVG height from width; aspect ratio is undefined.",
176
+ "invalid-dimensions"
177
+ );
178
+ }
179
+ finalHeight = finalWidth / aspect;
180
+ }
181
+ }
182
+ if (overrideHeight !== void 0 && overrideHeight > 0) {
183
+ finalHeight = overrideHeight;
184
+ if (overrideWidth === void 0) {
185
+ if (aspect === void 0) {
186
+ throw new SvgSizeResolutionError(
187
+ "Unable to infer SVG width from height; aspect ratio is undefined.",
188
+ "invalid-dimensions"
189
+ );
190
+ }
191
+ finalWidth = finalHeight * aspect;
192
+ }
193
+ }
194
+ if (!Number.isFinite(finalWidth) || !Number.isFinite(finalHeight) || finalWidth <= 0 || finalHeight <= 0) {
195
+ throw new SvgSizeResolutionError(
196
+ "Resolved SVG dimensions are invalid.",
197
+ "invalid-dimensions"
198
+ );
199
+ }
200
+ return {
201
+ width: Math.max(1, Math.round(finalWidth)),
202
+ height: Math.max(1, Math.round(finalHeight))
203
+ };
204
+ };
205
+ const isSvgMimeType = (mimeType) => {
206
+ if (!mimeType) {
207
+ return false;
208
+ }
209
+ return mimeType.toLowerCase().includes("image/svg");
210
+ };
211
+ const rasterizeSvgWithCanvas = async (blob, width, height, options) => {
212
+ if (typeof document === "undefined") {
213
+ throw new Error(
214
+ "SVG rasterization fallback requires a browser environment"
215
+ );
216
+ }
217
+ const blobUrl = URL.createObjectURL(blob);
218
+ try {
219
+ const image = await new Promise((resolve, reject) => {
220
+ const element = new Image();
221
+ element.onload = () => resolve(element);
222
+ element.onerror = () => reject(new Error("Failed to load SVG for rasterization"));
223
+ element.src = blobUrl;
224
+ });
225
+ const canvas = document.createElement("canvas");
226
+ canvas.width = width;
227
+ canvas.height = height;
228
+ const ctx = canvas.getContext("2d");
229
+ if (!ctx) {
230
+ throw new Error("Failed to acquire 2D context for SVG rasterization");
231
+ }
232
+ ctx.clearRect(0, 0, width, height);
233
+ ctx.imageSmoothingEnabled = true;
234
+ if ((options == null ? void 0 : options.resizeQuality) === "pixelated") {
235
+ ctx.imageSmoothingEnabled = false;
236
+ } else if (options == null ? void 0 : options.resizeQuality) {
237
+ ctx.imageSmoothingQuality = options.resizeQuality;
238
+ }
239
+ ctx.drawImage(image, 0, 0, width, height);
240
+ try {
241
+ return await createImageBitmap(canvas);
242
+ } catch (e) {
243
+ const canvasBlob = await new Promise((resolve, reject) => {
244
+ canvas.toBlob((result) => {
245
+ if (result) {
246
+ resolve(result);
247
+ } else {
248
+ reject(
249
+ new Error("Failed to convert canvas to blob during rasterization")
250
+ );
251
+ }
252
+ });
253
+ });
254
+ return await createImageBitmap(canvasBlob);
255
+ }
256
+ } finally {
257
+ URL.revokeObjectURL(blobUrl);
258
+ }
259
+ };
260
+ const resolveSvgBitmapWithFallback = async (blob, width, height, options) => {
261
+ const bitmapOptions = {
262
+ resizeWidth: width,
263
+ resizeHeight: height
264
+ };
265
+ if (options == null ? void 0 : options.resizeQuality) {
266
+ bitmapOptions.resizeQuality = options.resizeQuality;
267
+ }
268
+ try {
269
+ return await createImageBitmap(blob, bitmapOptions);
270
+ } catch (error) {
271
+ return await rasterizeSvgWithCanvas(blob, width, height, options);
272
+ }
273
+ };
274
+ const internalReadImageBitmap = async (blob, shouldTreatAsSvg, options) => {
275
+ const svgOptions = options == null ? void 0 : options.svg;
276
+ if (shouldTreatAsSvg) {
277
+ let parsed = null;
278
+ if ((svgOptions == null ? void 0 : svgOptions.inspectSize) !== false) {
279
+ const text = await blob.text();
280
+ parsed = parseSvgSize(text);
281
+ }
282
+ const { width, height } = determineSvgRasterDimensions(parsed, options);
283
+ return await resolveSvgBitmapWithFallback(blob, width, height, options);
284
+ }
285
+ return await createImageBitmap(blob, {
286
+ resizeWidth: options == null ? void 0 : options.width,
287
+ resizeHeight: options == null ? void 0 : options.height,
288
+ resizeQuality: options == null ? void 0 : options.resizeQuality
289
+ });
290
+ };
291
+ const readImageBitmap = (blob, options) => {
292
+ const svgOptions = options == null ? void 0 : options.svg;
293
+ const shouldTreatAsSvg = (svgOptions == null ? void 0 : svgOptions.assumeSvg) === true;
294
+ return internalReadImageBitmap(blob, shouldTreatAsSvg, options);
295
+ };
296
+ const loadImageBitmap = async (url, options) => {
297
+ var _a;
298
+ const response = await fetch(url);
299
+ if (!response.ok) {
300
+ throw new Error(`Failed to fetch image from ${url}`);
301
+ }
302
+ const mimeType = response.headers.get("content-type");
303
+ const blob = await response.blob();
304
+ const shouldTreatAsSvg = ((_a = options == null ? void 0 : options.svg) == null ? void 0 : _a.assumeSvg) === true || isSvgMimeType(mimeType);
305
+ return await internalReadImageBitmap(blob, shouldTreatAsSvg, options);
306
+ };
28
307
  var maplibreGl$1 = { exports: {} };
29
308
  /**
30
309
  * MapLibre GL JS
@@ -21099,14 +21378,6 @@ function transformMat4(out, a, m) {
21099
21378
  return a;
21100
21379
  };
21101
21380
  })();
21102
- const loadImageBitmap = async (url) => {
21103
- const response = await fetch(url);
21104
- if (!response.ok) {
21105
- throw new Error(`Failed to fetch image from ${url}`);
21106
- }
21107
- const blob = await response.blob();
21108
- return await createImageBitmap(blob);
21109
- };
21110
21381
  const cloneSpriteLocation = (location2) => {
21111
21382
  if (location2.z === void 0) {
21112
21383
  return { lng: location2.lng, lat: location2.lat };
@@ -21163,7 +21434,7 @@ const computeFeedforwardTarget = (previous, next) => {
21163
21434
  }
21164
21435
  return target;
21165
21436
  };
21166
- const normaliseOptions$1 = (options) => {
21437
+ const normalizeOptions$2 = (options) => {
21167
21438
  var _a;
21168
21439
  return {
21169
21440
  durationMs: Math.max(0, options.durationMs),
@@ -21173,7 +21444,7 @@ const normaliseOptions$1 = (options) => {
21173
21444
  };
21174
21445
  const createInterpolationState = (params) => {
21175
21446
  const { currentLocation, lastCommandLocation, nextCommandLocation } = params;
21176
- const options = normaliseOptions$1(params.options);
21447
+ const options = normalizeOptions$2(params.options);
21177
21448
  const from = cloneSpriteLocation(currentLocation);
21178
21449
  const easing = resolveEasing(options.easing);
21179
21450
  let to;
@@ -21218,8 +21489,8 @@ const evaluateInterpolation = (params) => {
21218
21489
  };
21219
21490
  };
21220
21491
  const NUMERIC_EPSILON = 1e-6;
21221
- const normaliseDuration = (durationMs) => Number.isFinite(durationMs) && durationMs > 0 ? durationMs : 0;
21222
- const normaliseDelta = (delta) => {
21492
+ const normalizeDuration$1 = (durationMs) => Number.isFinite(durationMs) && durationMs > 0 ? durationMs : 0;
21493
+ const normalizeDelta = (delta) => {
21223
21494
  if (!Number.isFinite(delta)) {
21224
21495
  return 0;
21225
21496
  }
@@ -21231,16 +21502,24 @@ const normaliseDelta = (delta) => {
21231
21502
  }
21232
21503
  return adjusted;
21233
21504
  };
21234
- const normaliseOptions = (options) => {
21505
+ const normalizeOptions$1 = (options) => {
21506
+ var _a;
21235
21507
  return {
21236
- durationMs: normaliseDuration(options.durationMs),
21237
- easing: resolveEasing(options.easing)
21508
+ durationMs: normalizeDuration$1(options.durationMs),
21509
+ easing: resolveEasing(options.easing),
21510
+ mode: (_a = options.mode) != null ? _a : "feedback"
21238
21511
  };
21239
21512
  };
21240
- const createNumericInterpolationState = (params) => {
21513
+ const createDegreeInterpolationState = (params) => {
21241
21514
  const { currentValue, targetValue } = params;
21242
- const options = normaliseOptions(params.options);
21243
- const delta = normaliseDelta(targetValue - currentValue);
21515
+ const options = normalizeOptions$1(params.options);
21516
+ let effectiveTarget = targetValue;
21517
+ const previousCommand = params.previousCommandValue;
21518
+ if (options.mode === "feedforward" && previousCommand !== void 0 && Number.isFinite(previousCommand)) {
21519
+ const commandDelta = normalizeDelta(targetValue - previousCommand);
21520
+ effectiveTarget = targetValue + commandDelta;
21521
+ }
21522
+ const delta = normalizeDelta(effectiveTarget - currentValue);
21244
21523
  const pathTarget = currentValue + delta;
21245
21524
  const requiresInterpolation = options.durationMs > 0 && Math.abs(delta) > NUMERIC_EPSILON;
21246
21525
  const state = {
@@ -21248,7 +21527,7 @@ const createNumericInterpolationState = (params) => {
21248
21527
  easing: options.easing,
21249
21528
  from: currentValue,
21250
21529
  to: pathTarget,
21251
- finalValue: targetValue,
21530
+ finalValue: effectiveTarget,
21252
21531
  startTimestamp: -1
21253
21532
  };
21254
21533
  return {
@@ -21256,7 +21535,7 @@ const createNumericInterpolationState = (params) => {
21256
21535
  requiresInterpolation
21257
21536
  };
21258
21537
  };
21259
- const clamp01 = (value) => {
21538
+ const clamp01$1 = (value) => {
21260
21539
  if (!Number.isFinite(value)) {
21261
21540
  return 1;
21262
21541
  }
@@ -21268,7 +21547,7 @@ const clamp01 = (value) => {
21268
21547
  }
21269
21548
  return value;
21270
21549
  };
21271
- const evaluateNumericInterpolation = (params) => {
21550
+ const evaluateDegreeInterpolation = (params) => {
21272
21551
  const { state } = params;
21273
21552
  const timestamp = Number.isFinite(params.timestamp) ? params.timestamp : Date.now();
21274
21553
  const duration = Math.max(0, state.durationMs);
@@ -21282,7 +21561,7 @@ const evaluateNumericInterpolation = (params) => {
21282
21561
  }
21283
21562
  const elapsed = timestamp - effectiveStart;
21284
21563
  const rawProgress = duration <= 0 ? 1 : elapsed / duration;
21285
- const eased = clamp01(state.easing(rawProgress));
21564
+ const eased = clamp01$1(state.easing(rawProgress));
21286
21565
  const interpolated = state.from + (state.to - state.from) * eased;
21287
21566
  const completed = rawProgress >= 1;
21288
21567
  return {
@@ -21291,7 +21570,7 @@ const evaluateNumericInterpolation = (params) => {
21291
21570
  effectiveStartTimestamp: effectiveStart
21292
21571
  };
21293
21572
  };
21294
- const normaliseAngleDeg = (angle) => {
21573
+ const normalizeAngleDeg = (angle) => {
21295
21574
  if (!Number.isFinite(angle)) {
21296
21575
  return 0;
21297
21576
  }
@@ -21301,17 +21580,19 @@ const normaliseAngleDeg = (angle) => {
21301
21580
  };
21302
21581
  const resolveRotationTarget = (params) => {
21303
21582
  const options = params.options;
21304
- const targetAngle = normaliseAngleDeg(params.targetAngleDeg);
21305
- const currentAngle = normaliseAngleDeg(params.currentAngleDeg);
21583
+ const targetAngle = normalizeAngleDeg(params.targetAngleDeg);
21584
+ const currentAngle = normalizeAngleDeg(params.currentAngleDeg);
21585
+ const previousCommandAngleDeg = params.previousCommandAngleDeg !== void 0 ? normalizeAngleDeg(params.previousCommandAngleDeg) : void 0;
21306
21586
  if (!options || options.durationMs <= 0) {
21307
21587
  return {
21308
21588
  nextAngleDeg: targetAngle,
21309
21589
  interpolationState: null
21310
21590
  };
21311
21591
  }
21312
- const { state, requiresInterpolation } = createNumericInterpolationState({
21592
+ const { state, requiresInterpolation } = createDegreeInterpolationState({
21313
21593
  currentValue: currentAngle,
21314
21594
  targetValue: targetAngle,
21595
+ previousCommandValue: previousCommandAngleDeg,
21315
21596
  options
21316
21597
  });
21317
21598
  if (!requiresInterpolation) {
@@ -21991,6 +22272,243 @@ const calculateSurfaceCornerDisplacements = (params) => {
21991
22272
  }
21992
22273
  return corners;
21993
22274
  };
22275
+ const DISTANCE_EPSILON = 1e-6;
22276
+ const normalizeDuration = (durationMs) => Number.isFinite(durationMs) && durationMs > 0 ? durationMs : 0;
22277
+ const normalizeOptions = (options) => {
22278
+ var _a;
22279
+ return {
22280
+ durationMs: normalizeDuration(options.durationMs),
22281
+ easing: resolveEasing(options.easing),
22282
+ mode: (_a = options.mode) != null ? _a : "feedback"
22283
+ };
22284
+ };
22285
+ const createDistanceInterpolationState = (params) => {
22286
+ const { currentValue, targetValue } = params;
22287
+ const options = normalizeOptions(params.options);
22288
+ let effectiveTarget = targetValue;
22289
+ const previousCommand = params.previousCommandValue;
22290
+ if (options.mode === "feedforward" && previousCommand !== void 0 && Number.isFinite(previousCommand)) {
22291
+ const commandDelta = targetValue - previousCommand;
22292
+ effectiveTarget = targetValue + commandDelta;
22293
+ }
22294
+ const delta = effectiveTarget - currentValue;
22295
+ const requiresInterpolation = options.durationMs > 0 && Math.abs(delta) > DISTANCE_EPSILON;
22296
+ const state = {
22297
+ durationMs: options.durationMs,
22298
+ easing: options.easing,
22299
+ from: currentValue,
22300
+ to: currentValue + delta,
22301
+ finalValue: effectiveTarget,
22302
+ startTimestamp: -1
22303
+ };
22304
+ return {
22305
+ state,
22306
+ requiresInterpolation
22307
+ };
22308
+ };
22309
+ const clamp01 = (value) => {
22310
+ if (!Number.isFinite(value)) {
22311
+ return 1;
22312
+ }
22313
+ if (value <= 0) {
22314
+ return 0;
22315
+ }
22316
+ if (value >= 1) {
22317
+ return 1;
22318
+ }
22319
+ return value;
22320
+ };
22321
+ const evaluateDistanceInterpolation = (params) => {
22322
+ const { state } = params;
22323
+ const timestamp = Number.isFinite(params.timestamp) ? params.timestamp : Date.now();
22324
+ const duration = Math.max(0, state.durationMs);
22325
+ const effectiveStart = state.startTimestamp >= 0 ? state.startTimestamp : timestamp;
22326
+ if (duration === 0 || Math.abs(state.to - state.from) <= DISTANCE_EPSILON) {
22327
+ return {
22328
+ value: state.finalValue,
22329
+ completed: true,
22330
+ effectiveStartTimestamp: effectiveStart
22331
+ };
22332
+ }
22333
+ const elapsed = timestamp - effectiveStart;
22334
+ const rawProgress = duration <= 0 ? 1 : elapsed / duration;
22335
+ const eased = clamp01(state.easing(rawProgress));
22336
+ const interpolated = state.from + (state.to - state.from) * eased;
22337
+ const completed = rawProgress >= 1;
22338
+ return {
22339
+ value: completed ? state.finalValue : interpolated,
22340
+ completed,
22341
+ effectiveStartTimestamp: effectiveStart
22342
+ };
22343
+ };
22344
+ const stepDegreeInterpolationState = (interpolationState, timestamp, applyValue, options) => {
22345
+ var _a, _b;
22346
+ if (!interpolationState) {
22347
+ return { state: null, active: false };
22348
+ }
22349
+ const evaluation = evaluateDegreeInterpolation({
22350
+ state: interpolationState,
22351
+ timestamp
22352
+ });
22353
+ if (interpolationState.startTimestamp < 0) {
22354
+ interpolationState.startTimestamp = evaluation.effectiveStartTimestamp;
22355
+ }
22356
+ const normalizeValue = (_a = options == null ? void 0 : options.normalize) != null ? _a : ((value) => value);
22357
+ const applyFinalValue = (_b = options == null ? void 0 : options.applyFinalValue) != null ? _b : applyValue;
22358
+ const interpolatedValue = normalizeValue(evaluation.value);
22359
+ applyValue(interpolatedValue);
22360
+ if (evaluation.completed) {
22361
+ const finalValue = normalizeValue(interpolationState.finalValue);
22362
+ applyFinalValue(finalValue);
22363
+ return { state: null, active: false };
22364
+ }
22365
+ return { state: interpolationState, active: true };
22366
+ };
22367
+ const updateImageDisplayedRotation = (image, optionsOverride) => {
22368
+ const targetAngle = normalizeAngleDeg(
22369
+ image.resolvedBaseRotateDeg + image.rotateDeg
22370
+ );
22371
+ const currentAngle = Number.isFinite(image.displayedRotateDeg) ? image.displayedRotateDeg : targetAngle;
22372
+ const previousCommandAngle = image.lastCommandRotateDeg;
22373
+ const options = optionsOverride === void 0 ? image.rotationInterpolationOptions : optionsOverride;
22374
+ const { nextAngleDeg, interpolationState } = resolveRotationTarget({
22375
+ currentAngleDeg: currentAngle,
22376
+ targetAngleDeg: targetAngle,
22377
+ previousCommandAngleDeg: previousCommandAngle,
22378
+ options: options != null ? options : void 0
22379
+ });
22380
+ image.displayedRotateDeg = nextAngleDeg;
22381
+ image.rotationInterpolationState = interpolationState;
22382
+ if (!interpolationState) {
22383
+ image.displayedRotateDeg = targetAngle;
22384
+ }
22385
+ image.lastCommandRotateDeg = targetAngle;
22386
+ };
22387
+ const syncImageRotationChannel = (image, optionsOverride) => {
22388
+ updateImageDisplayedRotation(image, optionsOverride);
22389
+ };
22390
+ const stepRotationInterpolation = (image, timestamp) => {
22391
+ const { state, active } = stepDegreeInterpolationState(
22392
+ image.rotationInterpolationState,
22393
+ timestamp,
22394
+ (value) => {
22395
+ image.displayedRotateDeg = value;
22396
+ },
22397
+ {
22398
+ normalize: normalizeAngleDeg
22399
+ }
22400
+ );
22401
+ image.rotationInterpolationState = state;
22402
+ return active;
22403
+ };
22404
+ const stepOffsetDegInterpolation = (image, timestamp) => {
22405
+ const { state, active } = stepDegreeInterpolationState(
22406
+ image.offsetDegInterpolationState,
22407
+ timestamp,
22408
+ (value) => {
22409
+ image.offset.offsetDeg = value;
22410
+ }
22411
+ );
22412
+ image.offsetDegInterpolationState = state;
22413
+ return active;
22414
+ };
22415
+ const clearOffsetDegInterpolation = (image) => {
22416
+ image.offsetDegInterpolationState = null;
22417
+ };
22418
+ const stepDistanceInterpolationState = (interpolationState, timestamp, applyValue) => {
22419
+ if (!interpolationState) {
22420
+ return { state: null, active: false };
22421
+ }
22422
+ const evaluation = evaluateDistanceInterpolation({
22423
+ state: interpolationState,
22424
+ timestamp
22425
+ });
22426
+ if (interpolationState.startTimestamp < 0) {
22427
+ interpolationState.startTimestamp = evaluation.effectiveStartTimestamp;
22428
+ }
22429
+ applyValue(evaluation.value);
22430
+ if (evaluation.completed) {
22431
+ applyValue(interpolationState.finalValue);
22432
+ return { state: null, active: false };
22433
+ }
22434
+ return { state: interpolationState, active: true };
22435
+ };
22436
+ const clearOffsetMetersInterpolation = (image) => {
22437
+ image.offsetMetersInterpolationState = null;
22438
+ };
22439
+ const applyOffsetDegUpdate = (image, nextOffset, interpolationOptions) => {
22440
+ const options = interpolationOptions;
22441
+ if (!options || options.durationMs <= 0) {
22442
+ image.offset.offsetDeg = nextOffset.offsetDeg;
22443
+ image.offsetDegInterpolationState = null;
22444
+ image.lastCommandOffsetDeg = nextOffset.offsetDeg;
22445
+ return;
22446
+ }
22447
+ const { state, requiresInterpolation } = createDegreeInterpolationState({
22448
+ currentValue: image.offset.offsetDeg,
22449
+ targetValue: nextOffset.offsetDeg,
22450
+ previousCommandValue: image.lastCommandOffsetDeg,
22451
+ options
22452
+ });
22453
+ image.lastCommandOffsetDeg = nextOffset.offsetDeg;
22454
+ if (requiresInterpolation) {
22455
+ image.offsetDegInterpolationState = state;
22456
+ } else {
22457
+ image.offset.offsetDeg = nextOffset.offsetDeg;
22458
+ image.offsetDegInterpolationState = null;
22459
+ }
22460
+ };
22461
+ const applyOffsetMetersUpdate = (image, nextOffset, interpolationOptions) => {
22462
+ const options = interpolationOptions;
22463
+ if (!options || options.durationMs <= 0) {
22464
+ image.offset.offsetMeters = nextOffset.offsetMeters;
22465
+ image.offsetMetersInterpolationState = null;
22466
+ image.lastCommandOffsetMeters = nextOffset.offsetMeters;
22467
+ return;
22468
+ }
22469
+ const { state, requiresInterpolation } = createDistanceInterpolationState({
22470
+ currentValue: image.offset.offsetMeters,
22471
+ targetValue: nextOffset.offsetMeters,
22472
+ previousCommandValue: image.lastCommandOffsetMeters,
22473
+ options
22474
+ });
22475
+ image.lastCommandOffsetMeters = nextOffset.offsetMeters;
22476
+ if (requiresInterpolation) {
22477
+ image.offsetMetersInterpolationState = state;
22478
+ } else {
22479
+ image.offset.offsetMeters = nextOffset.offsetMeters;
22480
+ image.offsetMetersInterpolationState = null;
22481
+ }
22482
+ };
22483
+ const stepOffsetMetersInterpolation = (image, timestamp) => {
22484
+ const { state, active } = stepDistanceInterpolationState(
22485
+ image.offsetMetersInterpolationState,
22486
+ timestamp,
22487
+ (value) => {
22488
+ image.offset.offsetMeters = value;
22489
+ }
22490
+ );
22491
+ image.offsetMetersInterpolationState = state;
22492
+ return active;
22493
+ };
22494
+ const IMAGE_INTERPOLATION_STEPPERS = [
22495
+ stepRotationInterpolation,
22496
+ stepOffsetDegInterpolation,
22497
+ stepOffsetMetersInterpolation
22498
+ ];
22499
+ const stepSpriteImageInterpolations = (image, timestamp) => {
22500
+ let active = false;
22501
+ for (const stepper of IMAGE_INTERPOLATION_STEPPERS) {
22502
+ if (stepper(image, timestamp)) {
22503
+ active = true;
22504
+ }
22505
+ }
22506
+ return active;
22507
+ };
22508
+ const applyOffsetUpdate = (image, nextOffset, options = {}) => {
22509
+ applyOffsetDegUpdate(image, nextOffset, options.deg);
22510
+ applyOffsetMetersUpdate(image, nextOffset, options.meters);
22511
+ };
21994
22512
  const DEFAULT_ANCHOR = { x: 0, y: 0 };
21995
22513
  const DEFAULT_AUTO_ROTATION_MIN_DISTANCE_METERS = 20;
21996
22514
  const DEFAULT_IMAGE_OFFSET = {
@@ -22002,6 +22520,91 @@ const MIN_CLIP_Z_EPSILON = 1e-7;
22002
22520
  const EPS_NDC = 1e-6;
22003
22521
  const ORDER_MAX = 16;
22004
22522
  const ORDER_BUCKET = 16;
22523
+ const MIN_FILTER_VALUES = [
22524
+ "nearest",
22525
+ "linear",
22526
+ "nearest-mipmap-nearest",
22527
+ "nearest-mipmap-linear",
22528
+ "linear-mipmap-nearest",
22529
+ "linear-mipmap-linear"
22530
+ ];
22531
+ const MAG_FILTER_VALUES = [
22532
+ "nearest",
22533
+ "linear"
22534
+ ];
22535
+ const MIPMAP_MIN_FILTERS = /* @__PURE__ */ new Set([
22536
+ "nearest-mipmap-nearest",
22537
+ "nearest-mipmap-linear",
22538
+ "linear-mipmap-nearest",
22539
+ "linear-mipmap-linear"
22540
+ ]);
22541
+ const filterRequiresMipmaps = (filter) => MIPMAP_MIN_FILTERS.has(filter);
22542
+ const resolveTextureFilteringOptions = (options) => {
22543
+ var _a, _b;
22544
+ const minCandidate = options == null ? void 0 : options.minFilter;
22545
+ const minFilter = MIN_FILTER_VALUES.includes(
22546
+ minCandidate
22547
+ ) ? minCandidate : DEFAULT_TEXTURE_FILTERING_OPTIONS.minFilter;
22548
+ const magCandidate = options == null ? void 0 : options.magFilter;
22549
+ const magFilter = MAG_FILTER_VALUES.includes(
22550
+ magCandidate
22551
+ ) ? magCandidate : DEFAULT_TEXTURE_FILTERING_OPTIONS.magFilter;
22552
+ let generateMipmaps = (_a = options == null ? void 0 : options.generateMipmaps) != null ? _a : DEFAULT_TEXTURE_FILTERING_OPTIONS.generateMipmaps;
22553
+ if (filterRequiresMipmaps(minFilter)) {
22554
+ generateMipmaps = true;
22555
+ }
22556
+ let maxAnisotropy = (_b = options == null ? void 0 : options.maxAnisotropy) != null ? _b : DEFAULT_TEXTURE_FILTERING_OPTIONS.maxAnisotropy;
22557
+ if (!Number.isFinite(maxAnisotropy) || maxAnisotropy < 1) {
22558
+ maxAnisotropy = 1;
22559
+ }
22560
+ return {
22561
+ minFilter,
22562
+ magFilter,
22563
+ generateMipmaps,
22564
+ maxAnisotropy
22565
+ };
22566
+ };
22567
+ const ANISOTROPY_EXTENSION_NAMES = [
22568
+ "EXT_texture_filter_anisotropic",
22569
+ "WEBKIT_EXT_texture_filter_anisotropic",
22570
+ "MOZ_EXT_texture_filter_anisotropic"
22571
+ ];
22572
+ const resolveAnisotropyExtension = (glContext) => {
22573
+ for (const name of ANISOTROPY_EXTENSION_NAMES) {
22574
+ const extension = glContext.getExtension(name);
22575
+ if (extension) {
22576
+ return extension;
22577
+ }
22578
+ }
22579
+ return null;
22580
+ };
22581
+ const isPowerOfTwo = (value) => value > 0 && (value & value - 1) === 0;
22582
+ const resolveGlMinFilter = (glContext, filter) => {
22583
+ switch (filter) {
22584
+ case "nearest":
22585
+ return glContext.NEAREST;
22586
+ case "nearest-mipmap-nearest":
22587
+ return glContext.NEAREST_MIPMAP_NEAREST;
22588
+ case "nearest-mipmap-linear":
22589
+ return glContext.NEAREST_MIPMAP_LINEAR;
22590
+ case "linear-mipmap-nearest":
22591
+ return glContext.LINEAR_MIPMAP_NEAREST;
22592
+ case "linear-mipmap-linear":
22593
+ return glContext.LINEAR_MIPMAP_LINEAR;
22594
+ case "linear":
22595
+ default:
22596
+ return glContext.LINEAR;
22597
+ }
22598
+ };
22599
+ const resolveGlMagFilter = (glContext, filter) => {
22600
+ switch (filter) {
22601
+ case "nearest":
22602
+ return glContext.NEAREST;
22603
+ case "linear":
22604
+ default:
22605
+ return glContext.LINEAR;
22606
+ }
22607
+ };
22005
22608
  const calculatePerspectiveRatio = (mapInstance, location2) => {
22006
22609
  var _a, _b, _c;
22007
22610
  const transform = mapInstance.transform;
@@ -22123,14 +22726,14 @@ const applyAutoRotation = (sprite, nextLocation) => {
22123
22726
  return false;
22124
22727
  }
22125
22728
  const resolvedAngleRaw = isFiniteNumber(bearingDeg) ? bearingDeg : sprite.lastAutoRotationAngleDeg;
22126
- const resolvedAngle = normaliseAngleDeg(resolvedAngleRaw);
22729
+ const resolvedAngle = normalizeAngleDeg(resolvedAngleRaw);
22127
22730
  sprite.images.forEach((orderMap) => {
22128
22731
  orderMap.forEach((image) => {
22129
22732
  if (!image.autoRotation) {
22130
22733
  return;
22131
22734
  }
22132
22735
  image.resolvedBaseRotateDeg = resolvedAngle;
22133
- updateImageDisplayedRotation(image);
22736
+ syncImageRotationChannel(image);
22134
22737
  });
22135
22738
  });
22136
22739
  sprite.lastAutoRotationLocation = cloneSpriteLocation(nextLocation);
@@ -22566,23 +23169,6 @@ const cloneOffset = (offset) => {
22566
23169
  offsetDeg: offset.offsetDeg
22567
23170
  };
22568
23171
  };
22569
- const updateImageDisplayedRotation = (image, optionsOverride) => {
22570
- const targetAngle = normaliseAngleDeg(
22571
- image.resolvedBaseRotateDeg + image.rotateDeg
22572
- );
22573
- const currentAngle = Number.isFinite(image.displayedRotateDeg) ? image.displayedRotateDeg : targetAngle;
22574
- const options = optionsOverride === void 0 ? image.rotationInterpolationOptions : optionsOverride;
22575
- const { nextAngleDeg, interpolationState } = resolveRotationTarget({
22576
- currentAngleDeg: currentAngle,
22577
- targetAngleDeg: targetAngle,
22578
- options: options != null ? options : void 0
22579
- });
22580
- image.displayedRotateDeg = nextAngleDeg;
22581
- image.rotationInterpolationState = interpolationState;
22582
- if (!interpolationState) {
22583
- image.displayedRotateDeg = targetAngle;
22584
- }
22585
- };
22586
23172
  const cloneInterpolationOptions = (options) => {
22587
23173
  return {
22588
23174
  mode: options.mode,
@@ -22590,46 +23176,49 @@ const cloneInterpolationOptions = (options) => {
22590
23176
  easing: options.easing
22591
23177
  };
22592
23178
  };
22593
- const cloneNumericInterpolationOptions = (options) => {
22594
- return {
22595
- durationMs: options.durationMs,
22596
- easing: options.easing
22597
- };
22598
- };
22599
23179
  const createImageStateFromInit = (imageInit, subLayer, order) => {
22600
23180
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
22601
23181
  const mode = (_a = imageInit.mode) != null ? _a : "surface";
22602
23182
  const autoRotationDefault = mode === "surface";
23183
+ const initialOffset = cloneOffset(imageInit.offset);
23184
+ const initialRotateDeg = normalizeAngleDeg((_b = imageInit.rotateDeg) != null ? _b : 0);
22603
23185
  const state = {
22604
23186
  subLayer,
22605
23187
  order,
22606
23188
  imageId: imageInit.imageId,
22607
23189
  mode,
22608
- opacity: (_b = imageInit.opacity) != null ? _b : 1,
22609
- scale: (_c = imageInit.scale) != null ? _c : 1,
23190
+ opacity: (_c = imageInit.opacity) != null ? _c : 1,
23191
+ scale: (_d = imageInit.scale) != null ? _d : 1,
22610
23192
  anchor: cloneAnchor(imageInit.anchor),
22611
- offset: cloneOffset(imageInit.offset),
22612
- rotateDeg: (_d = imageInit.rotateDeg) != null ? _d : 0,
22613
- displayedRotateDeg: normaliseAngleDeg((_e = imageInit.rotateDeg) != null ? _e : 0),
23193
+ offset: initialOffset,
23194
+ rotateDeg: (_e = imageInit.rotateDeg) != null ? _e : 0,
23195
+ displayedRotateDeg: initialRotateDeg,
22614
23196
  autoRotation: (_f = imageInit.autoRotation) != null ? _f : autoRotationDefault,
22615
23197
  autoRotationMinDistanceMeters: (_g = imageInit.autoRotationMinDistanceMeters) != null ? _g : DEFAULT_AUTO_ROTATION_MIN_DISTANCE_METERS,
22616
23198
  resolvedBaseRotateDeg: 0,
22617
23199
  originLocation: cloneOriginLocation(imageInit.originLocation),
22618
23200
  rotationInterpolationState: null,
22619
23201
  rotationInterpolationOptions: null,
22620
- offsetInterpolationState: null
23202
+ offsetDegInterpolationState: null,
23203
+ offsetMetersInterpolationState: null,
23204
+ lastCommandRotateDeg: initialRotateDeg,
23205
+ lastCommandOffsetDeg: initialOffset.offsetDeg,
23206
+ lastCommandOffsetMeters: initialOffset.offsetMeters
22621
23207
  };
22622
- const rotateInitOption = (_i = (_h = imageInit.rotationInterpolation) == null ? void 0 : _h.rotateDeg) != null ? _i : null;
23208
+ const rotateInitOption = (_i = (_h = imageInit.interpolation) == null ? void 0 : _h.rotateDeg) != null ? _i : null;
22623
23209
  if (rotateInitOption) {
22624
- state.rotationInterpolationOptions = cloneNumericInterpolationOptions(rotateInitOption);
23210
+ state.rotationInterpolationOptions = cloneInterpolationOptions(rotateInitOption);
22625
23211
  }
22626
- updateImageDisplayedRotation(state);
23212
+ syncImageRotationChannel(state);
22627
23213
  return state;
22628
23214
  };
22629
23215
  const createSpriteLayer = (options) => {
22630
23216
  var _a;
22631
23217
  const id = (_a = options == null ? void 0 : options.id) != null ? _a : "sprite-layer";
22632
23218
  const resolvedScaling = resolveScalingOptions(options == null ? void 0 : options.spriteScaling);
23219
+ const resolvedTextureFiltering = resolveTextureFilteringOptions(
23220
+ options == null ? void 0 : options.textureFiltering
23221
+ );
22633
23222
  let gl = null;
22634
23223
  let map = null;
22635
23224
  let program = null;
@@ -22638,6 +23227,8 @@ const createSpriteLayer = (options) => {
22638
23227
  let attribUvLocation = -1;
22639
23228
  let uniformTextureLocation = null;
22640
23229
  let uniformOpacityLocation = null;
23230
+ let anisotropyExtension = null;
23231
+ let maxSupportedAnisotropy = 1;
22641
23232
  const images = /* @__PURE__ */ new Map();
22642
23233
  const queuedTextureIds = /* @__PURE__ */ new Set();
22643
23234
  const queueTextureUpload = (image) => {
@@ -22768,7 +23359,7 @@ const createSpriteLayer = (options) => {
22768
23359
  baseY = refCenter.y;
22769
23360
  }
22770
23361
  }
22771
- const totalRotDeg = Number.isFinite(img.displayedRotateDeg) ? img.displayedRotateDeg : normaliseAngleDeg(
23362
+ const totalRotDeg = Number.isFinite(img.displayedRotateDeg) ? img.displayedRotateDeg : normalizeAngleDeg(
22772
23363
  ((_d = img.resolvedBaseRotateDeg) != null ? _d : 0) + ((_e = img.rotateDeg) != null ? _e : 0)
22773
23364
  );
22774
23365
  const imageScaleLocal = (_f = img.scale) != null ? _f : 1;
@@ -22918,13 +23509,15 @@ const createSpriteLayer = (options) => {
22918
23509
  };
22919
23510
  let canvasElement = null;
22920
23511
  const inputListenerDisposers = [];
22921
- const hasSpriteClickListeners = () => {
23512
+ const hasSpriteListeners = (type) => {
22922
23513
  var _a2, _b;
22923
23514
  return (
22924
23515
  // Treat missing listener sets as zero, otherwise check the registered count.
22925
- ((_b = (_a2 = eventListeners.get("spriteclick")) == null ? void 0 : _a2.size) != null ? _b : 0) > 0
23516
+ ((_b = (_a2 = eventListeners.get(type)) == null ? void 0 : _a2.size) != null ? _b : 0) > 0
22926
23517
  );
22927
23518
  };
23519
+ const hasSpriteClickListeners = () => hasSpriteListeners("spriteclick");
23520
+ const hasSpriteHoverListeners = () => hasSpriteListeners("spritehover");
22928
23521
  const resolveScreenPointFromEvent = (nativeEvent) => {
22929
23522
  var _a2, _b, _c, _d;
22930
23523
  if (!canvasElement) {
@@ -22949,20 +23542,31 @@ const createSpriteLayer = (options) => {
22949
23542
  const mouseLike = nativeEvent;
22950
23543
  return toScreenPoint(mouseLike.clientX, mouseLike.clientY);
22951
23544
  };
23545
+ const resolveSpriteEventPayload = (hitEntry) => {
23546
+ var _a2, _b;
23547
+ if (!hitEntry) {
23548
+ return {
23549
+ sprite: void 0,
23550
+ image: void 0
23551
+ };
23552
+ }
23553
+ const spriteState = getSpriteState(hitEntry.sprite.spriteId);
23554
+ const imageState = (_b = (_a2 = spriteState == null ? void 0 : spriteState.images.get(hitEntry.image.subLayer)) == null ? void 0 : _a2.get(hitEntry.image.order)) != null ? _b : void 0;
23555
+ return {
23556
+ sprite: spriteState,
23557
+ image: imageState
23558
+ };
23559
+ };
22952
23560
  const dispatchSpriteClick = (hitEntry, screenPoint, originalEvent) => {
22953
23561
  const listeners = eventListeners.get("spriteclick");
22954
23562
  if (!listeners || listeners.size === 0) {
22955
23563
  return;
22956
23564
  }
22957
- const spriteState = getSpriteState(hitEntry.sprite.spriteId);
22958
- if (!spriteState) {
22959
- return;
22960
- }
22961
- const imageState = hitEntry.image;
23565
+ const payload = resolveSpriteEventPayload(hitEntry);
22962
23566
  const clickEvent = {
22963
23567
  type: "spriteclick",
22964
- sprite: spriteState,
22965
- image: imageState,
23568
+ sprite: payload.sprite,
23569
+ image: payload.image,
22966
23570
  screenPoint,
22967
23571
  originalEvent
22968
23572
  };
@@ -22970,22 +23574,50 @@ const createSpriteLayer = (options) => {
22970
23574
  listener(clickEvent);
22971
23575
  });
22972
23576
  };
22973
- const processInteractionEvent = (nativeEvent) => {
23577
+ const dispatchSpriteHover = (hitEntry, screenPoint, originalEvent) => {
23578
+ const listeners = eventListeners.get("spritehover");
23579
+ if (!listeners || listeners.size === 0) {
23580
+ return;
23581
+ }
23582
+ const payload = resolveSpriteEventPayload(hitEntry);
23583
+ const hoverEvent = {
23584
+ type: "spritehover",
23585
+ sprite: payload.sprite,
23586
+ image: payload.image,
23587
+ screenPoint,
23588
+ originalEvent
23589
+ };
23590
+ listeners.forEach((listener) => {
23591
+ listener(hoverEvent);
23592
+ });
23593
+ };
23594
+ const resolveHitTestResult = (nativeEvent) => {
23595
+ const screenPoint = resolveScreenPointFromEvent(nativeEvent);
23596
+ if (!screenPoint) {
23597
+ return null;
23598
+ }
23599
+ const hitEntry = findTopmostHitEntry(screenPoint);
23600
+ return { hitEntry: hitEntry != null ? hitEntry : null, screenPoint };
23601
+ };
23602
+ const processClickEvent = (nativeEvent) => {
22974
23603
  if (!hasSpriteClickListeners()) {
22975
23604
  return;
22976
23605
  }
22977
- if (hitTestEntries.length === 0) {
23606
+ const hitResult = resolveHitTestResult(nativeEvent);
23607
+ if (!hitResult || !hitResult.hitEntry) {
22978
23608
  return;
22979
23609
  }
22980
- const screenPoint = resolveScreenPointFromEvent(nativeEvent);
22981
- if (!screenPoint) {
23610
+ dispatchSpriteClick(hitResult.hitEntry, hitResult.screenPoint, nativeEvent);
23611
+ };
23612
+ const processHoverEvent = (nativeEvent) => {
23613
+ if (!hasSpriteHoverListeners()) {
22982
23614
  return;
22983
23615
  }
22984
- const hitEntry = findTopmostHitEntry(screenPoint);
22985
- if (!hitEntry) {
23616
+ const hitResult = resolveHitTestResult(nativeEvent);
23617
+ if (!hitResult) {
22986
23618
  return;
22987
23619
  }
22988
- dispatchSpriteClick(hitEntry, screenPoint, nativeEvent);
23620
+ dispatchSpriteHover(hitResult.hitEntry, hitResult.screenPoint, nativeEvent);
22989
23621
  };
22990
23622
  const ensureTextures = () => {
22991
23623
  if (!gl) {
@@ -23020,16 +23652,6 @@ const createSpriteLayer = (options) => {
23020
23652
  glContext.TEXTURE_WRAP_T,
23021
23653
  glContext.CLAMP_TO_EDGE
23022
23654
  );
23023
- glContext.texParameteri(
23024
- glContext.TEXTURE_2D,
23025
- glContext.TEXTURE_MIN_FILTER,
23026
- glContext.LINEAR
23027
- );
23028
- glContext.texParameteri(
23029
- glContext.TEXTURE_2D,
23030
- glContext.TEXTURE_MAG_FILTER,
23031
- glContext.LINEAR
23032
- );
23033
23655
  glContext.pixelStorei(glContext.UNPACK_PREMULTIPLY_ALPHA_WEBGL, 1);
23034
23656
  glContext.texImage2D(
23035
23657
  glContext.TEXTURE_2D,
@@ -23039,6 +23661,52 @@ const createSpriteLayer = (options) => {
23039
23661
  glContext.UNSIGNED_BYTE,
23040
23662
  image.bitmap
23041
23663
  );
23664
+ let minFilterEnum = resolveGlMinFilter(
23665
+ glContext,
23666
+ resolvedTextureFiltering.minFilter
23667
+ );
23668
+ const magFilterEnum = resolveGlMagFilter(
23669
+ glContext,
23670
+ resolvedTextureFiltering.magFilter
23671
+ );
23672
+ let usedMipmaps = false;
23673
+ if (resolvedTextureFiltering.generateMipmaps) {
23674
+ const isWebGL2 = typeof WebGL2RenderingContext !== "undefined" && glContext instanceof WebGL2RenderingContext;
23675
+ const canUseMipmaps = isWebGL2 || isPowerOfTwo(image.width) && isPowerOfTwo(image.height);
23676
+ if (canUseMipmaps) {
23677
+ glContext.generateMipmap(glContext.TEXTURE_2D);
23678
+ usedMipmaps = true;
23679
+ } else {
23680
+ minFilterEnum = glContext.LINEAR;
23681
+ }
23682
+ }
23683
+ if (!usedMipmaps && filterRequiresMipmaps(resolvedTextureFiltering.minFilter)) {
23684
+ minFilterEnum = glContext.LINEAR;
23685
+ }
23686
+ glContext.texParameteri(
23687
+ glContext.TEXTURE_2D,
23688
+ glContext.TEXTURE_MIN_FILTER,
23689
+ minFilterEnum
23690
+ );
23691
+ glContext.texParameteri(
23692
+ glContext.TEXTURE_2D,
23693
+ glContext.TEXTURE_MAG_FILTER,
23694
+ magFilterEnum
23695
+ );
23696
+ if (usedMipmaps && anisotropyExtension && resolvedTextureFiltering.maxAnisotropy > 1) {
23697
+ const ext = anisotropyExtension;
23698
+ const targetAnisotropy = Math.min(
23699
+ resolvedTextureFiltering.maxAnisotropy,
23700
+ maxSupportedAnisotropy
23701
+ );
23702
+ if (targetAnisotropy > 1) {
23703
+ glContext.texParameterf(
23704
+ glContext.TEXTURE_2D,
23705
+ ext.TEXTURE_MAX_ANISOTROPY_EXT,
23706
+ targetAnisotropy
23707
+ );
23708
+ }
23709
+ }
23042
23710
  image.texture = texture;
23043
23711
  });
23044
23712
  };
@@ -23081,6 +23749,20 @@ const createSpriteLayer = (options) => {
23081
23749
  const onAdd = (mapInstance, glContext) => {
23082
23750
  map = mapInstance;
23083
23751
  gl = glContext;
23752
+ anisotropyExtension = resolveAnisotropyExtension(glContext);
23753
+ if (anisotropyExtension) {
23754
+ const ext = anisotropyExtension;
23755
+ const supported = glContext.getParameter(
23756
+ ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT
23757
+ );
23758
+ if (typeof supported === "number" && Number.isFinite(supported) && supported >= 1) {
23759
+ maxSupportedAnisotropy = supported;
23760
+ } else {
23761
+ maxSupportedAnisotropy = 1;
23762
+ }
23763
+ } else {
23764
+ maxSupportedAnisotropy = 1;
23765
+ }
23084
23766
  canvasElement = mapInstance.getCanvas();
23085
23767
  const registerDisposer = (disposer) => {
23086
23768
  inputListenerDisposers.push(disposer);
@@ -23088,24 +23770,42 @@ const createSpriteLayer = (options) => {
23088
23770
  const supportsPointerEvents = typeof window !== "undefined" && "PointerEvent" in window;
23089
23771
  if (canvasElement) {
23090
23772
  if (supportsPointerEvents) {
23091
- const pointerListener = (event) => {
23773
+ const pointerUpListener = (event) => {
23092
23774
  if (event.pointerType === "mouse" && event.button !== 0) {
23093
23775
  return;
23094
23776
  }
23095
- processInteractionEvent(event);
23777
+ processClickEvent(event);
23096
23778
  };
23097
- canvasElement.addEventListener("pointerup", pointerListener, {
23779
+ canvasElement.addEventListener("pointerup", pointerUpListener, {
23098
23780
  passive: true
23099
23781
  });
23100
23782
  registerDisposer(() => {
23101
- canvasElement == null ? void 0 : canvasElement.removeEventListener("pointerup", pointerListener);
23783
+ canvasElement == null ? void 0 : canvasElement.removeEventListener("pointerup", pointerUpListener);
23784
+ });
23785
+ const pointerMoveListener = (event) => {
23786
+ if (!event.isPrimary) {
23787
+ return;
23788
+ }
23789
+ if (event.pointerType === "touch") {
23790
+ return;
23791
+ }
23792
+ processHoverEvent(event);
23793
+ };
23794
+ canvasElement.addEventListener("pointermove", pointerMoveListener, {
23795
+ passive: true
23796
+ });
23797
+ registerDisposer(() => {
23798
+ canvasElement == null ? void 0 : canvasElement.removeEventListener(
23799
+ "pointermove",
23800
+ pointerMoveListener
23801
+ );
23102
23802
  });
23103
23803
  } else {
23104
23804
  const clickListener = (event) => {
23105
23805
  if (event.button !== 0) {
23106
23806
  return;
23107
23807
  }
23108
- processInteractionEvent(event);
23808
+ processClickEvent(event);
23109
23809
  };
23110
23810
  canvasElement.addEventListener("click", clickListener, {
23111
23811
  passive: true
@@ -23114,7 +23814,7 @@ const createSpriteLayer = (options) => {
23114
23814
  canvasElement == null ? void 0 : canvasElement.removeEventListener("click", clickListener);
23115
23815
  });
23116
23816
  const touchListener = (event) => {
23117
- processInteractionEvent(event);
23817
+ processClickEvent(event);
23118
23818
  };
23119
23819
  canvasElement.addEventListener("touchend", touchListener, {
23120
23820
  passive: true
@@ -23122,6 +23822,15 @@ const createSpriteLayer = (options) => {
23122
23822
  registerDisposer(() => {
23123
23823
  canvasElement == null ? void 0 : canvasElement.removeEventListener("touchend", touchListener);
23124
23824
  });
23825
+ const mouseMoveListener = (event) => {
23826
+ processHoverEvent(event);
23827
+ };
23828
+ canvasElement.addEventListener("mousemove", mouseMoveListener, {
23829
+ passive: true
23830
+ });
23831
+ registerDisposer(() => {
23832
+ canvasElement == null ? void 0 : canvasElement.removeEventListener("mousemove", mouseMoveListener);
23833
+ });
23125
23834
  }
23126
23835
  }
23127
23836
  const buffer = glContext.createBuffer();
@@ -23215,6 +23924,8 @@ const createSpriteLayer = (options) => {
23215
23924
  attribUvLocation = -1;
23216
23925
  uniformTextureLocation = null;
23217
23926
  uniformOpacityLocation = null;
23927
+ anisotropyExtension = null;
23928
+ maxSupportedAnisotropy = 1;
23218
23929
  };
23219
23930
  const render = (glContext, _options) => {
23220
23931
  hitTestEntries.length = 0;
@@ -23255,41 +23966,8 @@ const createSpriteLayer = (options) => {
23255
23966
  }
23256
23967
  sprite.images.forEach((orderMap) => {
23257
23968
  orderMap.forEach((image) => {
23258
- const rotationState = image.rotationInterpolationState;
23259
- if (rotationState) {
23260
- const evaluation = evaluateNumericInterpolation({
23261
- state: rotationState,
23262
- timestamp
23263
- });
23264
- if (rotationState.startTimestamp < 0) {
23265
- rotationState.startTimestamp = evaluation.effectiveStartTimestamp;
23266
- }
23267
- image.displayedRotateDeg = normaliseAngleDeg(evaluation.value);
23268
- if (evaluation.completed) {
23269
- image.displayedRotateDeg = normaliseAngleDeg(
23270
- rotationState.finalValue
23271
- );
23272
- image.rotationInterpolationState = null;
23273
- } else {
23274
- hasActiveInterpolation = true;
23275
- }
23276
- }
23277
- const offsetState = image.offsetInterpolationState;
23278
- if (offsetState) {
23279
- const evaluation = evaluateNumericInterpolation({
23280
- state: offsetState,
23281
- timestamp
23282
- });
23283
- if (offsetState.startTimestamp < 0) {
23284
- offsetState.startTimestamp = evaluation.effectiveStartTimestamp;
23285
- }
23286
- image.offset.offsetDeg = evaluation.value;
23287
- if (evaluation.completed) {
23288
- image.offset.offsetDeg = offsetState.finalValue;
23289
- image.offsetInterpolationState = null;
23290
- } else {
23291
- hasActiveInterpolation = true;
23292
- }
23969
+ if (stepSpriteImageInterpolations(image, timestamp)) {
23970
+ hasActiveInterpolation = true;
23293
23971
  }
23294
23972
  });
23295
23973
  });
@@ -23346,7 +24024,7 @@ const createSpriteLayer = (options) => {
23346
24024
  let screenCornerBuffer = null;
23347
24025
  const anchor = (_a2 = imageEntry.anchor) != null ? _a2 : DEFAULT_ANCHOR;
23348
24026
  const offsetDef = (_b = imageEntry.offset) != null ? _b : DEFAULT_IMAGE_OFFSET;
23349
- const totalRotateDeg = Number.isFinite(imageEntry.displayedRotateDeg) ? imageEntry.displayedRotateDeg : normaliseAngleDeg(
24027
+ const totalRotateDeg = Number.isFinite(imageEntry.displayedRotateDeg) ? imageEntry.displayedRotateDeg : normalizeAngleDeg(
23350
24028
  ((_c = imageEntry.resolvedBaseRotateDeg) != null ? _c : 0) + ((_d = imageEntry.rotateDeg) != null ? _d : 0)
23351
24029
  );
23352
24030
  const projected = mapInstance.project(spriteEntry.currentLocation);
@@ -23645,7 +24323,7 @@ const createSpriteLayer = (options) => {
23645
24323
  spriteMaxPixel
23646
24324
  }
23647
24325
  );
23648
- const totalRotateDeg = Number.isFinite(imageEntry.displayedRotateDeg) ? imageEntry.displayedRotateDeg : normaliseAngleDeg(
24326
+ const totalRotateDeg = Number.isFinite(imageEntry.displayedRotateDeg) ? imageEntry.displayedRotateDeg : normalizeAngleDeg(
23649
24327
  ((_e = imageEntry.resolvedBaseRotateDeg) != null ? _e : 0) + ((_f = imageEntry.rotateDeg) != null ? _f : 0)
23650
24328
  );
23651
24329
  const offsetMeters = calculateSurfaceOffsetMeters(
@@ -23765,8 +24443,20 @@ const createSpriteLayer = (options) => {
23765
24443
  glContext.disable(glContext.BLEND);
23766
24444
  scheduleRender();
23767
24445
  };
23768
- const registerImage = async (imageId, imageSource) => {
23769
- const bitmap = typeof imageSource === "string" ? await loadImageBitmap(imageSource) : imageSource;
24446
+ const registerImage = async (imageId, imageSource, options2) => {
24447
+ let bitmap;
24448
+ try {
24449
+ bitmap = typeof imageSource === "string" ? await loadImageBitmap(imageSource, options2) : imageSource;
24450
+ } catch (error) {
24451
+ if (error instanceof SvgSizeResolutionError) {
24452
+ console.warn(
24453
+ `[SpriteLayer] Unable to register image "${imageId}": ${error.message}`,
24454
+ error
24455
+ );
24456
+ return false;
24457
+ }
24458
+ throw error;
24459
+ }
23770
24460
  if (images.has(imageId)) {
23771
24461
  return false;
23772
24462
  }
@@ -24215,7 +24905,7 @@ const createSpriteLayer = (options) => {
24215
24905
  if (state.autoRotation) {
24216
24906
  state.resolvedBaseRotateDeg = sprite.lastAutoRotationAngleDeg;
24217
24907
  }
24218
- updateImageDisplayedRotation(state);
24908
+ syncImageRotationChannel(state);
24219
24909
  setImageState(sprite, state);
24220
24910
  resultOut.isUpdated = true;
24221
24911
  return true;
@@ -24256,41 +24946,32 @@ const createSpriteLayer = (options) => {
24256
24946
  if (imageUpdate.anchor !== void 0) {
24257
24947
  state.anchor = cloneAnchor(imageUpdate.anchor);
24258
24948
  }
24259
- const rotationInterpolation = imageUpdate.rotationInterpolation;
24260
- const offsetInterpolationOption = rotationInterpolation == null ? void 0 : rotationInterpolation.offsetDeg;
24261
- const rotateInterpolationOption = rotationInterpolation == null ? void 0 : rotationInterpolation.rotateDeg;
24949
+ const interpolationOptions = imageUpdate.interpolation;
24950
+ const offsetDegInterpolationOption = interpolationOptions == null ? void 0 : interpolationOptions.offsetDeg;
24951
+ const offsetMetersInterpolationOption = interpolationOptions == null ? void 0 : interpolationOptions.offsetMeters;
24952
+ const rotateInterpolationOption = interpolationOptions == null ? void 0 : interpolationOptions.rotateDeg;
24262
24953
  let rotationOverride;
24263
24954
  let hasRotationOverride = false;
24264
24955
  if (imageUpdate.offset !== void 0) {
24265
- const newOffset = cloneOffset(imageUpdate.offset);
24266
- if (offsetInterpolationOption && offsetInterpolationOption.durationMs > 0) {
24267
- const { state: interpolationState, requiresInterpolation } = createNumericInterpolationState({
24268
- currentValue: state.offset.offsetDeg,
24269
- targetValue: newOffset.offsetDeg,
24270
- options: offsetInterpolationOption
24271
- });
24272
- if (requiresInterpolation) {
24273
- state.offset.offsetMeters = newOffset.offsetMeters;
24274
- state.offsetInterpolationState = interpolationState;
24275
- } else {
24276
- state.offset = newOffset;
24277
- state.offsetInterpolationState = null;
24278
- }
24279
- } else {
24280
- state.offset = newOffset;
24281
- state.offsetInterpolationState = null;
24956
+ const clonedOffset = cloneOffset(imageUpdate.offset);
24957
+ applyOffsetUpdate(state, clonedOffset, {
24958
+ deg: offsetDegInterpolationOption,
24959
+ meters: offsetMetersInterpolationOption
24960
+ });
24961
+ } else {
24962
+ if (offsetDegInterpolationOption === null) {
24963
+ clearOffsetDegInterpolation(state);
24964
+ }
24965
+ if (offsetMetersInterpolationOption === null) {
24966
+ clearOffsetMetersInterpolation(state);
24282
24967
  }
24283
- } else if (offsetInterpolationOption === null) {
24284
- state.offsetInterpolationState = null;
24285
24968
  }
24286
24969
  if (rotateInterpolationOption !== void 0) {
24287
24970
  if (rotateInterpolationOption === null) {
24288
24971
  state.rotationInterpolationOptions = null;
24289
24972
  rotationOverride = null;
24290
24973
  } else {
24291
- const cloned = cloneNumericInterpolationOptions(
24292
- rotateInterpolationOption
24293
- );
24974
+ const cloned = cloneInterpolationOptions(rotateInterpolationOption);
24294
24975
  state.rotationInterpolationOptions = cloned;
24295
24976
  rotationOverride = cloned;
24296
24977
  }
@@ -24332,7 +25013,7 @@ const createSpriteLayer = (options) => {
24332
25013
  }
24333
25014
  }
24334
25015
  if (requireRotationSync) {
24335
- updateImageDisplayedRotation(
25016
+ syncImageRotationChannel(
24336
25017
  state,
24337
25018
  // When a rotation override has been computed, pass it along (null clears interpolation); otherwise leave undefined.
24338
25019
  hasRotationOverride ? rotationOverride != null ? rotationOverride : null : void 0
@@ -24497,34 +25178,6 @@ const createSpriteLayer = (options) => {
24497
25178
  return true;
24498
25179
  }
24499
25180
  };
24500
- const updateBulk = (updateBulkList) => {
24501
- let updatedCount = 0;
24502
- let isRequiredRender = false;
24503
- updateBulkList.forEach((update) => {
24504
- const result = updateSpriteInternal(update.spriteId, update);
24505
- switch (result) {
24506
- case "notfound":
24507
- // Sprite missing; nothing to do for this entry.
24508
- case "ignored":
24509
- break;
24510
- case "updated":
24511
- updatedCount++;
24512
- break;
24513
- // When rendering must occur because of this update
24514
- case "isRequiredRender":
24515
- ensureRenderTargetEntries();
24516
- scheduleRender();
24517
- updatedCount++;
24518
- isRequiredRender = true;
24519
- break;
24520
- }
24521
- });
24522
- if (isRequiredRender) {
24523
- ensureRenderTargetEntries();
24524
- scheduleRender();
24525
- }
24526
- return updatedCount;
24527
- };
24528
25181
  const mutateSprites = (sourceItems, mutator) => {
24529
25182
  if (sourceItems.length === 0) {
24530
25183
  return 0;
@@ -24537,6 +25190,10 @@ const createSpriteLayer = (options) => {
24537
25190
  isUpdated: false
24538
25191
  };
24539
25192
  const updateObject = {
25193
+ isEnabled: void 0,
25194
+ location: void 0,
25195
+ interpolation: void 0,
25196
+ tag: void 0,
24540
25197
  getImageIndexMap: () => {
24541
25198
  const map2 = /* @__PURE__ */ new Map();
24542
25199
  currentSprite.images.forEach((inner, subLayer) => {
@@ -24730,7 +25387,6 @@ const createSpriteLayer = (options) => {
24730
25387
  updateSpriteImage,
24731
25388
  removeSpriteImage,
24732
25389
  updateSprite,
24733
- updateBulk,
24734
25390
  mutateSprites,
24735
25391
  updateForEach,
24736
25392
  on: addEventListener2,
@@ -24739,7 +25395,10 @@ const createSpriteLayer = (options) => {
24739
25395
  return spriteLayout;
24740
25396
  };
24741
25397
  export {
25398
+ BETTER_TEXTURE_FILTERING_OPTIONS,
25399
+ DEFAULT_TEXTURE_FILTERING_OPTIONS,
24742
25400
  STANDARD_SPRITE_SCALING_OPTIONS,
25401
+ SvgSizeResolutionError,
24743
25402
  UNLIMITED_SPRITE_SCALING_OPTIONS,
24744
25403
  applyAutoRotation,
24745
25404
  calculatePerspectiveRatio,
@@ -24750,7 +25409,9 @@ export {
24750
25409
  createImageStateFromInit,
24751
25410
  createShaderProgram,
24752
25411
  createSpriteLayer,
25412
+ loadImageBitmap,
24753
25413
  multiplyMatrixAndVector,
24754
- projectLngLatToClipSpace
25414
+ projectLngLatToClipSpace,
25415
+ readImageBitmap
24755
25416
  };
24756
25417
  //# sourceMappingURL=index.mjs.map