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