maplibre-gl-layers 0.6.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.6.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: 481f544de02fd3e71a2ba6c28bbb7eeb98b49eff
11
+ * git.commit.hash: 58b99588e56fc80c6874d78a17e91a50901abc17
9
12
  */
10
13
  const UNLIMITED_SPRITE_SCALING_OPTIONS = {
11
14
  metersPerPixel: 1,
@@ -37,6 +40,270 @@ const BETTER_TEXTURE_FILTERING_OPTIONS = {
37
40
  generateMipmaps: true,
38
41
  maxAnisotropy: 8
39
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
+ };
40
307
  var maplibreGl$1 = { exports: {} };
41
308
  /**
42
309
  * MapLibre GL JS
@@ -21111,14 +21378,6 @@ function transformMat4(out, a, m) {
21111
21378
  return a;
21112
21379
  };
21113
21380
  })();
21114
- const loadImageBitmap = async (url) => {
21115
- const response = await fetch(url);
21116
- if (!response.ok) {
21117
- throw new Error(`Failed to fetch image from ${url}`);
21118
- }
21119
- const blob = await response.blob();
21120
- return await createImageBitmap(blob);
21121
- };
21122
21381
  const cloneSpriteLocation = (location2) => {
21123
21382
  if (location2.z === void 0) {
21124
21383
  return { lng: location2.lng, lat: location2.lat };
@@ -21175,7 +21434,7 @@ const computeFeedforwardTarget = (previous, next) => {
21175
21434
  }
21176
21435
  return target;
21177
21436
  };
21178
- const normaliseOptions$1 = (options) => {
21437
+ const normalizeOptions$2 = (options) => {
21179
21438
  var _a;
21180
21439
  return {
21181
21440
  durationMs: Math.max(0, options.durationMs),
@@ -21185,7 +21444,7 @@ const normaliseOptions$1 = (options) => {
21185
21444
  };
21186
21445
  const createInterpolationState = (params) => {
21187
21446
  const { currentLocation, lastCommandLocation, nextCommandLocation } = params;
21188
- const options = normaliseOptions$1(params.options);
21447
+ const options = normalizeOptions$2(params.options);
21189
21448
  const from = cloneSpriteLocation(currentLocation);
21190
21449
  const easing = resolveEasing(options.easing);
21191
21450
  let to;
@@ -21230,8 +21489,8 @@ const evaluateInterpolation = (params) => {
21230
21489
  };
21231
21490
  };
21232
21491
  const NUMERIC_EPSILON = 1e-6;
21233
- const normaliseDuration = (durationMs) => Number.isFinite(durationMs) && durationMs > 0 ? durationMs : 0;
21234
- const normaliseDelta = (delta) => {
21492
+ const normalizeDuration$1 = (durationMs) => Number.isFinite(durationMs) && durationMs > 0 ? durationMs : 0;
21493
+ const normalizeDelta = (delta) => {
21235
21494
  if (!Number.isFinite(delta)) {
21236
21495
  return 0;
21237
21496
  }
@@ -21243,16 +21502,24 @@ const normaliseDelta = (delta) => {
21243
21502
  }
21244
21503
  return adjusted;
21245
21504
  };
21246
- const normaliseOptions = (options) => {
21505
+ const normalizeOptions$1 = (options) => {
21506
+ var _a;
21247
21507
  return {
21248
- durationMs: normaliseDuration(options.durationMs),
21249
- easing: resolveEasing(options.easing)
21508
+ durationMs: normalizeDuration$1(options.durationMs),
21509
+ easing: resolveEasing(options.easing),
21510
+ mode: (_a = options.mode) != null ? _a : "feedback"
21250
21511
  };
21251
21512
  };
21252
- const createNumericInterpolationState = (params) => {
21513
+ const createDegreeInterpolationState = (params) => {
21253
21514
  const { currentValue, targetValue } = params;
21254
- const options = normaliseOptions(params.options);
21255
- 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);
21256
21523
  const pathTarget = currentValue + delta;
21257
21524
  const requiresInterpolation = options.durationMs > 0 && Math.abs(delta) > NUMERIC_EPSILON;
21258
21525
  const state = {
@@ -21260,7 +21527,7 @@ const createNumericInterpolationState = (params) => {
21260
21527
  easing: options.easing,
21261
21528
  from: currentValue,
21262
21529
  to: pathTarget,
21263
- finalValue: targetValue,
21530
+ finalValue: effectiveTarget,
21264
21531
  startTimestamp: -1
21265
21532
  };
21266
21533
  return {
@@ -21268,7 +21535,7 @@ const createNumericInterpolationState = (params) => {
21268
21535
  requiresInterpolation
21269
21536
  };
21270
21537
  };
21271
- const clamp01 = (value) => {
21538
+ const clamp01$1 = (value) => {
21272
21539
  if (!Number.isFinite(value)) {
21273
21540
  return 1;
21274
21541
  }
@@ -21280,7 +21547,7 @@ const clamp01 = (value) => {
21280
21547
  }
21281
21548
  return value;
21282
21549
  };
21283
- const evaluateNumericInterpolation = (params) => {
21550
+ const evaluateDegreeInterpolation = (params) => {
21284
21551
  const { state } = params;
21285
21552
  const timestamp = Number.isFinite(params.timestamp) ? params.timestamp : Date.now();
21286
21553
  const duration = Math.max(0, state.durationMs);
@@ -21294,7 +21561,7 @@ const evaluateNumericInterpolation = (params) => {
21294
21561
  }
21295
21562
  const elapsed = timestamp - effectiveStart;
21296
21563
  const rawProgress = duration <= 0 ? 1 : elapsed / duration;
21297
- const eased = clamp01(state.easing(rawProgress));
21564
+ const eased = clamp01$1(state.easing(rawProgress));
21298
21565
  const interpolated = state.from + (state.to - state.from) * eased;
21299
21566
  const completed = rawProgress >= 1;
21300
21567
  return {
@@ -21303,7 +21570,7 @@ const evaluateNumericInterpolation = (params) => {
21303
21570
  effectiveStartTimestamp: effectiveStart
21304
21571
  };
21305
21572
  };
21306
- const normaliseAngleDeg = (angle) => {
21573
+ const normalizeAngleDeg = (angle) => {
21307
21574
  if (!Number.isFinite(angle)) {
21308
21575
  return 0;
21309
21576
  }
@@ -21313,17 +21580,19 @@ const normaliseAngleDeg = (angle) => {
21313
21580
  };
21314
21581
  const resolveRotationTarget = (params) => {
21315
21582
  const options = params.options;
21316
- const targetAngle = normaliseAngleDeg(params.targetAngleDeg);
21317
- 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;
21318
21586
  if (!options || options.durationMs <= 0) {
21319
21587
  return {
21320
21588
  nextAngleDeg: targetAngle,
21321
21589
  interpolationState: null
21322
21590
  };
21323
21591
  }
21324
- const { state, requiresInterpolation } = createNumericInterpolationState({
21592
+ const { state, requiresInterpolation } = createDegreeInterpolationState({
21325
21593
  currentValue: currentAngle,
21326
21594
  targetValue: targetAngle,
21595
+ previousCommandValue: previousCommandAngleDeg,
21327
21596
  options
21328
21597
  });
21329
21598
  if (!requiresInterpolation) {
@@ -22003,6 +22272,243 @@ const calculateSurfaceCornerDisplacements = (params) => {
22003
22272
  }
22004
22273
  return corners;
22005
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
+ };
22006
22512
  const DEFAULT_ANCHOR = { x: 0, y: 0 };
22007
22513
  const DEFAULT_AUTO_ROTATION_MIN_DISTANCE_METERS = 20;
22008
22514
  const DEFAULT_IMAGE_OFFSET = {
@@ -22220,14 +22726,14 @@ const applyAutoRotation = (sprite, nextLocation) => {
22220
22726
  return false;
22221
22727
  }
22222
22728
  const resolvedAngleRaw = isFiniteNumber(bearingDeg) ? bearingDeg : sprite.lastAutoRotationAngleDeg;
22223
- const resolvedAngle = normaliseAngleDeg(resolvedAngleRaw);
22729
+ const resolvedAngle = normalizeAngleDeg(resolvedAngleRaw);
22224
22730
  sprite.images.forEach((orderMap) => {
22225
22731
  orderMap.forEach((image) => {
22226
22732
  if (!image.autoRotation) {
22227
22733
  return;
22228
22734
  }
22229
22735
  image.resolvedBaseRotateDeg = resolvedAngle;
22230
- updateImageDisplayedRotation(image);
22736
+ syncImageRotationChannel(image);
22231
22737
  });
22232
22738
  });
22233
22739
  sprite.lastAutoRotationLocation = cloneSpriteLocation(nextLocation);
@@ -22663,23 +23169,6 @@ const cloneOffset = (offset) => {
22663
23169
  offsetDeg: offset.offsetDeg
22664
23170
  };
22665
23171
  };
22666
- const updateImageDisplayedRotation = (image, optionsOverride) => {
22667
- const targetAngle = normaliseAngleDeg(
22668
- image.resolvedBaseRotateDeg + image.rotateDeg
22669
- );
22670
- const currentAngle = Number.isFinite(image.displayedRotateDeg) ? image.displayedRotateDeg : targetAngle;
22671
- const options = optionsOverride === void 0 ? image.rotationInterpolationOptions : optionsOverride;
22672
- const { nextAngleDeg, interpolationState } = resolveRotationTarget({
22673
- currentAngleDeg: currentAngle,
22674
- targetAngleDeg: targetAngle,
22675
- options: options != null ? options : void 0
22676
- });
22677
- image.displayedRotateDeg = nextAngleDeg;
22678
- image.rotationInterpolationState = interpolationState;
22679
- if (!interpolationState) {
22680
- image.displayedRotateDeg = targetAngle;
22681
- }
22682
- };
22683
23172
  const cloneInterpolationOptions = (options) => {
22684
23173
  return {
22685
23174
  mode: options.mode,
@@ -22687,40 +23176,40 @@ const cloneInterpolationOptions = (options) => {
22687
23176
  easing: options.easing
22688
23177
  };
22689
23178
  };
22690
- const cloneNumericInterpolationOptions = (options) => {
22691
- return {
22692
- durationMs: options.durationMs,
22693
- easing: options.easing
22694
- };
22695
- };
22696
23179
  const createImageStateFromInit = (imageInit, subLayer, order) => {
22697
23180
  var _a, _b, _c, _d, _e, _f, _g, _h, _i;
22698
23181
  const mode = (_a = imageInit.mode) != null ? _a : "surface";
22699
23182
  const autoRotationDefault = mode === "surface";
23183
+ const initialOffset = cloneOffset(imageInit.offset);
23184
+ const initialRotateDeg = normalizeAngleDeg((_b = imageInit.rotateDeg) != null ? _b : 0);
22700
23185
  const state = {
22701
23186
  subLayer,
22702
23187
  order,
22703
23188
  imageId: imageInit.imageId,
22704
23189
  mode,
22705
- opacity: (_b = imageInit.opacity) != null ? _b : 1,
22706
- scale: (_c = imageInit.scale) != null ? _c : 1,
23190
+ opacity: (_c = imageInit.opacity) != null ? _c : 1,
23191
+ scale: (_d = imageInit.scale) != null ? _d : 1,
22707
23192
  anchor: cloneAnchor(imageInit.anchor),
22708
- offset: cloneOffset(imageInit.offset),
22709
- rotateDeg: (_d = imageInit.rotateDeg) != null ? _d : 0,
22710
- displayedRotateDeg: normaliseAngleDeg((_e = imageInit.rotateDeg) != null ? _e : 0),
23193
+ offset: initialOffset,
23194
+ rotateDeg: (_e = imageInit.rotateDeg) != null ? _e : 0,
23195
+ displayedRotateDeg: initialRotateDeg,
22711
23196
  autoRotation: (_f = imageInit.autoRotation) != null ? _f : autoRotationDefault,
22712
23197
  autoRotationMinDistanceMeters: (_g = imageInit.autoRotationMinDistanceMeters) != null ? _g : DEFAULT_AUTO_ROTATION_MIN_DISTANCE_METERS,
22713
23198
  resolvedBaseRotateDeg: 0,
22714
23199
  originLocation: cloneOriginLocation(imageInit.originLocation),
22715
23200
  rotationInterpolationState: null,
22716
23201
  rotationInterpolationOptions: null,
22717
- offsetInterpolationState: null
23202
+ offsetDegInterpolationState: null,
23203
+ offsetMetersInterpolationState: null,
23204
+ lastCommandRotateDeg: initialRotateDeg,
23205
+ lastCommandOffsetDeg: initialOffset.offsetDeg,
23206
+ lastCommandOffsetMeters: initialOffset.offsetMeters
22718
23207
  };
22719
- 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;
22720
23209
  if (rotateInitOption) {
22721
- state.rotationInterpolationOptions = cloneNumericInterpolationOptions(rotateInitOption);
23210
+ state.rotationInterpolationOptions = cloneInterpolationOptions(rotateInitOption);
22722
23211
  }
22723
- updateImageDisplayedRotation(state);
23212
+ syncImageRotationChannel(state);
22724
23213
  return state;
22725
23214
  };
22726
23215
  const createSpriteLayer = (options) => {
@@ -22870,7 +23359,7 @@ const createSpriteLayer = (options) => {
22870
23359
  baseY = refCenter.y;
22871
23360
  }
22872
23361
  }
22873
- const totalRotDeg = Number.isFinite(img.displayedRotateDeg) ? img.displayedRotateDeg : normaliseAngleDeg(
23362
+ const totalRotDeg = Number.isFinite(img.displayedRotateDeg) ? img.displayedRotateDeg : normalizeAngleDeg(
22874
23363
  ((_d = img.resolvedBaseRotateDeg) != null ? _d : 0) + ((_e = img.rotateDeg) != null ? _e : 0)
22875
23364
  );
22876
23365
  const imageScaleLocal = (_f = img.scale) != null ? _f : 1;
@@ -23020,13 +23509,15 @@ const createSpriteLayer = (options) => {
23020
23509
  };
23021
23510
  let canvasElement = null;
23022
23511
  const inputListenerDisposers = [];
23023
- const hasSpriteClickListeners = () => {
23512
+ const hasSpriteListeners = (type) => {
23024
23513
  var _a2, _b;
23025
23514
  return (
23026
23515
  // Treat missing listener sets as zero, otherwise check the registered count.
23027
- ((_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
23028
23517
  );
23029
23518
  };
23519
+ const hasSpriteClickListeners = () => hasSpriteListeners("spriteclick");
23520
+ const hasSpriteHoverListeners = () => hasSpriteListeners("spritehover");
23030
23521
  const resolveScreenPointFromEvent = (nativeEvent) => {
23031
23522
  var _a2, _b, _c, _d;
23032
23523
  if (!canvasElement) {
@@ -23051,20 +23542,31 @@ const createSpriteLayer = (options) => {
23051
23542
  const mouseLike = nativeEvent;
23052
23543
  return toScreenPoint(mouseLike.clientX, mouseLike.clientY);
23053
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
+ };
23054
23560
  const dispatchSpriteClick = (hitEntry, screenPoint, originalEvent) => {
23055
23561
  const listeners = eventListeners.get("spriteclick");
23056
23562
  if (!listeners || listeners.size === 0) {
23057
23563
  return;
23058
23564
  }
23059
- const spriteState = getSpriteState(hitEntry.sprite.spriteId);
23060
- if (!spriteState) {
23061
- return;
23062
- }
23063
- const imageState = hitEntry.image;
23565
+ const payload = resolveSpriteEventPayload(hitEntry);
23064
23566
  const clickEvent = {
23065
23567
  type: "spriteclick",
23066
- sprite: spriteState,
23067
- image: imageState,
23568
+ sprite: payload.sprite,
23569
+ image: payload.image,
23068
23570
  screenPoint,
23069
23571
  originalEvent
23070
23572
  };
@@ -23072,22 +23574,50 @@ const createSpriteLayer = (options) => {
23072
23574
  listener(clickEvent);
23073
23575
  });
23074
23576
  };
23075
- 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) => {
23076
23603
  if (!hasSpriteClickListeners()) {
23077
23604
  return;
23078
23605
  }
23079
- if (hitTestEntries.length === 0) {
23606
+ const hitResult = resolveHitTestResult(nativeEvent);
23607
+ if (!hitResult || !hitResult.hitEntry) {
23080
23608
  return;
23081
23609
  }
23082
- const screenPoint = resolveScreenPointFromEvent(nativeEvent);
23083
- if (!screenPoint) {
23610
+ dispatchSpriteClick(hitResult.hitEntry, hitResult.screenPoint, nativeEvent);
23611
+ };
23612
+ const processHoverEvent = (nativeEvent) => {
23613
+ if (!hasSpriteHoverListeners()) {
23084
23614
  return;
23085
23615
  }
23086
- const hitEntry = findTopmostHitEntry(screenPoint);
23087
- if (!hitEntry) {
23616
+ const hitResult = resolveHitTestResult(nativeEvent);
23617
+ if (!hitResult) {
23088
23618
  return;
23089
23619
  }
23090
- dispatchSpriteClick(hitEntry, screenPoint, nativeEvent);
23620
+ dispatchSpriteHover(hitResult.hitEntry, hitResult.screenPoint, nativeEvent);
23091
23621
  };
23092
23622
  const ensureTextures = () => {
23093
23623
  if (!gl) {
@@ -23240,24 +23770,42 @@ const createSpriteLayer = (options) => {
23240
23770
  const supportsPointerEvents = typeof window !== "undefined" && "PointerEvent" in window;
23241
23771
  if (canvasElement) {
23242
23772
  if (supportsPointerEvents) {
23243
- const pointerListener = (event) => {
23773
+ const pointerUpListener = (event) => {
23244
23774
  if (event.pointerType === "mouse" && event.button !== 0) {
23245
23775
  return;
23246
23776
  }
23247
- processInteractionEvent(event);
23777
+ processClickEvent(event);
23778
+ };
23779
+ canvasElement.addEventListener("pointerup", pointerUpListener, {
23780
+ passive: true
23781
+ });
23782
+ registerDisposer(() => {
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);
23248
23793
  };
23249
- canvasElement.addEventListener("pointerup", pointerListener, {
23794
+ canvasElement.addEventListener("pointermove", pointerMoveListener, {
23250
23795
  passive: true
23251
23796
  });
23252
23797
  registerDisposer(() => {
23253
- canvasElement == null ? void 0 : canvasElement.removeEventListener("pointerup", pointerListener);
23798
+ canvasElement == null ? void 0 : canvasElement.removeEventListener(
23799
+ "pointermove",
23800
+ pointerMoveListener
23801
+ );
23254
23802
  });
23255
23803
  } else {
23256
23804
  const clickListener = (event) => {
23257
23805
  if (event.button !== 0) {
23258
23806
  return;
23259
23807
  }
23260
- processInteractionEvent(event);
23808
+ processClickEvent(event);
23261
23809
  };
23262
23810
  canvasElement.addEventListener("click", clickListener, {
23263
23811
  passive: true
@@ -23266,7 +23814,7 @@ const createSpriteLayer = (options) => {
23266
23814
  canvasElement == null ? void 0 : canvasElement.removeEventListener("click", clickListener);
23267
23815
  });
23268
23816
  const touchListener = (event) => {
23269
- processInteractionEvent(event);
23817
+ processClickEvent(event);
23270
23818
  };
23271
23819
  canvasElement.addEventListener("touchend", touchListener, {
23272
23820
  passive: true
@@ -23274,6 +23822,15 @@ const createSpriteLayer = (options) => {
23274
23822
  registerDisposer(() => {
23275
23823
  canvasElement == null ? void 0 : canvasElement.removeEventListener("touchend", touchListener);
23276
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
+ });
23277
23834
  }
23278
23835
  }
23279
23836
  const buffer = glContext.createBuffer();
@@ -23409,41 +23966,8 @@ const createSpriteLayer = (options) => {
23409
23966
  }
23410
23967
  sprite.images.forEach((orderMap) => {
23411
23968
  orderMap.forEach((image) => {
23412
- const rotationState = image.rotationInterpolationState;
23413
- if (rotationState) {
23414
- const evaluation = evaluateNumericInterpolation({
23415
- state: rotationState,
23416
- timestamp
23417
- });
23418
- if (rotationState.startTimestamp < 0) {
23419
- rotationState.startTimestamp = evaluation.effectiveStartTimestamp;
23420
- }
23421
- image.displayedRotateDeg = normaliseAngleDeg(evaluation.value);
23422
- if (evaluation.completed) {
23423
- image.displayedRotateDeg = normaliseAngleDeg(
23424
- rotationState.finalValue
23425
- );
23426
- image.rotationInterpolationState = null;
23427
- } else {
23428
- hasActiveInterpolation = true;
23429
- }
23430
- }
23431
- const offsetState = image.offsetInterpolationState;
23432
- if (offsetState) {
23433
- const evaluation = evaluateNumericInterpolation({
23434
- state: offsetState,
23435
- timestamp
23436
- });
23437
- if (offsetState.startTimestamp < 0) {
23438
- offsetState.startTimestamp = evaluation.effectiveStartTimestamp;
23439
- }
23440
- image.offset.offsetDeg = evaluation.value;
23441
- if (evaluation.completed) {
23442
- image.offset.offsetDeg = offsetState.finalValue;
23443
- image.offsetInterpolationState = null;
23444
- } else {
23445
- hasActiveInterpolation = true;
23446
- }
23969
+ if (stepSpriteImageInterpolations(image, timestamp)) {
23970
+ hasActiveInterpolation = true;
23447
23971
  }
23448
23972
  });
23449
23973
  });
@@ -23500,7 +24024,7 @@ const createSpriteLayer = (options) => {
23500
24024
  let screenCornerBuffer = null;
23501
24025
  const anchor = (_a2 = imageEntry.anchor) != null ? _a2 : DEFAULT_ANCHOR;
23502
24026
  const offsetDef = (_b = imageEntry.offset) != null ? _b : DEFAULT_IMAGE_OFFSET;
23503
- const totalRotateDeg = Number.isFinite(imageEntry.displayedRotateDeg) ? imageEntry.displayedRotateDeg : normaliseAngleDeg(
24027
+ const totalRotateDeg = Number.isFinite(imageEntry.displayedRotateDeg) ? imageEntry.displayedRotateDeg : normalizeAngleDeg(
23504
24028
  ((_c = imageEntry.resolvedBaseRotateDeg) != null ? _c : 0) + ((_d = imageEntry.rotateDeg) != null ? _d : 0)
23505
24029
  );
23506
24030
  const projected = mapInstance.project(spriteEntry.currentLocation);
@@ -23799,7 +24323,7 @@ const createSpriteLayer = (options) => {
23799
24323
  spriteMaxPixel
23800
24324
  }
23801
24325
  );
23802
- const totalRotateDeg = Number.isFinite(imageEntry.displayedRotateDeg) ? imageEntry.displayedRotateDeg : normaliseAngleDeg(
24326
+ const totalRotateDeg = Number.isFinite(imageEntry.displayedRotateDeg) ? imageEntry.displayedRotateDeg : normalizeAngleDeg(
23803
24327
  ((_e = imageEntry.resolvedBaseRotateDeg) != null ? _e : 0) + ((_f = imageEntry.rotateDeg) != null ? _f : 0)
23804
24328
  );
23805
24329
  const offsetMeters = calculateSurfaceOffsetMeters(
@@ -23919,8 +24443,20 @@ const createSpriteLayer = (options) => {
23919
24443
  glContext.disable(glContext.BLEND);
23920
24444
  scheduleRender();
23921
24445
  };
23922
- const registerImage = async (imageId, imageSource) => {
23923
- 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
+ }
23924
24460
  if (images.has(imageId)) {
23925
24461
  return false;
23926
24462
  }
@@ -24369,7 +24905,7 @@ const createSpriteLayer = (options) => {
24369
24905
  if (state.autoRotation) {
24370
24906
  state.resolvedBaseRotateDeg = sprite.lastAutoRotationAngleDeg;
24371
24907
  }
24372
- updateImageDisplayedRotation(state);
24908
+ syncImageRotationChannel(state);
24373
24909
  setImageState(sprite, state);
24374
24910
  resultOut.isUpdated = true;
24375
24911
  return true;
@@ -24410,41 +24946,32 @@ const createSpriteLayer = (options) => {
24410
24946
  if (imageUpdate.anchor !== void 0) {
24411
24947
  state.anchor = cloneAnchor(imageUpdate.anchor);
24412
24948
  }
24413
- const rotationInterpolation = imageUpdate.rotationInterpolation;
24414
- const offsetInterpolationOption = rotationInterpolation == null ? void 0 : rotationInterpolation.offsetDeg;
24415
- 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;
24416
24953
  let rotationOverride;
24417
24954
  let hasRotationOverride = false;
24418
24955
  if (imageUpdate.offset !== void 0) {
24419
- const newOffset = cloneOffset(imageUpdate.offset);
24420
- if (offsetInterpolationOption && offsetInterpolationOption.durationMs > 0) {
24421
- const { state: interpolationState, requiresInterpolation } = createNumericInterpolationState({
24422
- currentValue: state.offset.offsetDeg,
24423
- targetValue: newOffset.offsetDeg,
24424
- options: offsetInterpolationOption
24425
- });
24426
- if (requiresInterpolation) {
24427
- state.offset.offsetMeters = newOffset.offsetMeters;
24428
- state.offsetInterpolationState = interpolationState;
24429
- } else {
24430
- state.offset = newOffset;
24431
- state.offsetInterpolationState = null;
24432
- }
24433
- } else {
24434
- state.offset = newOffset;
24435
- 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);
24436
24967
  }
24437
- } else if (offsetInterpolationOption === null) {
24438
- state.offsetInterpolationState = null;
24439
24968
  }
24440
24969
  if (rotateInterpolationOption !== void 0) {
24441
24970
  if (rotateInterpolationOption === null) {
24442
24971
  state.rotationInterpolationOptions = null;
24443
24972
  rotationOverride = null;
24444
24973
  } else {
24445
- const cloned = cloneNumericInterpolationOptions(
24446
- rotateInterpolationOption
24447
- );
24974
+ const cloned = cloneInterpolationOptions(rotateInterpolationOption);
24448
24975
  state.rotationInterpolationOptions = cloned;
24449
24976
  rotationOverride = cloned;
24450
24977
  }
@@ -24486,7 +25013,7 @@ const createSpriteLayer = (options) => {
24486
25013
  }
24487
25014
  }
24488
25015
  if (requireRotationSync) {
24489
- updateImageDisplayedRotation(
25016
+ syncImageRotationChannel(
24490
25017
  state,
24491
25018
  // When a rotation override has been computed, pass it along (null clears interpolation); otherwise leave undefined.
24492
25019
  hasRotationOverride ? rotationOverride != null ? rotationOverride : null : void 0
@@ -24651,34 +25178,6 @@ const createSpriteLayer = (options) => {
24651
25178
  return true;
24652
25179
  }
24653
25180
  };
24654
- const updateBulk = (updateBulkList) => {
24655
- let updatedCount = 0;
24656
- let isRequiredRender = false;
24657
- updateBulkList.forEach((update) => {
24658
- const result = updateSpriteInternal(update.spriteId, update);
24659
- switch (result) {
24660
- case "notfound":
24661
- // Sprite missing; nothing to do for this entry.
24662
- case "ignored":
24663
- break;
24664
- case "updated":
24665
- updatedCount++;
24666
- break;
24667
- // When rendering must occur because of this update
24668
- case "isRequiredRender":
24669
- ensureRenderTargetEntries();
24670
- scheduleRender();
24671
- updatedCount++;
24672
- isRequiredRender = true;
24673
- break;
24674
- }
24675
- });
24676
- if (isRequiredRender) {
24677
- ensureRenderTargetEntries();
24678
- scheduleRender();
24679
- }
24680
- return updatedCount;
24681
- };
24682
25181
  const mutateSprites = (sourceItems, mutator) => {
24683
25182
  if (sourceItems.length === 0) {
24684
25183
  return 0;
@@ -24691,6 +25190,10 @@ const createSpriteLayer = (options) => {
24691
25190
  isUpdated: false
24692
25191
  };
24693
25192
  const updateObject = {
25193
+ isEnabled: void 0,
25194
+ location: void 0,
25195
+ interpolation: void 0,
25196
+ tag: void 0,
24694
25197
  getImageIndexMap: () => {
24695
25198
  const map2 = /* @__PURE__ */ new Map();
24696
25199
  currentSprite.images.forEach((inner, subLayer) => {
@@ -24884,7 +25387,6 @@ const createSpriteLayer = (options) => {
24884
25387
  updateSpriteImage,
24885
25388
  removeSpriteImage,
24886
25389
  updateSprite,
24887
- updateBulk,
24888
25390
  mutateSprites,
24889
25391
  updateForEach,
24890
25392
  on: addEventListener2,
@@ -24896,6 +25398,7 @@ export {
24896
25398
  BETTER_TEXTURE_FILTERING_OPTIONS,
24897
25399
  DEFAULT_TEXTURE_FILTERING_OPTIONS,
24898
25400
  STANDARD_SPRITE_SCALING_OPTIONS,
25401
+ SvgSizeResolutionError,
24899
25402
  UNLIMITED_SPRITE_SCALING_OPTIONS,
24900
25403
  applyAutoRotation,
24901
25404
  calculatePerspectiveRatio,
@@ -24906,7 +25409,9 @@ export {
24906
25409
  createImageStateFromInit,
24907
25410
  createShaderProgram,
24908
25411
  createSpriteLayer,
25412
+ loadImageBitmap,
24909
25413
  multiplyMatrixAndVector,
24910
- projectLngLatToClipSpace
25414
+ projectLngLatToClipSpace,
25415
+ readImageBitmap
24911
25416
  };
24912
25417
  //# sourceMappingURL=index.mjs.map