@canvas-tile-engine/core 0.0.4 → 0.2.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.d.mts +190 -50
- package/dist/index.d.ts +190 -50
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -54,6 +54,7 @@ type CanvasTileEngineConfig = {
|
|
|
54
54
|
maxWidth?: number;
|
|
55
55
|
maxHeight?: number;
|
|
56
56
|
};
|
|
57
|
+
responsive?: "preserve-scale" | "preserve-viewport" | false;
|
|
57
58
|
eventHandlers?: EventHandlers;
|
|
58
59
|
bounds?: {
|
|
59
60
|
minX: number;
|
|
@@ -146,23 +147,27 @@ type DrawObject = {
|
|
|
146
147
|
radius?: number | number[];
|
|
147
148
|
};
|
|
148
149
|
type Rect = DrawObject;
|
|
150
|
+
type Circle = Omit<DrawObject, "rotate" | "radius">;
|
|
151
|
+
type ImageItem = Omit<DrawObject, "style"> & {
|
|
152
|
+
img: HTMLImageElement;
|
|
153
|
+
};
|
|
154
|
+
type Text = Omit<DrawObject, "radius" | "size"> & {
|
|
155
|
+
text: string;
|
|
156
|
+
/** Font size in world units (scales with zoom). Default: 1 */
|
|
157
|
+
size?: number;
|
|
158
|
+
style?: {
|
|
159
|
+
fillStyle?: string;
|
|
160
|
+
/** Font family (default: "sans-serif") */
|
|
161
|
+
fontFamily?: string;
|
|
162
|
+
textAlign?: CanvasTextAlign;
|
|
163
|
+
textBaseline?: CanvasTextBaseline;
|
|
164
|
+
};
|
|
165
|
+
};
|
|
149
166
|
type Line = {
|
|
150
167
|
from: Coords;
|
|
151
168
|
to: Coords;
|
|
152
|
-
style: {
|
|
153
|
-
strokeStyle?: string;
|
|
154
|
-
lineWidth?: number;
|
|
155
|
-
};
|
|
156
|
-
};
|
|
157
|
-
type Circle = Omit<DrawObject, "rotate" | "radius">;
|
|
158
|
-
type Text = {
|
|
159
|
-
coords: Coords;
|
|
160
|
-
text: string;
|
|
161
169
|
};
|
|
162
170
|
type Path = Coords[];
|
|
163
|
-
type ImageItem = Omit<DrawObject, "style"> & {
|
|
164
|
-
img: HTMLImageElement;
|
|
165
|
-
};
|
|
166
171
|
|
|
167
172
|
/**
|
|
168
173
|
* Core engine wiring camera, config, renderer, events, and draw helpers.
|
|
@@ -179,36 +184,148 @@ declare class CanvasTileEngine {
|
|
|
179
184
|
images: ImageLoader;
|
|
180
185
|
private sizeController;
|
|
181
186
|
private animationController;
|
|
187
|
+
private responsiveWatcher?;
|
|
182
188
|
canvasWrapper: HTMLDivElement;
|
|
183
189
|
canvas: HTMLCanvasElement;
|
|
184
|
-
/**
|
|
190
|
+
/**
|
|
191
|
+
* Callback when center coordinates change (pan or zoom).
|
|
192
|
+
* @param coords - Center world coordinates: `{ x, y }`
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
* engine.onCoordsChange = (coords) => {
|
|
196
|
+
* console.log(`Center: ${coords.x}, ${coords.y}`);
|
|
197
|
+
* };
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
185
200
|
onCoordsChange?: (coords: Coords) => void;
|
|
186
201
|
private _onClick?;
|
|
202
|
+
/**
|
|
203
|
+
* Callback when a tile is clicked (mouse or touch tap).
|
|
204
|
+
* @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
|
|
205
|
+
* @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
|
|
206
|
+
* @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
|
|
207
|
+
* @example
|
|
208
|
+
* ```ts
|
|
209
|
+
* engine.onClick = (coords, mouse, client) => {
|
|
210
|
+
* console.log(`Clicked tile: ${coords.snapped.x}, ${coords.snapped.y}`);
|
|
211
|
+
* };
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
187
214
|
get onClick(): onClickCallback | undefined;
|
|
188
215
|
set onClick(cb: onClickCallback | undefined);
|
|
189
216
|
private _onRightClick?;
|
|
217
|
+
/**
|
|
218
|
+
* Callback when a tile is right-clicked.
|
|
219
|
+
* @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
|
|
220
|
+
* @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
|
|
221
|
+
* @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
|
|
222
|
+
* @example
|
|
223
|
+
* ```ts
|
|
224
|
+
* engine.onRightClick = (coords) => {
|
|
225
|
+
* showContextMenu(coords.snapped.x, coords.snapped.y);
|
|
226
|
+
* };
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
190
229
|
get onRightClick(): onRightClickCallback | undefined;
|
|
191
230
|
set onRightClick(cb: onRightClickCallback | undefined);
|
|
192
231
|
private _onHover?;
|
|
232
|
+
/**
|
|
233
|
+
* Callback when hovering over tiles.
|
|
234
|
+
* @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
|
|
235
|
+
* @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
|
|
236
|
+
* @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* engine.onHover = (coords) => {
|
|
240
|
+
* setHoveredTile({ x: coords.snapped.x, y: coords.snapped.y });
|
|
241
|
+
* };
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
193
244
|
get onHover(): onHoverCallback | undefined;
|
|
194
245
|
set onHover(cb: onHoverCallback | undefined);
|
|
195
246
|
private _onMouseDown?;
|
|
247
|
+
/**
|
|
248
|
+
* Callback on mouse/touch down.
|
|
249
|
+
* @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
|
|
250
|
+
* @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
|
|
251
|
+
* @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
|
|
252
|
+
* @example
|
|
253
|
+
* ```ts
|
|
254
|
+
* engine.onMouseDown = (coords) => {
|
|
255
|
+
* startPainting(coords.snapped.x, coords.snapped.y);
|
|
256
|
+
* };
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
196
259
|
get onMouseDown(): onMouseDownCallback | undefined;
|
|
197
260
|
set onMouseDown(cb: onMouseDownCallback | undefined);
|
|
198
261
|
private _onMouseUp?;
|
|
262
|
+
/**
|
|
263
|
+
* Callback on mouse/touch up.
|
|
264
|
+
* @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
|
|
265
|
+
* @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
|
|
266
|
+
* @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* engine.onMouseUp = (coords) => {
|
|
270
|
+
* stopPainting();
|
|
271
|
+
* };
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
199
274
|
get onMouseUp(): onMouseUpCallback | undefined;
|
|
200
275
|
set onMouseUp(cb: onMouseUpCallback | undefined);
|
|
201
276
|
private _onMouseLeave?;
|
|
277
|
+
/**
|
|
278
|
+
* Callback when mouse/touch leaves the canvas.
|
|
279
|
+
* @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
|
|
280
|
+
* @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
|
|
281
|
+
* @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
|
|
282
|
+
* @example
|
|
283
|
+
* ```ts
|
|
284
|
+
* engine.onMouseLeave = () => {
|
|
285
|
+
* clearHoveredTile();
|
|
286
|
+
* };
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
202
289
|
get onMouseLeave(): onMouseLeaveCallback | undefined;
|
|
203
290
|
set onMouseLeave(cb: onMouseLeaveCallback | undefined);
|
|
204
291
|
private _onDraw?;
|
|
292
|
+
/**
|
|
293
|
+
* Callback after each draw frame. Use for custom canvas drawing.
|
|
294
|
+
* @param ctx - The canvas 2D rendering context
|
|
295
|
+
* @param info - Frame info: `scale`, `width`, `height`, `coords` (center)
|
|
296
|
+
* @example
|
|
297
|
+
* ```ts
|
|
298
|
+
* engine.onDraw = (ctx, info) => {
|
|
299
|
+
* ctx.fillStyle = "red";
|
|
300
|
+
* ctx.fillText(`Scale: ${info.scale}`, 10, 20);
|
|
301
|
+
* };
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
205
304
|
get onDraw(): onDrawCallback | undefined;
|
|
206
305
|
set onDraw(cb: onDrawCallback | undefined);
|
|
207
306
|
private _onResize?;
|
|
307
|
+
/**
|
|
308
|
+
* Callback on canvas resize.
|
|
309
|
+
* @example
|
|
310
|
+
* ```ts
|
|
311
|
+
* engine.onResize = () => {
|
|
312
|
+
* console.log("Canvas resized:", engine.getSize());
|
|
313
|
+
* };
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
208
316
|
get onResize(): (() => void) | undefined;
|
|
209
317
|
set onResize(cb: (() => void) | undefined);
|
|
210
318
|
private _onZoom?;
|
|
211
|
-
/**
|
|
319
|
+
/**
|
|
320
|
+
* Callback when zoom level changes (wheel or pinch).
|
|
321
|
+
* @param scale - The new scale value
|
|
322
|
+
* @example
|
|
323
|
+
* ```ts
|
|
324
|
+
* engine.onZoom = (scale) => {
|
|
325
|
+
* console.log(`Zoom level: ${scale}`);
|
|
326
|
+
* };
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
212
329
|
get onZoom(): ((scale: number) => void) | undefined;
|
|
213
330
|
set onZoom(cb: ((scale: number) => void) | undefined);
|
|
214
331
|
/**
|
|
@@ -242,6 +359,12 @@ declare class CanvasTileEngine {
|
|
|
242
359
|
* @returns Current canvas scale.
|
|
243
360
|
*/
|
|
244
361
|
getScale(): number;
|
|
362
|
+
/**
|
|
363
|
+
* Set the canvas scale directly, clamped to min/max bounds.
|
|
364
|
+
* @param newScale The desired scale value.
|
|
365
|
+
* @throws {ConfigValidationError} If scale is not a positive finite number.
|
|
366
|
+
*/
|
|
367
|
+
setScale(newScale: number): void;
|
|
245
368
|
/**
|
|
246
369
|
* Zoom in by a given factor, centered on the viewport.
|
|
247
370
|
* @param factor Zoom multiplier (default: 1.5). Higher values zoom in more.
|
|
@@ -275,7 +398,11 @@ declare class CanvasTileEngine {
|
|
|
275
398
|
minY: number;
|
|
276
399
|
maxY: number;
|
|
277
400
|
};
|
|
278
|
-
/**
|
|
401
|
+
/**
|
|
402
|
+
* Set center coordinates from outside (adjusts the camera accordingly).
|
|
403
|
+
* @param newCenter The new center coordinates.
|
|
404
|
+
* @throws {ConfigValidationError} If coordinates are not finite numbers.
|
|
405
|
+
*/
|
|
279
406
|
updateCoords(newCenter: Coords): void;
|
|
280
407
|
/**
|
|
281
408
|
* Smoothly move the camera center to target coordinates over the given duration.
|
|
@@ -283,6 +410,7 @@ declare class CanvasTileEngine {
|
|
|
283
410
|
* @param y Target world y.
|
|
284
411
|
* @param durationMs Animation duration in milliseconds (default: 500ms). Set to 0 for instant move.
|
|
285
412
|
* @param onComplete Optional callback fired when animation completes.
|
|
413
|
+
* @throws {ConfigValidationError} If coordinates are not finite numbers.
|
|
286
414
|
*/
|
|
287
415
|
goCoords(x: number, y: number, durationMs?: number, onComplete?: () => void): void;
|
|
288
416
|
/**
|
|
@@ -335,7 +463,7 @@ declare class CanvasTileEngine {
|
|
|
335
463
|
* @param items Rectangle definitions.
|
|
336
464
|
* @param layer Layer order (lower draws first).
|
|
337
465
|
*/
|
|
338
|
-
drawRect(items:
|
|
466
|
+
drawRect(items: Rect | Array<Rect>, layer?: number): LayerHandle;
|
|
339
467
|
/**
|
|
340
468
|
* Draw rectangles with pre-rendering cache (canvas renderer only).
|
|
341
469
|
* Renders all items once to an offscreen canvas, then blits the visible portion each frame.
|
|
@@ -345,7 +473,7 @@ declare class CanvasTileEngine {
|
|
|
345
473
|
* @param cacheKey Unique key for this cache (e.g., "minimap-items").
|
|
346
474
|
* @param layer Layer order (lower draws first).
|
|
347
475
|
*/
|
|
348
|
-
drawStaticRect(items: Array<
|
|
476
|
+
drawStaticRect(items: Array<Rect>, cacheKey: string, layer?: number): LayerHandle;
|
|
349
477
|
/**
|
|
350
478
|
* Draw circles with pre-rendering cache (canvas renderer only).
|
|
351
479
|
* Renders all items once to an offscreen canvas, then blits the visible portion each frame.
|
|
@@ -354,7 +482,7 @@ declare class CanvasTileEngine {
|
|
|
354
482
|
* @param cacheKey Unique key for this cache (e.g., "minimap-circles").
|
|
355
483
|
* @param layer Layer order (lower draws first).
|
|
356
484
|
*/
|
|
357
|
-
drawStaticCircle(items: Array<
|
|
485
|
+
drawStaticCircle(items: Array<Circle>, cacheKey: string, layer?: number): LayerHandle;
|
|
358
486
|
/**
|
|
359
487
|
* Draw images with pre-rendering cache (canvas renderer only).
|
|
360
488
|
* Renders all items once to an offscreen canvas, then blits the visible portion each frame.
|
|
@@ -364,9 +492,7 @@ declare class CanvasTileEngine {
|
|
|
364
492
|
* @param cacheKey Unique key for this cache (e.g., "terrain-cache").
|
|
365
493
|
* @param layer Layer order (lower draws first).
|
|
366
494
|
*/
|
|
367
|
-
drawStaticImage(items: Array<
|
|
368
|
-
img: HTMLImageElement;
|
|
369
|
-
}>, cacheKey: string, layer?: number): LayerHandle;
|
|
495
|
+
drawStaticImage(items: Array<ImageItem>, cacheKey: string, layer?: number): LayerHandle;
|
|
370
496
|
/**
|
|
371
497
|
* Clear a static rendering cache.
|
|
372
498
|
* @param cacheKey The cache key to clear, or undefined to clear all caches.
|
|
@@ -378,13 +504,7 @@ declare class CanvasTileEngine {
|
|
|
378
504
|
* @param style Line style overrides.
|
|
379
505
|
* @param layer Layer order.
|
|
380
506
|
*/
|
|
381
|
-
drawLine(items: Array<{
|
|
382
|
-
from: Coords;
|
|
383
|
-
to: Coords;
|
|
384
|
-
}> | {
|
|
385
|
-
from: Coords;
|
|
386
|
-
to: Coords;
|
|
387
|
-
}, style?: {
|
|
507
|
+
drawLine(items: Array<Line> | Line, style?: {
|
|
388
508
|
strokeStyle?: string;
|
|
389
509
|
lineWidth?: number;
|
|
390
510
|
}, layer?: number): LayerHandle;
|
|
@@ -393,32 +513,36 @@ declare class CanvasTileEngine {
|
|
|
393
513
|
* @param items Circle definitions.
|
|
394
514
|
* @param layer Layer order.
|
|
395
515
|
*/
|
|
396
|
-
drawCircle(items:
|
|
516
|
+
drawCircle(items: Circle | Array<Circle>, layer?: number): LayerHandle;
|
|
397
517
|
/**
|
|
398
518
|
* Draw one or many texts at world positions (canvas renderer only).
|
|
399
|
-
* @param items Text definitions.
|
|
400
|
-
* @param style Text style overrides.
|
|
519
|
+
* @param items Text definitions with position, text, size, and style.
|
|
401
520
|
* @param layer Layer order.
|
|
521
|
+
* @example
|
|
522
|
+
* ```ts
|
|
523
|
+
* engine.drawText({
|
|
524
|
+
* x: 0,
|
|
525
|
+
* y: 0,
|
|
526
|
+
* text: "Hello",
|
|
527
|
+
* size: 1, // 1 tile height
|
|
528
|
+
* style: { fillStyle: "black", fontFamily: "Arial" }
|
|
529
|
+
* });
|
|
530
|
+
*
|
|
531
|
+
* // Multiple texts
|
|
532
|
+
* engine.drawText([
|
|
533
|
+
* { x: 0, y: 0, text: "A", size: 2 },
|
|
534
|
+
* { x: 1, y: 0, text: "B", size: 2 }
|
|
535
|
+
* ]);
|
|
536
|
+
* ```
|
|
402
537
|
*/
|
|
403
|
-
drawText(items: Array<
|
|
404
|
-
coords: Coords;
|
|
405
|
-
text: string;
|
|
406
|
-
}> | {
|
|
407
|
-
coords: Coords;
|
|
408
|
-
text: string;
|
|
409
|
-
}, style?: {
|
|
410
|
-
fillStyle?: string;
|
|
411
|
-
font?: string;
|
|
412
|
-
textAlign?: CanvasTextAlign;
|
|
413
|
-
textBaseline?: CanvasTextBaseline;
|
|
414
|
-
}, layer?: number): LayerHandle;
|
|
538
|
+
drawText(items: Array<Text> | Text, layer?: number): LayerHandle;
|
|
415
539
|
/**
|
|
416
540
|
* Draw one or many polylines through world points (canvas renderer only).
|
|
417
541
|
* @param items Polyline point collections.
|
|
418
542
|
* @param style Stroke style overrides.
|
|
419
543
|
* @param layer Layer order.
|
|
420
544
|
*/
|
|
421
|
-
drawPath(items: Array<
|
|
545
|
+
drawPath(items: Array<Path> | Path, style?: {
|
|
422
546
|
strokeStyle?: string;
|
|
423
547
|
lineWidth?: number;
|
|
424
548
|
}, layer?: number): LayerHandle;
|
|
@@ -428,11 +552,7 @@ declare class CanvasTileEngine {
|
|
|
428
552
|
* @param items Image definitions.
|
|
429
553
|
* @param layer Layer order.
|
|
430
554
|
*/
|
|
431
|
-
drawImage(items: Array<
|
|
432
|
-
img: HTMLImageElement;
|
|
433
|
-
}> | (Omit<DrawObject, "style"> & {
|
|
434
|
-
img: HTMLImageElement;
|
|
435
|
-
}), layer?: number): LayerHandle;
|
|
555
|
+
drawImage(items: Array<ImageItem> | ImageItem, layer?: number): LayerHandle;
|
|
436
556
|
/**
|
|
437
557
|
* Draw grid lines at specified cell size (canvas renderer only).
|
|
438
558
|
* @param cellSize Size of each grid cell in world units.
|
|
@@ -546,4 +666,24 @@ declare const VISIBILITY_BUFFER: {
|
|
|
546
666
|
readonly TILE_BUFFER: 1;
|
|
547
667
|
};
|
|
548
668
|
|
|
549
|
-
|
|
669
|
+
/**
|
|
670
|
+
* Convert grid-based dimensions to pixel-based config.
|
|
671
|
+
* @param options Grid configuration with columns, rows, and cell size.
|
|
672
|
+
* @returns Config object with size and scale properties.
|
|
673
|
+
* @example
|
|
674
|
+
* ```ts
|
|
675
|
+
* const config = {
|
|
676
|
+
* ...gridToSize({ columns: 12, rows: 12, cellSize: 50 }),
|
|
677
|
+
* backgroundColor: "#337426",
|
|
678
|
+
* };
|
|
679
|
+
* // config.size = { width: 600, height: 600 }
|
|
680
|
+
* // config.scale = 50
|
|
681
|
+
* ```
|
|
682
|
+
*/
|
|
683
|
+
declare function gridToSize(options: {
|
|
684
|
+
columns: number;
|
|
685
|
+
rows: number;
|
|
686
|
+
cellSize: number;
|
|
687
|
+
}): Pick<CanvasTileEngineConfig, "size" | "scale">;
|
|
688
|
+
|
|
689
|
+
export { COORDINATE_OVERLAY, CanvasTileEngine, type CanvasTileEngineConfig, type Circle, type Coords, DEBUG_HUD, DEFAULT_VALUES, type DrawObject, type EventHandlers, type ImageItem, type LayerHandle, type Line, type Path, RENDER_DEFAULTS, type Rect, SCALE_LIMITS, SIZE_LIMITS, type Text, VISIBILITY_BUFFER, gridToSize, type onClickCallback, type onDrawCallback, type onHoverCallback, type onMouseDownCallback, type onMouseLeaveCallback, type onMouseUpCallback, type onRightClickCallback, type onZoomCallback };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var ce=Object.create;var z=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var me=Object.getOwnPropertyNames;var ue=Object.getPrototypeOf,ve=Object.prototype.hasOwnProperty;var fe=(u,e)=>{for(var t in e)z(u,t,{get:e[t],enumerable:!0})},re=(u,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of me(e))!ve.call(u,i)&&i!==t&&z(u,i,{get:()=>e[i],enumerable:!(r=de(e,i))||r.enumerable});return u};var pe=(u,e,t)=>(t=u!=null?ce(ue(u)):{},re(e||!u||!u.__esModule?z(t,"default",{value:u,enumerable:!0}):t,u)),ge=u=>re(z({},"__esModule",{value:!0}),u);var be={};fe(be,{COORDINATE_OVERLAY:()=>T,CanvasTileEngine:()=>K,DEBUG_HUD:()=>E,DEFAULT_VALUES:()=>b,RENDER_DEFAULTS:()=>F,SCALE_LIMITS:()=>O,SIZE_LIMITS:()=>R,VISIBILITY_BUFFER:()=>S});module.exports=ge(be);var b={ANIMATION_DURATION_MS:500,CELL_CENTER_OFFSET:.5,IMAGE_LOAD_RETRY_COUNT:1,MAX_WHEEL_DELTA:100,MIN_WHEEL_DELTA:-100,ZOOM_SENSITIVITY:.001},O={MIN_SCALE_MULTIPLIER:.5,MAX_SCALE_MULTIPLIER:2},R={MIN_WIDTH:100,MIN_HEIGHT:100,MAX_WIDTH:1/0,MAX_HEIGHT:1/0},F={BACKGROUND_COLOR:"#ffffff",RENDERER_TYPE:"canvas"},T={BORDER_WIDTH:20,TEXT_OPACITY:.8,BORDER_OPACITY:.1,MIN_FONT_SIZE:8,MAX_FONT_SIZE:12,FONT_SIZE_SCALE_FACTOR:.25},E={PANEL_WIDTH:160,PADDING:8,LINE_HEIGHT:16},S={TILE_BUFFER:1};function ie(u,e,t,r){return{x:u.x-t/e,y:u.y-r/e}}function ne(u,e,t,r,i,s){let n=Math.min(Math.max(t,b.MIN_WHEEL_DELTA),b.MAX_WHEEL_DELTA),a=Math.exp(-n*b.ZOOM_SENSITIVITY),d=Math.min(i,Math.max(r,e*a));return d===e?{topLeft:u,scale:e}:{topLeft:{x:u.x+s.x*(1/e-1/d),y:u.y+s.y*(1/e-1/d)},scale:d}}function se(u,e){return{x:(u.x+b.CELL_CENTER_OFFSET-e.x)*e.scale,y:(u.y+b.CELL_CENTER_OFFSET-e.y)*e.scale}}function ae(u,e){return{x:e.x+u.x/e.scale,y:e.y+u.y/e.scale}}var D=class{_x;_y;_scale;minScale;maxScale;bounds;viewport;constructor(e,t=1,r=.1,i=10,s){this._x=e.x+b.CELL_CENTER_OFFSET,this._y=e.y+b.CELL_CENTER_OFFSET,this._scale=t,this.minScale=r,this.maxScale=i,this.viewport=s}setBounds(e){this.bounds=e,this.bounds&&this.clampToBounds()}clampToBounds(){if(!this.bounds||!this.viewport)return;let{width:e,height:t}=this.viewport.getSize(),r=e/this._scale,i=t/this._scale;this._x=this.clampAxis(this._x,r,this.bounds.minX,this.bounds.maxX),this._y=this.clampAxis(this._y,i,this.bounds.minY,this.bounds.maxY)}clampAxis(e,t,r,i){let s=i-r;return t>=s?r-(t-s)/2:e<r?r:e+t>i?i-t:e}get x(){return this._x}get y(){return this._y}get scale(){return this._scale}pan(e,t){let r=ie({x:this._x,y:this._y},this._scale,e,t);this._x=r.x,this._y=r.y,this.clampToBounds()}zoom(e,t,r,i){let s=e-i.left,n=t-i.top,a=ne({x:this._x,y:this._y},this._scale,r,this.minScale,this.maxScale,{x:s,y:n});this._x=a.topLeft.x,this._y=a.topLeft.y,this._scale=a.scale,this.clampToBounds()}zoomByFactor(e,t,r){let i=Math.min(this.maxScale,Math.max(this.minScale,this._scale*e));i!==this._scale&&(this._x=this._x+t*(1/this._scale-1/i),this._y=this._y+r*(1/this._scale-1/i),this._scale=i,this.clampToBounds())}getCenter(e,t){return{x:this._x+e/(2*this._scale)-.5,y:this._y+t/(2*this._scale)-.5}}setCenter(e,t,r){this._x=e.x-t/(2*this._scale)+.5,this._y=e.y-r/(2*this._scale)+.5,this.clampToBounds()}adjustForResize(e,t){this._x-=e/(2*this._scale),this._y-=t/(2*this._scale),this.clampToBounds()}getVisibleBounds(e,t){let r=this._x-b.CELL_CENTER_OFFSET,i=this._y-b.CELL_CENTER_OFFSET,s=r+e/this._scale,n=i+t/this._scale;return{minX:Math.floor(r),maxX:Math.ceil(s),minY:Math.floor(i),maxY:Math.ceil(n)}}};var W=class{config;constructor(e){let t={renderer:F.RENDERER_TYPE,scale:e.scale,minScale:e.minScale??e.scale*O.MIN_SCALE_MULTIPLIER,maxScale:e.maxScale??e.scale*O.MAX_SCALE_MULTIPLIER,gridAligned:e.gridAligned??!1,size:{width:e.size.width,height:e.size.height,maxHeight:e.size.maxHeight??R.MAX_HEIGHT,maxWidth:e.size.maxWidth??R.MAX_WIDTH,minHeight:e.size.minHeight??R.MIN_HEIGHT,minWidth:e.size.minWidth??R.MIN_WIDTH},backgroundColor:e.backgroundColor??F.BACKGROUND_COLOR,eventHandlers:{click:e.eventHandlers?.click??!1,rightClick:e.eventHandlers?.rightClick??!1,hover:e.eventHandlers?.hover??!1,drag:e.eventHandlers?.drag??!1,zoom:e.eventHandlers?.zoom??!1,resize:e.eventHandlers?.resize??!1},bounds:e.bounds??{minX:-1/0,maxX:1/0,minY:-1/0,maxY:1/0},coordinates:{enabled:e.coordinates?.enabled??!1,shownScaleRange:e.coordinates?.shownScaleRange??{min:0,max:1/0}},cursor:{default:e.cursor?.default??"default",move:e.cursor?.move??"move"},debug:{enabled:e.debug?.enabled??!1,hud:{enabled:e.debug?.hud?.enabled??!1,topLeftCoordinates:e.debug?.hud?.topLeftCoordinates??!1,coordinates:e.debug?.hud?.coordinates??!1,scale:e.debug?.hud?.scale??!1,tilesInView:e.debug?.hud?.tilesInView??!1,fps:e.debug?.hud?.fps??!1},eventHandlers:{click:e.debug?.eventHandlers?.click??!0,hover:e.debug?.eventHandlers?.hover??!0,drag:e.debug?.eventHandlers?.drag??!0,zoom:e.debug?.eventHandlers?.zoom??!0,resize:e.debug?.eventHandlers?.resize??!0}}};this.config={...t,size:Object.freeze(t.size),eventHandlers:Object.freeze(t.eventHandlers),bounds:Object.freeze(t.bounds),coordinates:Object.freeze({...t.coordinates,shownScaleRange:Object.freeze(t.coordinates.shownScaleRange)}),cursor:Object.freeze(t.cursor),debug:Object.freeze({enabled:t.debug.enabled,hud:Object.freeze(t.debug.hud),eventHandlers:Object.freeze(t.debug.eventHandlers)})}}get(){let e=this.config;return{...e,size:{...e.size},eventHandlers:{...e.eventHandlers},bounds:{...e.bounds},coordinates:{...e.coordinates,shownScaleRange:{min:e.coordinates.shownScaleRange?.min??0,max:e.coordinates.shownScaleRange?.max??1/0}},cursor:{...e.cursor},debug:{...e.debug,hud:{...e.debug.hud},eventHandlers:{...e.debug.eventHandlers}}}}updateEventHandlers(e){this.config={...this.config,eventHandlers:Object.freeze({...this.config.eventHandlers,...e})}}updateBounds(e){this.config={...this.config,bounds:Object.freeze(e)}}};var Y=class{constructor(e){this.camera=e}worldToScreen(e,t){return se({x:e,y:t},{x:this.camera.x,y:this.camera.y,scale:this.camera.scale})}screenToWorld(e,t){return ae({x:e,y:t},{x:this.camera.x,y:this.camera.y,scale:this.camera.scale})}};var oe=pe(require("rbush")),_=class u{tree;constructor(){this.tree=new oe.default}load(e){let t=e.map(r=>{let s=(typeof r.size=="number"?r.size:0)/2;return{minX:r.x-s,minY:r.y-s,maxX:r.x+s,maxY:r.y+s,item:r}});this.tree.load(t)}query(e,t,r,i){return this.tree.search({minX:e,minY:t,maxX:r,maxY:i}).map(n=>n.item)}clear(){this.tree.clear()}static fromArray(e){let t=new u;return t.load(e),t}};function H(u,e){if(e>=1)return u.lineWidth=e,()=>{};let t=Math.max(0,Math.min(e,1));return u.lineWidth=1,u.globalAlpha=t,()=>{u.globalAlpha=1}}var te=500,le=16384,P=class{constructor(e,t,r){this.layers=e;this.transformer=t;this.camera=r;this.staticCacheSupported=typeof OffscreenCanvas<"u"||typeof document<"u"}staticCaches=new Map;staticCacheSupported;warnedStaticCacheDisabled=!1;isVisible(e,t,r,i,s){let n=s.size.width/s.scale,a=s.size.height/s.scale,d=i.x-S.TILE_BUFFER,o=i.y-S.TILE_BUFFER,h=i.x+n+S.TILE_BUFFER,l=i.y+a+S.TILE_BUFFER;return e+r>=d&&e-r<=h&&t+r>=o&&t-r<=l}getViewportBounds(e,t){let r=t.size.width/t.scale,i=t.size.height/t.scale;return{minX:e.x-S.TILE_BUFFER,minY:e.y-S.TILE_BUFFER,maxX:e.x+r+S.TILE_BUFFER,maxY:e.y+i+S.TILE_BUFFER}}addDrawFunction(e,t=1){return this.layers.add(t,({ctx:r,config:i,topLeft:s})=>{e(r,s,i)})}drawRect(e,t=1){let r=Array.isArray(e)?e:[e],s=r.length>te?_.fromArray(r):null;return this.layers.add(t,({ctx:n,config:a,topLeft:d})=>{let o=this.getViewportBounds(d,a),h=s?s.query(o.minX,o.minY,o.maxX,o.maxY):r;n.save();let l,f,p;for(let c of h){let m=c.size??1,g={mode:c.origin?.mode==="self"?"self":"cell",x:c.origin?.x??.5,y:c.origin?.y??.5},v=c.style;if(!s&&!this.isVisible(c.x,c.y,m/2,d,a))continue;let y=this.transformer.worldToScreen(c.x,c.y),C=m*this.camera.scale,{x:w,y:x}=this.computeOriginOffset(y,C,g,this.camera);v?.fillStyle&&v.fillStyle!==l&&(n.fillStyle=v.fillStyle,l=v.fillStyle),v?.strokeStyle&&v.strokeStyle!==f&&(n.strokeStyle=v.strokeStyle,f=v.strokeStyle);let I;v?.lineWidth&&v.lineWidth!==p&&(I=H(n,v.lineWidth),p=v.lineWidth);let L=c.rotate??0,Q=L*(Math.PI/180),M=c.radius;if(L!==0){let ee=w+C/2,he=x+C/2;n.save(),n.translate(ee,he),n.rotate(Q),n.beginPath(),M&&n.roundRect?n.roundRect(-C/2,-C/2,C,C,M):n.rect(-C/2,-C/2,C,C),v?.fillStyle&&n.fill(),v?.strokeStyle&&n.stroke(),n.restore()}else n.beginPath(),M&&n.roundRect?n.roundRect(w,x,C,C,M):n.rect(w,x,C,C),v?.fillStyle&&n.fill(),v?.strokeStyle&&n.stroke();I?.()}n.restore()})}drawLine(e,t,r=1){let i=Array.isArray(e)?e:[e];return this.layers.add(r,({ctx:s,config:n,topLeft:a})=>{s.save(),t?.strokeStyle&&(s.strokeStyle=t.strokeStyle);let d=t?.lineWidth?H(s,t.lineWidth):void 0;s.beginPath();for(let o of i){let h=(o.from.x+o.to.x)/2,l=(o.from.y+o.to.y)/2,f=Math.max(Math.abs(o.from.x-o.to.x),Math.abs(o.from.y-o.to.y))/2;if(!this.isVisible(h,l,f,a,n))continue;let p=this.transformer.worldToScreen(o.from.x,o.from.y),c=this.transformer.worldToScreen(o.to.x,o.to.y);s.moveTo(p.x,p.y),s.lineTo(c.x,c.y)}s.stroke(),d?.(),s.restore()})}drawCircle(e,t=1){let r=Array.isArray(e)?e:[e],s=r.length>te?_.fromArray(r):null;return this.layers.add(t,({ctx:n,config:a,topLeft:d})=>{let o=this.getViewportBounds(d,a),h=s?s.query(o.minX,o.minY,o.maxX,o.maxY):r;n.save();let l,f,p;for(let c of h){let m=c.size??1,g={mode:c.origin?.mode==="self"?"self":"cell",x:c.origin?.x??.5,y:c.origin?.y??.5},v=c.style;if(!s&&!this.isVisible(c.x,c.y,m/2,d,a))continue;let y=this.transformer.worldToScreen(c.x,c.y),C=m*this.camera.scale,w=C/2,{x,y:I}=this.computeOriginOffset(y,C,g,this.camera);v?.fillStyle&&v.fillStyle!==l&&(n.fillStyle=v.fillStyle,l=v.fillStyle),v?.strokeStyle&&v.strokeStyle!==f&&(n.strokeStyle=v.strokeStyle,f=v.strokeStyle);let L;v?.lineWidth&&v.lineWidth!==p&&(L=H(n,v.lineWidth),p=v.lineWidth),n.beginPath(),n.arc(x+w,I+w,w,0,Math.PI*2),v?.fillStyle&&n.fill(),v?.strokeStyle&&n.stroke(),L?.()}n.restore()})}drawText(e,t,r=2){let i=Array.isArray(e)?e:[e];return this.layers.add(r,({ctx:s,config:n,topLeft:a})=>{s.save(),t?.font&&(s.font=t.font),t?.fillStyle&&(s.fillStyle=t.fillStyle),s.textAlign=t?.textAlign??"center",s.textBaseline=t?.textBaseline??"middle";for(let d of i){if(!this.isVisible(d.coords.x,d.coords.y,0,a,n))continue;let o=this.transformer.worldToScreen(d.coords.x,d.coords.y);s.fillText(d.text,o.x,o.y)}s.restore()})}drawPath(e,t,r=1){let i=Array.isArray(e[0])?e:[e];return this.layers.add(r,({ctx:s,config:n,topLeft:a})=>{s.save(),t?.strokeStyle&&(s.strokeStyle=t.strokeStyle);let d=t?.lineWidth?H(s,t.lineWidth):void 0;s.beginPath();for(let o of i){if(!o.length)continue;let h=o.map(w=>w.x),l=o.map(w=>w.y),f=Math.min(...h),p=Math.max(...h),c=Math.min(...l),m=Math.max(...l),g=(f+p)/2,v=(c+m)/2,y=Math.max(p-f,m-c)/2;if(!this.isVisible(g,v,y,a,n))continue;let C=this.transformer.worldToScreen(o[0].x,o[0].y);s.moveTo(C.x,C.y);for(let w=1;w<o.length;w++){let x=this.transformer.worldToScreen(o[w].x,o[w].y);s.lineTo(x.x,x.y)}}s.stroke(),d?.(),s.restore()})}drawImage(e,t=1){let r=Array.isArray(e)?e:[e],s=r.length>te?_.fromArray(r):null;return this.layers.add(t,({ctx:n,config:a,topLeft:d})=>{let o=this.getViewportBounds(d,a),h=s?s.query(o.minX,o.minY,o.maxX,o.maxY):r;for(let l of h){let f=l.size??1,p={mode:l.origin?.mode==="self"?"self":"cell",x:l.origin?.x??.5,y:l.origin?.y??.5};if(!s&&!this.isVisible(l.x,l.y,f/2,d,a))continue;let c=this.transformer.worldToScreen(l.x,l.y),m=f*this.camera.scale,g=l.img.width/l.img.height,v=m,y=m;g>1?y=m/g:v=m*g;let{x:C,y:w}=this.computeOriginOffset(c,m,p,this.camera),x=C+(m-v)/2,I=w+(m-y)/2,L=l.rotate??0,Q=L*(Math.PI/180);if(L!==0){let M=x+v/2,ee=I+y/2;n.save(),n.translate(M,ee),n.rotate(Q),n.drawImage(l.img,-v/2,-y/2,v,y),n.restore()}else n.drawImage(l.img,x,I,v,y)}})}drawGridLines(e,t,r=0){return this.layers.add(r,({ctx:i,config:s,topLeft:n})=>{let a=s.size.width/s.scale,d=s.size.height/s.scale,o=Math.floor(n.x/e)*e-b.CELL_CENTER_OFFSET,h=Math.ceil((n.x+a)/e)*e-b.CELL_CENTER_OFFSET,l=Math.floor(n.y/e)*e-b.CELL_CENTER_OFFSET,f=Math.ceil((n.y+d)/e)*e-b.CELL_CENTER_OFFSET;i.save(),i.strokeStyle=t.strokeStyle;let p=H(i,t.lineWidth);i.beginPath();for(let c=o;c<=h;c+=e){let m=this.transformer.worldToScreen(c,l),g=this.transformer.worldToScreen(c,f);i.moveTo(m.x,m.y),i.lineTo(g.x,g.y)}for(let c=l;c<=f;c+=e){let m=this.transformer.worldToScreen(o,c),g=this.transformer.worldToScreen(h,c);i.moveTo(m.x,m.y),i.lineTo(g.x,g.y)}i.stroke(),p(),i.restore()})}computeOriginOffset(e,t,r,i){if(r.mode==="cell"){let s=i.scale;return{x:e.x-s/2+r.x*s-t/2,y:e.y-s/2+r.y*s-t/2}}return{x:e.x-r.x*t,y:e.y-r.y*t}}getOrCreateStaticCache(e,t,r){if(!this.staticCacheSupported)return this.warnedStaticCacheDisabled||(console.warn("[CanvasDraw] Static cache disabled: OffscreenCanvas not available."),this.warnedStaticCacheDisabled=!0),null;let i=1/0,s=-1/0,n=1/0,a=-1/0;for(let m of e){let g=m.size??1;m.x-g/2<i&&(i=m.x-g/2),m.x+g/2>s&&(s=m.x+g/2),m.y-g/2<n&&(n=m.y-g/2),m.y+g/2>a&&(a=m.y+g/2)}i-=1,n-=1,s+=1,a+=1;let d=s-i,o=a-n,h=this.camera.scale,l=Math.ceil(d*h),f=Math.ceil(o*h);if(l>le||f>le)return this.warnedStaticCacheDisabled||(console.warn(`Static cache disabled: offscreen canvas too large (${l}x${f}).`),this.warnedStaticCacheDisabled=!0),null;let p=this.staticCaches.get(t);if(!p||p.scale!==h||p.worldBounds.minX!==i||p.worldBounds.maxX!==s||p.worldBounds.minY!==n||p.worldBounds.maxY!==a){let m=typeof OffscreenCanvas<"u"?new OffscreenCanvas(l,f):document.createElement("canvas");typeof OffscreenCanvas<"u"&&m instanceof OffscreenCanvas||(m.width=l,m.height=f);let v=m.getContext("2d");if(!v)return this.warnedStaticCacheDisabled||(console.warn("[CanvasDraw] Static cache disabled: 2D context unavailable."),this.warnedStaticCacheDisabled=!0),null;for(let y of e){let w=(y.size??1)*h,x=(y.x+b.CELL_CENTER_OFFSET-i)*h-w/2,I=(y.y+b.CELL_CENTER_OFFSET-n)*h-w/2;r(v,y,x,I,w)}p={canvas:m,ctx:v,worldBounds:{minX:i,minY:n,maxX:s,maxY:a},scale:h},this.staticCaches.set(t,p)}return p||null}addStaticCacheLayer(e,t){if(!e)return null;let r=e.canvas,i=e.worldBounds,s=e.scale;return this.layers.add(t,({ctx:n,config:a,topLeft:d})=>{let o=a.size.width/a.scale,h=a.size.height/a.scale,l=(d.x-i.minX)*s,f=(d.y-i.minY)*s,p=o*s,c=h*s;n.drawImage(r,l,f,p,c,0,0,a.size.width,a.size.height)})}drawStaticRect(e,t,r=1){let i,s=this.getOrCreateStaticCache(e,t,(n,a,d,o,h)=>{let l=a.style,f=a.rotate??0,p=f*(Math.PI/180),c=a.radius;if(l?.fillStyle&&l.fillStyle!==i&&(n.fillStyle=l.fillStyle,i=l.fillStyle),f!==0){let m=d+h/2,g=o+h/2;n.save(),n.translate(m,g),n.rotate(p),c&&n.roundRect?(n.beginPath(),n.roundRect(-h/2,-h/2,h,h,c),n.fill()):n.fillRect(-h/2,-h/2,h,h),n.restore()}else c&&n.roundRect?(n.beginPath(),n.roundRect(d,o,h,h,c),n.fill()):n.fillRect(d,o,h,h)});return s?this.addStaticCacheLayer(s,r):this.drawRect(e,r)}drawStaticImage(e,t,r=1){let i=this.getOrCreateStaticCache(e,t,(s,n,a,d,o)=>{let h=n.img,l=n.rotate??0,f=l*(Math.PI/180),p=h.width/h.height,c=o,m=o;p>1?m=o/p:c=o*p;let g=a+(o-c)/2,v=d+(o-m)/2;if(l!==0){let y=g+c/2,C=v+m/2;s.save(),s.translate(y,C),s.rotate(f),s.drawImage(h,-c/2,-m/2,c,m),s.restore()}else s.drawImage(h,g,v,c,m)});return i?this.addStaticCacheLayer(i,r):this.drawImage(e,r)}drawStaticCircle(e,t,r=1){let i,s=this.getOrCreateStaticCache(e,t,(n,a,d,o,h)=>{let l=a.style,f=h/2;l?.fillStyle&&l.fillStyle!==i&&(n.fillStyle=l.fillStyle,i=l.fillStyle),n.beginPath(),n.arc(d+f,o+f,f,0,Math.PI*2),n.fill()});return s?this.addStaticCacheLayer(s,r):this.drawCircle(e,r)}clearStaticCache(e){e?this.staticCaches.delete(e):this.staticCaches.clear()}destroy(){this.staticCaches.clear(),this.layers.clear()}};var X=class{constructor(e,t){this.canvas=e;this.handlers=t}attach(){this.handlers.click&&this.canvas.addEventListener("click",this.handlers.click),this.handlers.contextmenu&&this.canvas.addEventListener("contextmenu",this.handlers.contextmenu),this.handlers.mousedown&&this.canvas.addEventListener("mousedown",this.handlers.mousedown),this.handlers.mousemove&&this.canvas.addEventListener("mousemove",this.handlers.mousemove),this.handlers.mouseup&&this.canvas.addEventListener("mouseup",this.handlers.mouseup),this.handlers.mouseleave&&this.canvas.addEventListener("mouseleave",this.handlers.mouseleave),this.handlers.wheel&&this.canvas.addEventListener("wheel",this.handlers.wheel,{passive:!1}),this.handlers.touchstart&&this.canvas.addEventListener("touchstart",this.handlers.touchstart,{passive:!1}),this.handlers.touchmove&&this.canvas.addEventListener("touchmove",this.handlers.touchmove,{passive:!1}),this.handlers.touchend&&this.canvas.addEventListener("touchend",this.handlers.touchend,{passive:!1})}detach(){this.handlers.click&&this.canvas.removeEventListener("click",this.handlers.click),this.handlers.contextmenu&&this.canvas.removeEventListener("contextmenu",this.handlers.contextmenu),this.handlers.mousedown&&this.canvas.removeEventListener("mousedown",this.handlers.mousedown),this.handlers.mousemove&&this.canvas.removeEventListener("mousemove",this.handlers.mousemove),this.handlers.mouseup&&this.canvas.removeEventListener("mouseup",this.handlers.mouseup),this.handlers.mouseleave&&this.canvas.removeEventListener("mouseleave",this.handlers.mouseleave),this.handlers.wheel&&this.canvas.removeEventListener("wheel",this.handlers.wheel),this.handlers.touchstart&&this.canvas.removeEventListener("touchstart",this.handlers.touchstart),this.handlers.touchmove&&this.canvas.removeEventListener("touchmove",this.handlers.touchmove),this.handlers.touchend&&this.canvas.removeEventListener("touchend",this.handlers.touchend)}};var B=class{constructor(e,t,r,i,s,n){this.canvas=e;this.camera=t;this.viewport=r;this.config=i;this.transformer=s;this.onCameraChange=n}isDragging=!1;shouldPreventClick=!1;lastPos={x:0,y:0};isPinching=!1;lastPinchDistance=0;lastPinchCenter={x:0,y:0};onClick;onRightClick;onHover;onMouseDown;onMouseUp;onMouseLeave;onZoom;getEventCoords=e=>{let t=this.canvas.getBoundingClientRect(),r=e.clientX-t.left,i=e.clientY-t.top,s=this.transformer.screenToWorld(r,i),n=this.transformer.worldToScreen(Math.floor(s.x),Math.floor(s.y));return{coords:{raw:s,snapped:{x:Math.floor(s.x),y:Math.floor(s.y)}},mouse:{raw:{x:r,y:i},snapped:{x:n.x,y:n.y}},client:{raw:{x:e.clientX,y:e.clientY},snapped:{x:n.x+t.left,y:n.y+t.top}}}};handleClick=e=>{if(this.shouldPreventClick){this.shouldPreventClick=!1;return}if(!this.config.get().eventHandlers.click||!this.onClick)return;let{coords:t,mouse:r,client:i}=this.getEventCoords(e);this.onClick(t,r,i)};handleContextMenu=e=>{if(!this.config.get().eventHandlers.rightClick||!this.onRightClick)return;e.preventDefault();let{coords:t,mouse:r,client:i}=this.getEventCoords(e);this.onRightClick(t,r,i)};handleMouseDown=e=>{if(this.onMouseDown){let{coords:t,mouse:r,client:i}=this.getEventCoords(e);this.onMouseDown(t,r,i)}this.config.get().eventHandlers.drag&&(this.isDragging=!0,this.shouldPreventClick=!1,this.lastPos={x:e.clientX,y:e.clientY})};handleMouseMove=e=>{if(!this.isDragging){if(this.onHover&&this.config.get().eventHandlers.hover){let{coords:i,mouse:s,client:n}=this.getEventCoords(e);this.onHover(i,s,n)}return}let t=e.clientX-this.lastPos.x,r=e.clientY-this.lastPos.y;(t!==0||r!==0)&&(this.canvas.style.cursor=this.config.get().cursor.move||"move",this.shouldPreventClick=!0),this.camera.pan(t,r),this.lastPos={x:e.clientX,y:e.clientY},this.onCameraChange()};handleMouseUp=e=>{if(this.onMouseUp){let{coords:t,mouse:r,client:i}=this.getEventCoords(e);this.onMouseUp(t,r,i)}this.isDragging=!1,this.canvas.style.cursor=this.config.get().cursor.default||"default"};handleMouseLeave=e=>{if(this.onMouseLeave){let{coords:t,mouse:r,client:i}=this.getEventCoords(e);this.onMouseLeave(t,r,i)}this.isDragging=!1,this.canvas.style.cursor=this.config.get().cursor.default||"default"};handleTouchStart=e=>{let t=this.config.get().eventHandlers;if(e.touches.length===2&&t.zoom){e.preventDefault(),this.isPinching=!0,this.isDragging=!1,this.lastPinchDistance=this.getTouchDistance(e.touches),this.lastPinchCenter=this.getTouchCenter(e.touches);return}if(!t.drag||e.touches.length!==1)return;let r=e.touches[0];this.isDragging=!0,this.isPinching=!1,this.shouldPreventClick=!1,this.lastPos={x:r.clientX,y:r.clientY}};handleTouchMove=e=>{if(this.isPinching&&e.touches.length===2){e.preventDefault();let s=this.getTouchDistance(e.touches),n=this.getTouchCenter(e.touches),a=this.canvas.getBoundingClientRect(),d=s/this.lastPinchDistance,o=n.x-a.left,h=n.y-a.top;this.camera.zoomByFactor(d,o,h);let l=n.x-this.lastPinchCenter.x,f=n.y-this.lastPinchCenter.y;(l!==0||f!==0)&&this.camera.pan(l,f),this.lastPinchDistance=s,this.lastPinchCenter=n,this.onZoom&&this.onZoom(this.camera.scale),this.onCameraChange();return}if(!this.isDragging||e.touches.length!==1)return;e.preventDefault();let t=e.touches[0],r=t.clientX-this.lastPos.x,i=t.clientY-this.lastPos.y;(r!==0||i!==0)&&(this.canvas.style.cursor=this.config.get().cursor.move||"move",this.shouldPreventClick=!0),this.camera.pan(r,i),this.lastPos={x:t.clientX,y:t.clientY},this.onCameraChange()};handleTouchEnd=e=>{if(e.touches.length>=2&&this.isPinching){this.lastPinchDistance=this.getTouchDistance(e.touches),this.lastPinchCenter=this.getTouchCenter(e.touches);return}if(e.touches.length===1&&this.isPinching){if(this.isPinching=!1,this.config.get().eventHandlers.drag){this.isDragging=!0;let t=e.touches[0];this.lastPos={x:t.clientX,y:t.clientY}}return}this.isDragging=!1,this.isPinching=!1,this.canvas.style.cursor=this.config.get().cursor.default||"default"};getTouchDistance(e){let t=e[1].clientX-e[0].clientX,r=e[1].clientY-e[0].clientY;return Math.sqrt(t*t+r*r)}getTouchCenter(e){return{x:(e[0].clientX+e[1].clientX)/2,y:(e[0].clientY+e[1].clientY)/2}}handleWheel=e=>{if(!this.config.get().eventHandlers.zoom)return;e.preventDefault();let t=this.canvas.getBoundingClientRect();this.camera.zoom(e.clientX,e.clientY,e.deltaY,t),this.onZoom&&this.onZoom(this.camera.scale),this.onCameraChange()}};var U=class{constructor(e,t,r,i,s,n){this.wrapper=e;this.canvas=t;this.viewport=r;this.camera=i;this.config=s;this.onCameraChange=n;this.currentDpr=this.viewport.dpr}resizeObserver;handleWindowResize;currentDpr;onResize;start(){this.viewport.updateDpr(),this.currentDpr=this.viewport.dpr;let e=this.viewport.getSize(),t=this.config.get().size,r=t?.maxWidth,i=t?.maxHeight,s=t?.minWidth,n=t?.minHeight;e.width=this.clamp(e.width,s,r),e.height=this.clamp(e.height,n,i),Object.assign(this.wrapper.style,{resize:"both",overflow:"hidden",width:`${e.width}px`,height:`${e.height}px`,touchAction:"none",position:"relative",maxWidth:r?`${r}px`:"",maxHeight:i?`${i}px`:"",minWidth:s?`${s}px`:"",minHeight:n?`${n}px`:""}),this.resizeObserver=new ResizeObserver(a=>{for(let d of a){let{width:o,height:h}=d.contentRect,l=this.clamp(o,s,r),f=this.clamp(h,n,i),p=this.viewport.getSize();if(l===p.width&&f===p.height)continue;let c=l-p.width,m=f-p.height,g=this.viewport.dpr;this.camera.adjustForResize(c,m),this.viewport.setSize(l,f),this.canvas.width=l*g,this.canvas.height=f*g,this.canvas.style.width=`${l}px`,this.canvas.style.height=`${f}px`,this.wrapper.style.width=`${l}px`,this.wrapper.style.height=`${f}px`,this.onResize&&this.onResize(),this.onCameraChange()}}),this.resizeObserver.observe(this.wrapper),this.attachDprWatcher()}stop(){this.resizeObserver&&(this.resizeObserver.unobserve(this.wrapper),this.resizeObserver.disconnect()),this.resizeObserver=void 0,this.handleWindowResize&&(window.removeEventListener("resize",this.handleWindowResize),this.handleWindowResize=void 0)}clamp(e,t,r){let i=e;return t!==void 0&&(i=Math.max(t,i)),r!==void 0&&(i=Math.min(r,i)),i}attachDprWatcher(){typeof window>"u"||(this.handleWindowResize=()=>{let e=this.currentDpr;this.viewport.updateDpr();let t=this.viewport.dpr;if(t===e)return;this.currentDpr=t;let{width:r,height:i}=this.viewport.getSize();this.canvas.width=r*t,this.canvas.height=i*t,this.canvas.style.width=`${r}px`,this.canvas.style.height=`${i}px`,this.onResize&&this.onResize(),this.onCameraChange()},window.addEventListener("resize",this.handleWindowResize,{passive:!0}))}};var N=class{constructor(e,t,r,i,s,n,a){this.canvasWrapper=e;this.canvas=t;this.camera=r;this.viewport=i;this.config=s;this.coordinateTransformer=n;this.onCameraChange=a;this.gestures=new B(this.canvas,this.camera,this.viewport,this.config,this.coordinateTransformer,this.onCameraChange),this.binder=new X(this.canvas,{click:this.gestures.handleClick,contextmenu:this.gestures.handleContextMenu,mousedown:this.gestures.handleMouseDown,mousemove:this.gestures.handleMouseMove,mouseup:this.gestures.handleMouseUp,mouseleave:this.gestures.handleMouseLeave,wheel:this.gestures.handleWheel,touchstart:this.gestures.handleTouchStart,touchmove:this.gestures.handleTouchMove,touchend:this.gestures.handleTouchEnd})}binder;gestures;resizeWatcher;attached=!1;onResize;get onClick(){return this.gestures.onClick}set onClick(e){this.gestures.onClick=e}get onRightClick(){return this.gestures.onRightClick}set onRightClick(e){this.gestures.onRightClick=e}get onHover(){return this.gestures.onHover}set onHover(e){this.gestures.onHover=e}get onMouseDown(){return this.gestures.onMouseDown}set onMouseDown(e){this.gestures.onMouseDown=e}get onMouseUp(){return this.gestures.onMouseUp}set onMouseUp(e){this.gestures.onMouseUp=e}get onMouseLeave(){return this.gestures.onMouseLeave}set onMouseLeave(e){this.gestures.onMouseLeave=e}get onZoom(){return this.gestures.onZoom}set onZoom(e){this.gestures.onZoom=e}setupEvents(){this.attached||(this.binder.attach(),this.attached=!0,this.config.get().eventHandlers.resize&&this.camera instanceof D&&(this.resizeWatcher=new U(this.canvasWrapper,this.canvas,this.viewport,this.camera,this.config,this.onCameraChange),this.resizeWatcher.onResize=()=>{this.onResize&&this.onResize()},this.resizeWatcher.start()))}destroy(){this.attached&&(this.binder.detach(),this.resizeWatcher?.stop(),this.resizeWatcher=void 0,this.attached=!1)}};var V=class{cache=new Map;inflight=new Map;listeners=new Set;onLoad(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notifyLoaded(){for(let e of this.listeners)e()}async load(e,t=b.IMAGE_LOAD_RETRY_COUNT){if(this.cache.has(e))return this.cache.get(e);if(this.inflight.has(e))return this.inflight.get(e);let r=new Promise((i,s)=>{let n=new Image;n.crossOrigin="anonymous",n.decoding="async",n.loading="eager",n.onload=async()=>{try{"decode"in n&&await n.decode?.()}catch{}this.cache.set(e,n),this.inflight.delete(e),this.notifyLoaded(),i(n)},n.onerror=a=>{if(this.inflight.delete(e),t>0)console.warn(`Retrying image: ${e}`),i(this.load(e,t-1));else{console.error(`Image failed to load: ${e}`,a);let d=a instanceof Error?a.message:typeof a=="string"?a:JSON.stringify(a);s(new Error(`Image failed to load: ${e}. Reason: ${d}`))}},n.src=e});return this.inflight.set(e,r),r}get(e){return this.cache.get(e)}has(e){return this.cache.has(e)}clear(){this.cache.clear(),this.inflight.clear(),this.listeners.clear()}};var $=class{layers=new Map;add(e,t){let r=Symbol("layer-callback"),i={id:r,fn:t};return this.layers.has(e)||this.layers.set(e,[]),this.layers.get(e).push(i),{layer:e,id:r}}remove(e){let t=this.layers.get(e.layer);t&&this.layers.set(e.layer,t.filter(r=>r.id!==e.id))}clear(e){if(e===void 0){this.layers.clear();return}this.layers.set(e,[])}drawAll(e){let t=[...this.layers.keys()].sort((r,i)=>r-i);for(let r of t){let i=this.layers.get(r);if(i)for(let{fn:s}of i)e.ctx.save(),s(e),e.ctx.restore()}}};var Z=class{width;height;_dpr;constructor(e,t){this.width=e,this.height=t,this._dpr=typeof window<"u"&&window.devicePixelRatio||1}getSize(){return{width:this.width,height:this.height}}setSize(e,t){this.width=e,this.height=t}get dpr(){return this._dpr}updateDpr(){this._dpr=typeof window<"u"&&window.devicePixelRatio||1}};var j=class{ctx;camera;config;viewport;constructor(e,t,r,i){this.ctx=e,this.camera=t,this.config=r,this.viewport=i}draw(){this.ctx.save(),this.ctx.fillStyle=`rgba(0, 0, 0, ${T.BORDER_OPACITY})`;let{width:e,height:t}=this.viewport.getSize();this.ctx.fillRect(0,0,T.BORDER_WIDTH,t),this.ctx.fillRect(T.BORDER_WIDTH,t-T.BORDER_WIDTH,e,T.BORDER_WIDTH),this.ctx.fillStyle=`rgba(255, 255, 255, ${T.TEXT_OPACITY})`;let r=Math.min(T.MAX_FONT_SIZE,Math.max(T.MIN_FONT_SIZE,this.camera.scale*T.FONT_SIZE_SCALE_FACTOR));this.ctx.font=`${r}px Arial`,this.ctx.textAlign="center",this.ctx.textBaseline="middle";let i=this.camera.scale,s=e/i,n=t/i;for(let a=0-this.camera.y%1;a<=n+1;a++)this.ctx.fillText(Math.round(this.camera.y+a).toString(),10,i*a+i/2);for(let a=0-this.camera.x%1;a<=s+1;a++)this.ctx.fillText(Math.round(this.camera.x+a).toString(),i*a+i/2,t-10);this.ctx.restore()}shouldDraw(e){let t=this.config.get().coordinates;if(!t.enabled||!t.shownScaleRange)return!1;let{min:r,max:i}=t.shownScaleRange;return e>=r&&e<=i}};var Ce=10,A=class{ctx;camera;transformer;config;viewport;frameTimes=[];lastFrameTime=0;currentFps=0;fpsLoopRunning=!1;onFpsUpdate=null;constructor(e,t,r,i,s){this.ctx=e,this.camera=t,this.transformer=r,this.config=i,this.viewport=s}setFpsUpdateCallback(e){this.onFpsUpdate=e}startFpsLoop(){this.fpsLoopRunning||(this.fpsLoopRunning=!0,this.lastFrameTime=performance.now(),this.fpsLoop())}stopFpsLoop(){this.fpsLoopRunning=!1}fpsLoop(){if(!this.fpsLoopRunning)return;let e=performance.now(),t=e-this.lastFrameTime;this.lastFrameTime=e,this.frameTimes.push(t),this.frameTimes.length>Ce&&this.frameTimes.shift();let r=this.frameTimes.reduce((s,n)=>s+n,0)/this.frameTimes.length,i=Math.round(1e3/r);i!==this.currentFps&&(this.currentFps=i,this.onFpsUpdate?.()),requestAnimationFrame(()=>this.fpsLoop())}draw(){this.drawHud()}destroy(){this.stopFpsLoop(),this.onFpsUpdate=null}drawHud(){let e=this.config.get();if(!e.debug.hud||!e.debug.hud.enabled)return;let t=[],r={x:this.camera.x,y:this.camera.y};if(e.debug.hud.topLeftCoordinates&&t.push(`TopLeft: ${r.x.toFixed(2)}, ${r.y.toFixed(2)}`),e.debug.hud.coordinates){let{width:s,height:n}=this.viewport.getSize(),a=this.camera.getCenter(s,n);t.push(`Coords: ${a.x.toFixed(2)}, ${a.y.toFixed(2)}`)}if(e.debug.hud.scale&&t.push(`Scale: ${this.camera.scale.toFixed(2)}`),e.debug.hud.tilesInView){let{width:s,height:n}=this.viewport.getSize();t.push(`Tiles in view: ${Math.ceil(s/this.camera.scale)} x ${Math.ceil(n/this.camera.scale)}`)}e.debug.hud.fps&&t.push(`FPS: ${this.currentFps}`);let{width:i}=this.viewport.getSize();this.ctx.save(),this.ctx.fillStyle="rgba(0,0,0,0.5)",this.ctx.fillRect(i-E.PANEL_WIDTH-E.PADDING,E.PADDING/2,E.PANEL_WIDTH,t.length*E.LINE_HEIGHT+E.PADDING),this.ctx.fillStyle="#00ff99",this.ctx.font="12px monospace";for(let s=0;s<t.length;s++)this.ctx.fillText(t[s],i-E.PANEL_WIDTH-E.PADDING+5,18+s*E.LINE_HEIGHT);this.ctx.restore()}};var k=class{constructor(e,t,r,i,s,n){this.canvas=e;this.camera=t;this.coordinateTransformer=r;this.config=i;this.viewport=s;this.layers=n;let a=e.getContext("2d");if(!a)throw new Error("Failed to get 2D canvas context");this.ctx=a,this.applyCanvasSize(),this.coordinateOverlayRenderer=new j(this.ctx,this.camera,this.config,this.viewport),this.config.get().debug?.enabled&&(this.debugOverlay=new A(this.ctx,this.camera,this.coordinateTransformer,this.config,this.viewport),this.config.get().debug?.hud?.fps&&(this.debugOverlay.setFpsUpdateCallback(()=>this.render()),this.debugOverlay.startFpsLoop()))}ctx;coordinateOverlayRenderer;debugOverlay;onDraw;init(){this.applyCanvasSize()}applyCanvasSize(){let e=this.viewport.getSize(),t=this.viewport.dpr;this.canvas.width=e.width*t,this.canvas.height=e.height*t,this.canvas.style.width=`${e.width}px`,this.canvas.style.height=`${e.height}px`,this.ctx.setTransform(t,0,0,t,0,0)}render(){let e=this.viewport.getSize(),t=this.viewport.dpr,r={...this.config.get(),size:{...e},scale:this.camera.scale},i={x:this.camera.x,y:this.camera.y};this.ctx.setTransform(t,0,0,t,0,0),this.ctx.clearRect(0,0,r.size.width,r.size.height),this.ctx.fillStyle=r.backgroundColor,this.ctx.fillRect(0,0,r.size.width,r.size.height),this.layers.drawAll({ctx:this.ctx,camera:this.camera,transformer:this.coordinateTransformer,config:r,topLeft:i}),this.onDraw?.(this.ctx,{scale:this.camera.scale,width:r.size.width,height:r.size.height,coords:i}),this.coordinateOverlayRenderer.shouldDraw(this.camera.scale)&&this.coordinateOverlayRenderer.draw(),r.debug?.enabled&&(this.debugOverlay||(this.debugOverlay=new A(this.ctx,this.camera,this.coordinateTransformer,this.config,this.viewport),r.debug?.hud?.fps&&(this.debugOverlay.setFpsUpdateCallback(()=>this.render()),this.debugOverlay.startFpsLoop())),this.debugOverlay.draw())}resize(e,t){let r=this.viewport.dpr;this.viewport.setSize(e,t),this.canvas.width=e*r,this.canvas.height=t*r,this.canvas.style.width=`${e}px`,this.canvas.style.height=`${t}px`,this.ctx.setTransform(r,0,0,r,0,0)}destroy(){this.debugOverlay&&(this.debugOverlay.destroy(),this.debugOverlay=void 0),this.layers.clear()}getContext(){return this.ctx}};var G=class{canvasWrapper;canvas;camera;viewport;renderer;config;onSizeApplied;constructor(e,t,r,i,s,n,a){this.canvasWrapper=e,this.canvas=t,this.camera=r,this.renderer=i,this.viewport=s,this.config=n,this.onSizeApplied=a}resizeWithAnimation(e,t,r,i,s){if(e<=0||t<=0)return;let n=this.config.get().size,a=(d,o,h)=>{let l=d;return o!==void 0&&(l=Math.max(o,l)),h!==void 0&&(l=Math.min(h,l)),l};e=a(e,n?.minWidth,n?.maxWidth),t=a(t,n?.minHeight,n?.maxHeight),i.animateResize(e,t,r,(d,o,h)=>this.applySize(d,o,h),s)}applySize(e,t,r){let i=Math.round(e),s=Math.round(t),n=this.viewport.dpr;this.viewport.setSize(i,s),this.canvasWrapper.style.width=`${i}px`,this.canvasWrapper.style.height=`${s}px`,this.canvas.width=i*n,this.canvas.height=s*n,this.canvas.style.width=`${i}px`,this.canvas.style.height=`${s}px`,this.camera.setCenter(r,i,s),this.renderer.resize(i,s),this.onSizeApplied()}};var q=class{constructor(e,t,r){this.camera=e;this.viewport=t;this.onAnimationFrame=r}moveAnimationId;resizeAnimationId;animateMoveTo(e,t,r=b.ANIMATION_DURATION_MS,i){if(this.cancelMove(),r<=0){let o=this.viewport.getSize();this.camera.setCenter({x:e,y:t},o.width,o.height),this.onAnimationFrame(),i?.();return}let s=this.viewport.getSize(),n=this.camera.getCenter(s.width,s.height),a=performance.now(),d=o=>{let h=o-a,l=Math.min(1,h/r),f=l<.5?2*l*l:1-Math.pow(-2*l+2,2)/2,p=n.x+(e-n.x)*f,c=n.y+(t-n.y)*f,m=this.viewport.getSize();this.camera.setCenter({x:p,y:c},m.width,m.height),this.onAnimationFrame(),l<1?this.moveAnimationId=requestAnimationFrame(d):(this.moveAnimationId=void 0,i?.())};this.moveAnimationId=requestAnimationFrame(d)}animateResize(e,t,r=b.ANIMATION_DURATION_MS,i,s){if(e<=0||t<=0)return;this.cancelResize();let n=this.viewport.getSize(),a=this.camera.getCenter(n.width,n.height);if(r<=0){i(e,t,a),s?.();return}let d=n.width,o=n.height,h=e-n.width,l=t-n.height,f=performance.now(),p=c=>{let m=c-f,g=Math.min(1,m/r),v=d+h*g,y=o+l*g;i(v,y,a),g<1?this.resizeAnimationId=requestAnimationFrame(p):(this.resizeAnimationId=void 0,s?.())};this.resizeAnimationId=requestAnimationFrame(p)}cancelMove(){this.moveAnimationId!==void 0&&(cancelAnimationFrame(this.moveAnimationId),this.moveAnimationId=void 0)}cancelResize(){this.resizeAnimationId!==void 0&&(cancelAnimationFrame(this.resizeAnimationId),this.resizeAnimationId=void 0)}cancelAll(){this.cancelMove(),this.cancelResize()}isAnimating(){return this.moveAnimationId!==void 0||this.resizeAnimationId!==void 0}};var J=class{static createRenderer(e,t,r,i,s,n,a){switch(e){case"canvas":return new k(t,r,i,s,n,a);default:throw new Error(`Unsupported renderer type: ${e}`)}}static isSupported(e){return e==="canvas"}static getSupportedTypes(){return["canvas"]}};var K=class{config;camera;viewport;coordinateTransformer;layers;renderer;events;draw;images;sizeController;animationController;canvasWrapper;canvas;onCoordsChange;_onClick;get onClick(){return this._onClick}set onClick(e){this._onClick=e,this.events.onClick=e}_onRightClick;get onRightClick(){return this._onRightClick}set onRightClick(e){this._onRightClick=e,this.events.onRightClick=e}_onHover;get onHover(){return this._onHover}set onHover(e){this._onHover=e,this.events.onHover=e}_onMouseDown;get onMouseDown(){return this._onMouseDown}set onMouseDown(e){this._onMouseDown=e,this.events.onMouseDown=e}_onMouseUp;get onMouseUp(){return this._onMouseUp}set onMouseUp(e){this._onMouseUp=e,this.events.onMouseUp=e}_onMouseLeave;get onMouseLeave(){return this._onMouseLeave}set onMouseLeave(e){this._onMouseLeave=e,this.events.onMouseLeave=e}_onDraw;get onDraw(){return this._onDraw}set onDraw(e){this._onDraw=e,this.getCanvasRenderer().onDraw=e}_onResize;get onResize(){return this._onResize}set onResize(e){this._onResize=e,this.events.onResize=e}_onZoom;get onZoom(){return this._onZoom}set onZoom(e){this._onZoom=e,this.events.onZoom=e}constructor(e,t,r={x:0,y:0}){this.canvasWrapper=e,this.canvas=e.querySelector("canvas"),Object.assign(this.canvasWrapper.style,{position:"relative",width:t.size.width+"px",height:t.size.height+"px"}),Object.assign(this.canvas.style,{position:"absolute",top:"0",left:"0"}),this.config=new W(t);let i=t.renderer??"canvas",s=t.gridAligned?{x:Math.floor(r.x)+.5,y:Math.floor(r.y)+.5}:r,n={x:s.x-t.size.width/(2*t.scale),y:s.y-t.size.height/(2*t.scale)};this.viewport=new Z(t.size.width,t.size.height),this.camera=new D(n,this.config.get().scale,this.config.get().minScale,this.config.get().maxScale,this.viewport),this.coordinateTransformer=new Y(this.camera),this.animationController=new q(this.camera,this.viewport,()=>this.handleCameraChange()),this.renderer=this.createRenderer(i),this.images=new V,this.events=new N(this.canvasWrapper,this.canvas,this.camera,this.viewport,this.config,this.coordinateTransformer,()=>this.handleCameraChange()),this.sizeController=new G(this.canvasWrapper,this.canvas,this.camera,this.renderer,this.viewport,this.config,()=>this.handleCameraChange()),this.events.setupEvents(),t.bounds&&this.camera.setBounds(t.bounds)}destroy(){this.events.destroy(),this.animationController.cancelAll(),this.draw?.destroy(),this.layers?.clear(),this.images.clear(),this.renderer.destroy()}render(){this.renderer.render()}resize(e,t,r=500,i){this.sizeController.resizeWithAnimation(e,t,r,this.animationController,()=>{this._onResize?.(),i?.()})}getSize(){return this.viewport.getSize()}getScale(){return this.camera.scale}zoomIn(e=1.5){let t=this.viewport.getSize();this.camera.zoomByFactor(e,t.width/2,t.height/2),this.handleCameraChange()}zoomOut(e=1.5){let t=this.viewport.getSize();this.camera.zoomByFactor(1/e,t.width/2,t.height/2),this.handleCameraChange()}getConfig(){let e=this.config.get(),t=this.viewport.getSize();return{...e,scale:this.camera.scale,size:{...t}}}getCenterCoords(){let e=this.viewport.getSize();return this.camera.getCenter(e.width,e.height)}getVisibleBounds(){let e=this.viewport.getSize();return this.camera.getVisibleBounds(e.width,e.height)}updateCoords(e){let t=this.viewport.getSize();this.camera.setCenter(e,t.width,t.height),this.handleCameraChange()}goCoords(e,t,r=500,i){this.animationController.animateMoveTo(e,t,r,i)}setEventHandlers(e){this.config.updateEventHandlers(e)}setBounds(e){this.config.updateBounds(e),this.camera.setBounds(e),this.render()}addDrawFunction(e,t=1){return this.ensureCanvasDraw().addDrawFunction(e,t)}drawRect(e,t=1){return this.ensureCanvasDraw().drawRect(e,t)}drawStaticRect(e,t,r=1){return this.ensureCanvasDraw().drawStaticRect(e,t,r)}drawStaticCircle(e,t,r=1){return this.ensureCanvasDraw().drawStaticCircle(e,t,r)}drawStaticImage(e,t,r=1){return this.ensureCanvasDraw().drawStaticImage(e,t,r)}clearStaticCache(e){this.ensureCanvasDraw().clearStaticCache(e)}drawLine(e,t,r=1){return this.ensureCanvasDraw().drawLine(e,t,r)}drawCircle(e,t=1){return this.ensureCanvasDraw().drawCircle(e,t)}drawText(e,t,r=2){return this.ensureCanvasDraw().drawText(e,t,r)}drawPath(e,t,r=1){return this.ensureCanvasDraw().drawPath(e,t,r)}drawImage(e,t=1){return this.ensureCanvasDraw().drawImage(e,t)}drawGridLines(e,t=1,r="black",i=0){return this.ensureCanvasDraw().drawGridLines(e,{lineWidth:t,strokeStyle:r},i)}removeLayerHandle(e){if(!this.layers)throw new Error("removeLayerHandle is only available when renderer is set to 'canvas'.");this.layers.remove(e)}clearLayer(e){if(!this.layers)throw new Error("clearLayer is only available when renderer is set to 'canvas'.");this.layers.clear(e)}clearAll(){if(!this.layers)throw new Error("clearAll is only available when renderer is set to 'canvas'.");this.layers.clear()}createRenderer(e){return e==="canvas"&&(this.layers=new $,this.draw=new P(this.layers,this.coordinateTransformer,this.camera)),J.createRenderer(e??"canvas",this.canvas,this.camera,this.coordinateTransformer,this.config,this.viewport,this.layers)}ensureCanvasDraw(){if(!this.draw)throw new Error("Draw helpers are only available when renderer is set to 'canvas'.");return this.draw}getCanvasRenderer(){if(!(this.renderer instanceof k))throw new Error("Canvas renderer required for this operation.");return this.renderer}handleCameraChange(){this.onCoordsChange&&this.onCoordsChange(this.getCenterCoords()),this.render()}};0&&(module.exports={COORDINATE_OVERLAY,CanvasTileEngine,DEBUG_HUD,DEFAULT_VALUES,RENDER_DEFAULTS,SCALE_LIMITS,SIZE_LIMITS,VISIBILITY_BUFFER});
|
|
1
|
+
"use strict";var ge=Object.create;var W=Object.defineProperty;var be=Object.getOwnPropertyDescriptor;var Ce=Object.getOwnPropertyNames;var we=Object.getPrototypeOf,ye=Object.prototype.hasOwnProperty;var xe=(a,e)=>{for(var t in e)W(a,t,{get:e[t],enumerable:!0})},ne=(a,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of Ce(e))!ye.call(a,s)&&s!==t&&W(a,s,{get:()=>e[s],enumerable:!(i=be(e,s))||i.enumerable});return a};var Te=(a,e,t)=>(t=a!=null?ge(we(a)):{},ne(e||!a||!a.__esModule?W(t,"default",{value:a,enumerable:!0}):t,a)),Se=a=>ne(W({},"__esModule",{value:!0}),a);var ze={};xe(ze,{COORDINATE_OVERLAY:()=>E,CanvasTileEngine:()=>te,DEBUG_HUD:()=>z,DEFAULT_VALUES:()=>x,RENDER_DEFAULTS:()=>F,SCALE_LIMITS:()=>O,SIZE_LIMITS:()=>D,VISIBILITY_BUFFER:()=>R,gridToSize:()=>ve});module.exports=Se(ze);var x={ANIMATION_DURATION_MS:500,CELL_CENTER_OFFSET:.5,IMAGE_LOAD_RETRY_COUNT:1,MAX_WHEEL_DELTA:100,MIN_WHEEL_DELTA:-100,ZOOM_SENSITIVITY:.001},O={MIN_SCALE_MULTIPLIER:.5,MAX_SCALE_MULTIPLIER:2},D={MIN_WIDTH:100,MIN_HEIGHT:100,MAX_WIDTH:1/0,MAX_HEIGHT:1/0},F={BACKGROUND_COLOR:"#ffffff",RENDERER_TYPE:"canvas"},E={BORDER_WIDTH:20,TEXT_OPACITY:.8,BORDER_OPACITY:.1,MIN_FONT_SIZE:8,MAX_FONT_SIZE:12,FONT_SIZE_SCALE_FACTOR:.25},z={PANEL_WIDTH:160,PADDING:8,LINE_HEIGHT:16},R={TILE_BUFFER:1};function ae(a,e,t,i){return{x:a.x-t/e,y:a.y-i/e}}function oe(a,e,t,i,s,n){let r=Math.min(Math.max(t,x.MIN_WHEEL_DELTA),x.MAX_WHEEL_DELTA),o=Math.exp(-r*x.ZOOM_SENSITIVITY),m=Math.min(s,Math.max(i,e*o));return m===e?{topLeft:a,scale:e}:{topLeft:{x:a.x+n.x*(1/e-1/m),y:a.y+n.y*(1/e-1/m)},scale:m}}function he(a,e){return{x:(a.x+x.CELL_CENTER_OFFSET-e.x)*e.scale,y:(a.y+x.CELL_CENTER_OFFSET-e.y)*e.scale}}function le(a,e){return{x:e.x+a.x/e.scale,y:e.y+a.y/e.scale}}var H=class{_x;_y;_scale;minScale;maxScale;bounds;viewport;constructor(e,t=1,i=.1,s=10,n){this._x=e.x+x.CELL_CENTER_OFFSET,this._y=e.y+x.CELL_CENTER_OFFSET,this._scale=t,this.minScale=i,this.maxScale=s,this.viewport=n}setBounds(e){this.bounds=e,this.bounds&&this.clampToBounds()}clampToBounds(){if(!this.bounds||!this.viewport)return;let{width:e,height:t}=this.viewport.getSize(),i=e/this._scale,s=t/this._scale;this._x=this.clampAxis(this._x,i,this.bounds.minX,this.bounds.maxX),this._y=this.clampAxis(this._y,s,this.bounds.minY,this.bounds.maxY)}clampAxis(e,t,i,s){let n=s-i;return t>=n?i-(t-n)/2:e<i?i:e+t>s?s-t:e}get x(){return this._x}get y(){return this._y}get scale(){return this._scale}setScale(e){this._scale=Math.min(this.maxScale,Math.max(this.minScale,e)),this.clampToBounds()}pan(e,t){let i=ae({x:this._x,y:this._y},this._scale,e,t);this._x=i.x,this._y=i.y,this.clampToBounds()}zoom(e,t,i,s){let n=e-s.left,r=t-s.top,o=oe({x:this._x,y:this._y},this._scale,i,this.minScale,this.maxScale,{x:n,y:r});this._x=o.topLeft.x,this._y=o.topLeft.y,this._scale=o.scale,this.clampToBounds()}zoomByFactor(e,t,i){let s=Math.min(this.maxScale,Math.max(this.minScale,this._scale*e));s!==this._scale&&(this._x=this._x+t*(1/this._scale-1/s),this._y=this._y+i*(1/this._scale-1/s),this._scale=s,this.clampToBounds())}getCenter(e,t){return{x:this._x+e/(2*this._scale)-.5,y:this._y+t/(2*this._scale)-.5}}setCenter(e,t,i){this._x=e.x-t/(2*this._scale)+.5,this._y=e.y-i/(2*this._scale)+.5,this.clampToBounds()}adjustForResize(e,t){this._x-=e/(2*this._scale),this._y-=t/(2*this._scale),this.clampToBounds()}getVisibleBounds(e,t){let i=this._x-x.CELL_CENTER_OFFSET,s=this._y-x.CELL_CENTER_OFFSET,n=i+e/this._scale,r=s+t/this._scale;return{minX:Math.floor(i),maxX:Math.ceil(n),minY:Math.floor(s),maxY:Math.ceil(r)}}};function C(a){return new Error(`[CanvasTileEngine] Invalid config: ${a}`)}function ce(a){if(typeof a.scale!="number"||!Number.isFinite(a.scale))throw C(`scale must be a finite number, got ${a.scale}`);if(a.scale<=0)throw C(`scale must be positive, got ${a.scale}`);if(a.minScale!==void 0){if(typeof a.minScale!="number"||!Number.isFinite(a.minScale))throw C(`minScale must be a finite number, got ${a.minScale}`);if(a.minScale<=0)throw C(`minScale must be positive, got ${a.minScale}`)}if(a.maxScale!==void 0){if(typeof a.maxScale!="number"||!Number.isFinite(a.maxScale))throw C(`maxScale must be a finite number, got ${a.maxScale}`);if(a.maxScale<=0)throw C(`maxScale must be positive, got ${a.maxScale}`)}if(a.minScale!==void 0&&a.maxScale!==void 0&&a.minScale>a.maxScale)throw C(`minScale (${a.minScale}) cannot be greater than maxScale (${a.maxScale})`);if(!a.size||typeof a.size!="object")throw C("size is required and must be an object");if(typeof a.size.width!="number"||!Number.isFinite(a.size.width))throw C(`size.width must be a finite number, got ${a.size.width}`);if(a.size.width<=0)throw C(`size.width must be positive, got ${a.size.width}`);if(typeof a.size.height!="number"||!Number.isFinite(a.size.height))throw C(`size.height must be a finite number, got ${a.size.height}`);if(a.size.height<=0)throw C(`size.height must be positive, got ${a.size.height}`);if(a.size.minWidth!==void 0&&a.size.minWidth<=0)throw C(`size.minWidth must be positive, got ${a.size.minWidth}`);if(a.size.maxWidth!==void 0&&a.size.maxWidth<=0)throw C(`size.maxWidth must be positive, got ${a.size.maxWidth}`);if(a.size.minHeight!==void 0&&a.size.minHeight<=0)throw C(`size.minHeight must be positive, got ${a.size.minHeight}`);if(a.size.maxHeight!==void 0&&a.size.maxHeight<=0)throw C(`size.maxHeight must be positive, got ${a.size.maxHeight}`);if(a.size.minWidth!==void 0&&a.size.maxWidth!==void 0&&a.size.minWidth>a.size.maxWidth)throw C(`size.minWidth (${a.size.minWidth}) cannot be greater than size.maxWidth (${a.size.maxWidth})`);if(a.size.minHeight!==void 0&&a.size.maxHeight!==void 0&&a.size.minHeight>a.size.maxHeight)throw C(`size.minHeight (${a.size.minHeight}) cannot be greater than size.maxHeight (${a.size.maxHeight})`);if(a.bounds){let{minX:e,maxX:t,minY:i,maxY:s}=a.bounds;if(typeof e!="number"||typeof t!="number")throw C("bounds.minX and bounds.maxX must be numbers");if(typeof i!="number"||typeof s!="number")throw C("bounds.minY and bounds.maxY must be numbers");if(Number.isFinite(e)&&Number.isFinite(t)&&e>=t)throw C(`bounds.minX (${e}) must be less than bounds.maxX (${t})`);if(Number.isFinite(i)&&Number.isFinite(s)&&i>=s)throw C(`bounds.minY (${i}) must be less than bounds.maxY (${s})`)}}function de(a){let{minX:e,maxX:t,minY:i,maxY:s}=a;if(typeof e!="number"||typeof t!="number")throw C("bounds.minX and bounds.maxX must be numbers");if(typeof i!="number"||typeof s!="number")throw C("bounds.minY and bounds.maxY must be numbers");if(Number.isFinite(e)&&Number.isFinite(t)&&e>=t)throw C(`bounds.minX (${e}) must be less than bounds.maxX (${t})`);if(Number.isFinite(i)&&Number.isFinite(s)&&i>=s)throw C(`bounds.minY (${i}) must be less than bounds.maxY (${s})`)}function re(a,e){if(typeof a!="number"||!Number.isFinite(a))throw C(`x coordinate must be a finite number, got ${a}`);if(typeof e!="number"||!Number.isFinite(e))throw C(`y coordinate must be a finite number, got ${e}`)}function me(a){if(typeof a!="number"||!Number.isFinite(a))throw C(`scale must be a finite number, got ${a}`);if(a<=0)throw C(`scale must be positive, got ${a}`)}var Y=class{config;constructor(e){ce(e);let t={renderer:F.RENDERER_TYPE,scale:e.scale,minScale:e.minScale??e.scale*O.MIN_SCALE_MULTIPLIER,maxScale:e.maxScale??e.scale*O.MAX_SCALE_MULTIPLIER,gridAligned:e.gridAligned??!1,size:{width:e.size.width,height:e.size.height,maxHeight:e.size.maxHeight??D.MAX_HEIGHT,maxWidth:e.size.maxWidth??D.MAX_WIDTH,minHeight:e.size.minHeight??D.MIN_HEIGHT,minWidth:e.size.minWidth??D.MIN_WIDTH},responsive:e.responsive??!1,backgroundColor:e.backgroundColor??F.BACKGROUND_COLOR,eventHandlers:{click:e.eventHandlers?.click??!1,rightClick:e.eventHandlers?.rightClick??!1,hover:e.eventHandlers?.hover??!1,drag:e.eventHandlers?.drag??!1,zoom:e.eventHandlers?.zoom??!1,resize:e.eventHandlers?.resize??!1},bounds:e.bounds??{minX:-1/0,maxX:1/0,minY:-1/0,maxY:1/0},coordinates:{enabled:e.coordinates?.enabled??!1,shownScaleRange:e.coordinates?.shownScaleRange??{min:0,max:1/0}},cursor:{default:e.cursor?.default??"default",move:e.cursor?.move??"move"},debug:{enabled:e.debug?.enabled??!1,hud:{enabled:e.debug?.hud?.enabled??!1,topLeftCoordinates:e.debug?.hud?.topLeftCoordinates??!1,coordinates:e.debug?.hud?.coordinates??!1,scale:e.debug?.hud?.scale??!1,tilesInView:e.debug?.hud?.tilesInView??!1,fps:e.debug?.hud?.fps??!1},eventHandlers:{click:e.debug?.eventHandlers?.click??!0,hover:e.debug?.eventHandlers?.hover??!0,drag:e.debug?.eventHandlers?.drag??!0,zoom:e.debug?.eventHandlers?.zoom??!0,resize:e.debug?.eventHandlers?.resize??!0}}};this.config={...t,size:Object.freeze(t.size),eventHandlers:Object.freeze(t.eventHandlers),bounds:Object.freeze(t.bounds),coordinates:Object.freeze({...t.coordinates,shownScaleRange:Object.freeze(t.coordinates.shownScaleRange)}),cursor:Object.freeze(t.cursor),debug:Object.freeze({enabled:t.debug.enabled,hud:Object.freeze(t.debug.hud),eventHandlers:Object.freeze(t.debug.eventHandlers)})}}get(){let e=this.config;return{...e,size:{...e.size},responsive:e.responsive,eventHandlers:{...e.eventHandlers},bounds:{...e.bounds},coordinates:{...e.coordinates,shownScaleRange:{min:e.coordinates.shownScaleRange?.min??0,max:e.coordinates.shownScaleRange?.max??1/0}},cursor:{...e.cursor},debug:{...e.debug,hud:{...e.debug.hud},eventHandlers:{...e.debug.eventHandlers}}}}updateEventHandlers(e){this.config={...this.config,eventHandlers:Object.freeze({...this.config.eventHandlers,...e})}}updateBounds(e){de(e),this.config={...this.config,bounds:Object.freeze(e)}}};var X=class{constructor(e){this.camera=e}worldToScreen(e,t){return he({x:e,y:t},{x:this.camera.x,y:this.camera.y,scale:this.camera.scale})}screenToWorld(e,t){return le({x:e,y:t},{x:this.camera.x,y:this.camera.y,scale:this.camera.scale})}};var ue=Te(require("rbush")),L=class a{tree;constructor(){this.tree=new ue.default}load(e){let t=e.map(i=>{let n=(typeof i.size=="number"?i.size:0)/2;return{minX:i.x-n,minY:i.y-n,maxX:i.x+n,maxY:i.y+n,item:i}});this.tree.load(t)}query(e,t,i,s){return this.tree.search({minX:e,minY:t,maxX:i,maxY:s}).map(r=>r.item)}clear(){this.tree.clear()}static fromArray(e){let t=new a;return t.load(e),t}};function _(a,e){if(e>=1)return a.lineWidth=e,()=>{};let t=Math.max(0,Math.min(e,1));return a.lineWidth=1,a.globalAlpha=t,()=>{a.globalAlpha=1}}var P=500,pe=16384,N=class{constructor(e,t,i){this.layers=e;this.transformer=t;this.camera=i;this.staticCacheSupported=typeof OffscreenCanvas<"u"||typeof document<"u"}staticCaches=new Map;staticCacheSupported;warnedStaticCacheDisabled=!1;isVisible(e,t,i,s,n){let r=n.size.width/n.scale,o=n.size.height/n.scale,m=s.x-R.TILE_BUFFER,l=s.y-R.TILE_BUFFER,d=s.x+r+R.TILE_BUFFER,h=s.y+o+R.TILE_BUFFER;return e+i>=m&&e-i<=d&&t+i>=l&&t-i<=h}getViewportBounds(e,t){let i=t.size.width/t.scale,s=t.size.height/t.scale;return{minX:e.x-R.TILE_BUFFER,minY:e.y-R.TILE_BUFFER,maxX:e.x+i+R.TILE_BUFFER,maxY:e.y+s+R.TILE_BUFFER}}addDrawFunction(e,t=1){return this.layers.add(t,({ctx:i,config:s,topLeft:n})=>{e(i,n,s)})}drawRect(e,t=1){let i=Array.isArray(e)?e:[e],n=i.length>P?L.fromArray(i):null;return this.layers.add(t,({ctx:r,config:o,topLeft:m})=>{let l=this.getViewportBounds(m,o),d=n?n.query(l.minX,l.minY,l.maxX,l.maxY):i;r.save();let h,p,f;for(let c of d){let u=c.size??1,g={mode:c.origin?.mode==="self"?"self":"cell",x:c.origin?.x??.5,y:c.origin?.y??.5},v=c.style;if(!n&&!this.isVisible(c.x,c.y,u/2,m,o))continue;let w=this.transformer.worldToScreen(c.x,c.y),b=u*this.camera.scale,{x:y,y:T}=this.computeOriginOffset(w,b,g,this.camera);v?.fillStyle&&v.fillStyle!==h&&(r.fillStyle=v.fillStyle,h=v.fillStyle),v?.strokeStyle&&v.strokeStyle!==p&&(r.strokeStyle=v.strokeStyle,p=v.strokeStyle);let S;v?.lineWidth&&v.lineWidth!==f&&(S=_(r,v.lineWidth),f=v.lineWidth);let I=c.rotate??0,ie=I*(Math.PI/180),M=c.radius;if(I!==0){let se=y+b/2,fe=T+b/2;r.save(),r.translate(se,fe),r.rotate(ie),r.beginPath(),M&&r.roundRect?r.roundRect(-b/2,-b/2,b,b,M):r.rect(-b/2,-b/2,b,b),v?.fillStyle&&r.fill(),v?.strokeStyle&&r.stroke(),r.restore()}else r.beginPath(),M&&r.roundRect?r.roundRect(y,T,b,b,M):r.rect(y,T,b,b),v?.fillStyle&&r.fill(),v?.strokeStyle&&r.stroke();S?.()}r.restore()})}drawLine(e,t,i=1){let s=Array.isArray(e)?e:[e];return this.layers.add(i,({ctx:n,config:r,topLeft:o})=>{n.save(),t?.strokeStyle&&(n.strokeStyle=t.strokeStyle);let m=t?.lineWidth?_(n,t.lineWidth):void 0;n.beginPath();for(let l of s){let d=(l.from.x+l.to.x)/2,h=(l.from.y+l.to.y)/2,p=Math.max(Math.abs(l.from.x-l.to.x),Math.abs(l.from.y-l.to.y))/2;if(!this.isVisible(d,h,p,o,r))continue;let f=this.transformer.worldToScreen(l.from.x,l.from.y),c=this.transformer.worldToScreen(l.to.x,l.to.y);n.moveTo(f.x,f.y),n.lineTo(c.x,c.y)}n.stroke(),m?.(),n.restore()})}drawCircle(e,t=1){let i=Array.isArray(e)?e:[e],n=i.length>P?L.fromArray(i):null;return this.layers.add(t,({ctx:r,config:o,topLeft:m})=>{let l=this.getViewportBounds(m,o),d=n?n.query(l.minX,l.minY,l.maxX,l.maxY):i;r.save();let h,p,f;for(let c of d){let u=c.size??1,g={mode:c.origin?.mode==="self"?"self":"cell",x:c.origin?.x??.5,y:c.origin?.y??.5},v=c.style;if(!n&&!this.isVisible(c.x,c.y,u/2,m,o))continue;let w=this.transformer.worldToScreen(c.x,c.y),b=u*this.camera.scale,y=b/2,{x:T,y:S}=this.computeOriginOffset(w,b,g,this.camera);v?.fillStyle&&v.fillStyle!==h&&(r.fillStyle=v.fillStyle,h=v.fillStyle),v?.strokeStyle&&v.strokeStyle!==p&&(r.strokeStyle=v.strokeStyle,p=v.strokeStyle);let I;v?.lineWidth&&v.lineWidth!==f&&(I=_(r,v.lineWidth),f=v.lineWidth),r.beginPath(),r.arc(T+y,S+y,y,0,Math.PI*2),v?.fillStyle&&r.fill(),v?.strokeStyle&&r.stroke(),I?.()}r.restore()})}drawText(e,t=2){let i=Array.isArray(e)?e:[e],n=i.length>P?L.fromArray(i):null;return this.layers.add(t,({ctx:r,config:o,topLeft:m})=>{let l=this.getViewportBounds(m,o),d=n?n.query(l.minX,l.minY,l.maxX,l.maxY):i;r.save();for(let h of d){let p=h.size??1,f=h.style;if(!n&&!this.isVisible(h.x,h.y,p,m,o))continue;let c=p*this.camera.scale*.3,u=f?.fontFamily??"sans-serif";r.font=`${c}px ${u}`,f?.fillStyle&&(r.fillStyle=f.fillStyle),r.textAlign=f?.textAlign??"center",r.textBaseline=f?.textBaseline??"middle";let g=this.transformer.worldToScreen(h.x,h.y),v=h.rotate??0;if(v!==0){let w=v*(Math.PI/180);r.save(),r.translate(g.x,g.y),r.rotate(w),r.fillText(h.text,0,0),r.restore()}else r.fillText(h.text,g.x,g.y)}r.restore()})}drawPath(e,t,i=1){let s=Array.isArray(e[0])?e:[e];return this.layers.add(i,({ctx:n,config:r,topLeft:o})=>{n.save(),t?.strokeStyle&&(n.strokeStyle=t.strokeStyle);let m=t?.lineWidth?_(n,t.lineWidth):void 0;n.beginPath();for(let l of s){if(!l.length)continue;let d=l.map(y=>y.x),h=l.map(y=>y.y),p=Math.min(...d),f=Math.max(...d),c=Math.min(...h),u=Math.max(...h),g=(p+f)/2,v=(c+u)/2,w=Math.max(f-p,u-c)/2;if(!this.isVisible(g,v,w,o,r))continue;let b=this.transformer.worldToScreen(l[0].x,l[0].y);n.moveTo(b.x,b.y);for(let y=1;y<l.length;y++){let T=this.transformer.worldToScreen(l[y].x,l[y].y);n.lineTo(T.x,T.y)}}n.stroke(),m?.(),n.restore()})}drawImage(e,t=1){let i=Array.isArray(e)?e:[e],n=i.length>P?L.fromArray(i):null;return this.layers.add(t,({ctx:r,config:o,topLeft:m})=>{let l=this.getViewportBounds(m,o),d=n?n.query(l.minX,l.minY,l.maxX,l.maxY):i;for(let h of d){let p=h.size??1,f={mode:h.origin?.mode==="self"?"self":"cell",x:h.origin?.x??.5,y:h.origin?.y??.5};if(!n&&!this.isVisible(h.x,h.y,p/2,m,o))continue;let c=this.transformer.worldToScreen(h.x,h.y),u=p*this.camera.scale,g=h.img.width/h.img.height,v=u,w=u;g>1?w=u/g:v=u*g;let{x:b,y}=this.computeOriginOffset(c,u,f,this.camera),T=b+(u-v)/2,S=y+(u-w)/2,I=h.rotate??0,ie=I*(Math.PI/180);if(I!==0){let M=T+v/2,se=S+w/2;r.save(),r.translate(M,se),r.rotate(ie),r.drawImage(h.img,-v/2,-w/2,v,w),r.restore()}else r.drawImage(h.img,T,S,v,w)}})}drawGridLines(e,t,i=0){return this.layers.add(i,({ctx:s,config:n,topLeft:r})=>{let o=n.size.width/n.scale,m=n.size.height/n.scale,l=Math.floor(r.x/e)*e-x.CELL_CENTER_OFFSET,d=Math.ceil((r.x+o)/e)*e-x.CELL_CENTER_OFFSET,h=Math.floor(r.y/e)*e-x.CELL_CENTER_OFFSET,p=Math.ceil((r.y+m)/e)*e-x.CELL_CENTER_OFFSET;s.save(),s.strokeStyle=t.strokeStyle;let f=_(s,t.lineWidth);s.beginPath();for(let c=l;c<=d;c+=e){let u=this.transformer.worldToScreen(c,h),g=this.transformer.worldToScreen(c,p);s.moveTo(u.x,u.y),s.lineTo(g.x,g.y)}for(let c=h;c<=p;c+=e){let u=this.transformer.worldToScreen(l,c),g=this.transformer.worldToScreen(d,c);s.moveTo(u.x,u.y),s.lineTo(g.x,g.y)}s.stroke(),f(),s.restore()})}computeOriginOffset(e,t,i,s){if(i.mode==="cell"){let n=s.scale;return{x:e.x-n/2+i.x*n-t/2,y:e.y-n/2+i.y*n-t/2}}return{x:e.x-i.x*t,y:e.y-i.y*t}}getOrCreateStaticCache(e,t,i){if(!this.staticCacheSupported)return this.warnedStaticCacheDisabled||(console.warn("[CanvasDraw] Static cache disabled: OffscreenCanvas not available."),this.warnedStaticCacheDisabled=!0),null;let s=1/0,n=-1/0,r=1/0,o=-1/0;for(let u of e){let g=u.size??1;u.x-g/2<s&&(s=u.x-g/2),u.x+g/2>n&&(n=u.x+g/2),u.y-g/2<r&&(r=u.y-g/2),u.y+g/2>o&&(o=u.y+g/2)}s-=1,r-=1,n+=1,o+=1;let m=n-s,l=o-r,d=this.camera.scale,h=Math.ceil(m*d),p=Math.ceil(l*d);if(h>pe||p>pe)return this.warnedStaticCacheDisabled||(console.warn(`Static cache disabled: offscreen canvas too large (${h}x${p}).`),this.warnedStaticCacheDisabled=!0),null;let f=this.staticCaches.get(t);if(!f||f.scale!==d||f.worldBounds.minX!==s||f.worldBounds.maxX!==n||f.worldBounds.minY!==r||f.worldBounds.maxY!==o){let u=typeof OffscreenCanvas<"u"?new OffscreenCanvas(h,p):document.createElement("canvas");typeof OffscreenCanvas<"u"&&u instanceof OffscreenCanvas||(u.width=h,u.height=p);let v=u.getContext("2d");if(!v)return this.warnedStaticCacheDisabled||(console.warn("[CanvasDraw] Static cache disabled: 2D context unavailable."),this.warnedStaticCacheDisabled=!0),null;for(let w of e){let y=(w.size??1)*d,T=(w.x+x.CELL_CENTER_OFFSET-s)*d-y/2,S=(w.y+x.CELL_CENTER_OFFSET-r)*d-y/2;i(v,w,T,S,y)}f={canvas:u,ctx:v,worldBounds:{minX:s,minY:r,maxX:n,maxY:o},scale:d},this.staticCaches.set(t,f)}return f||null}addStaticCacheLayer(e,t){if(!e)return null;let i=e.canvas,s=e.worldBounds,n=e.scale;return this.layers.add(t,({ctx:r,config:o,topLeft:m})=>{let l=o.size.width/o.scale,d=o.size.height/o.scale,h=(m.x-s.minX)*n,p=(m.y-s.minY)*n,f=l*n,c=d*n,u=0,g=0,v=o.size.width,w=o.size.height,b=i.width,y=i.height;if(h<0&&(u=-h/n*o.scale,v-=u,f+=h,h=0),p<0&&(g=-p/n*o.scale,w-=g,c+=p,p=0),h+f>b){let S=(h+f-b)/n;f=b-h,v-=S*o.scale}if(p+c>y){let S=(p+c-y)/n;c=y-p,w-=S*o.scale}f>0&&c>0&&v>0&&w>0&&r.drawImage(i,h,p,f,c,u,g,v,w)})}drawStaticRect(e,t,i=1){let s,n=this.getOrCreateStaticCache(e,t,(r,o,m,l,d)=>{let h=o.style,p=o.rotate??0,f=p*(Math.PI/180),c=o.radius;if(h?.fillStyle&&h.fillStyle!==s&&(r.fillStyle=h.fillStyle,s=h.fillStyle),p!==0){let u=m+d/2,g=l+d/2;r.save(),r.translate(u,g),r.rotate(f),c&&r.roundRect?(r.beginPath(),r.roundRect(-d/2,-d/2,d,d,c),r.fill()):r.fillRect(-d/2,-d/2,d,d),r.restore()}else c&&r.roundRect?(r.beginPath(),r.roundRect(m,l,d,d,c),r.fill()):r.fillRect(m,l,d,d)});return n?this.addStaticCacheLayer(n,i):this.drawRect(e,i)}drawStaticImage(e,t,i=1){let s=this.getOrCreateStaticCache(e,t,(n,r,o,m,l)=>{let d=r.img,h=r.rotate??0,p=h*(Math.PI/180),f=d.width/d.height,c=l,u=l;f>1?u=l/f:c=l*f;let g=o+(l-c)/2,v=m+(l-u)/2;if(h!==0){let w=g+c/2,b=v+u/2;n.save(),n.translate(w,b),n.rotate(p),n.drawImage(d,-c/2,-u/2,c,u),n.restore()}else n.drawImage(d,g,v,c,u)});return s?this.addStaticCacheLayer(s,i):this.drawImage(e,i)}drawStaticCircle(e,t,i=1){let s,n=this.getOrCreateStaticCache(e,t,(r,o,m,l,d)=>{let h=o.style,p=d/2;h?.fillStyle&&h.fillStyle!==s&&(r.fillStyle=h.fillStyle,s=h.fillStyle),r.beginPath(),r.arc(m+p,l+p,p,0,Math.PI*2),r.fill()});return n?this.addStaticCacheLayer(n,i):this.drawCircle(e,i)}clearStaticCache(e){e?this.staticCaches.delete(e):this.staticCaches.clear()}destroy(){this.staticCaches.clear(),this.layers.clear()}};var $=class{constructor(e,t){this.canvas=e;this.handlers=t}attach(){this.handlers.click&&this.canvas.addEventListener("click",this.handlers.click),this.handlers.contextmenu&&this.canvas.addEventListener("contextmenu",this.handlers.contextmenu),this.handlers.mousedown&&this.canvas.addEventListener("mousedown",this.handlers.mousedown),this.handlers.mousemove&&this.canvas.addEventListener("mousemove",this.handlers.mousemove),this.handlers.mouseup&&this.canvas.addEventListener("mouseup",this.handlers.mouseup),this.handlers.mouseleave&&this.canvas.addEventListener("mouseleave",this.handlers.mouseleave),this.handlers.wheel&&this.canvas.addEventListener("wheel",this.handlers.wheel,{passive:!1}),this.handlers.touchstart&&this.canvas.addEventListener("touchstart",this.handlers.touchstart,{passive:!1}),this.handlers.touchmove&&this.canvas.addEventListener("touchmove",this.handlers.touchmove,{passive:!1}),this.handlers.touchend&&this.canvas.addEventListener("touchend",this.handlers.touchend,{passive:!1})}detach(){this.handlers.click&&this.canvas.removeEventListener("click",this.handlers.click),this.handlers.contextmenu&&this.canvas.removeEventListener("contextmenu",this.handlers.contextmenu),this.handlers.mousedown&&this.canvas.removeEventListener("mousedown",this.handlers.mousedown),this.handlers.mousemove&&this.canvas.removeEventListener("mousemove",this.handlers.mousemove),this.handlers.mouseup&&this.canvas.removeEventListener("mouseup",this.handlers.mouseup),this.handlers.mouseleave&&this.canvas.removeEventListener("mouseleave",this.handlers.mouseleave),this.handlers.wheel&&this.canvas.removeEventListener("wheel",this.handlers.wheel),this.handlers.touchstart&&this.canvas.removeEventListener("touchstart",this.handlers.touchstart),this.handlers.touchmove&&this.canvas.removeEventListener("touchmove",this.handlers.touchmove),this.handlers.touchend&&this.canvas.removeEventListener("touchend",this.handlers.touchend)}};var B=class{constructor(e,t,i,s,n,r){this.canvas=e;this.camera=t;this.viewport=i;this.config=s;this.transformer=n;this.onCameraChange=r}isDragging=!1;shouldPreventClick=!1;lastPos={x:0,y:0};isPinching=!1;lastPinchDistance=0;lastPinchCenter={x:0,y:0};onClick;onRightClick;onHover;onMouseDown;onMouseUp;onMouseLeave;onZoom;getEventCoords=e=>{let t=this.canvas.getBoundingClientRect(),i=e.clientX-t.left,s=e.clientY-t.top,n=this.transformer.screenToWorld(i,s),r=this.transformer.worldToScreen(Math.floor(n.x),Math.floor(n.y));return{coords:{raw:n,snapped:{x:Math.floor(n.x),y:Math.floor(n.y)}},mouse:{raw:{x:i,y:s},snapped:{x:r.x,y:r.y}},client:{raw:{x:e.clientX,y:e.clientY},snapped:{x:r.x+t.left,y:r.y+t.top}}}};handleClick=e=>{if(this.shouldPreventClick){this.shouldPreventClick=!1;return}if(!this.config.get().eventHandlers.click||!this.onClick)return;let{coords:t,mouse:i,client:s}=this.getEventCoords(e);this.onClick(t,i,s)};handleContextMenu=e=>{if(!this.config.get().eventHandlers.rightClick||!this.onRightClick)return;e.preventDefault();let{coords:t,mouse:i,client:s}=this.getEventCoords(e);this.onRightClick(t,i,s)};handleMouseDown=e=>{if(this.onMouseDown){let{coords:t,mouse:i,client:s}=this.getEventCoords(e);this.onMouseDown(t,i,s)}this.config.get().eventHandlers.drag&&(this.isDragging=!0,this.shouldPreventClick=!1,this.lastPos={x:e.clientX,y:e.clientY})};handleMouseMove=e=>{if(!this.isDragging){if(this.onHover&&this.config.get().eventHandlers.hover){let{coords:s,mouse:n,client:r}=this.getEventCoords(e);this.onHover(s,n,r)}return}let t=e.clientX-this.lastPos.x,i=e.clientY-this.lastPos.y;(t!==0||i!==0)&&(this.canvas.style.cursor=this.config.get().cursor.move||"move",this.shouldPreventClick=!0),this.camera.pan(t,i),this.lastPos={x:e.clientX,y:e.clientY},this.onCameraChange()};handleMouseUp=e=>{if(this.onMouseUp){let{coords:t,mouse:i,client:s}=this.getEventCoords(e);this.onMouseUp(t,i,s)}this.isDragging=!1,this.canvas.style.cursor=this.config.get().cursor.default||"default"};handleMouseLeave=e=>{if(this.onMouseLeave){let{coords:t,mouse:i,client:s}=this.getEventCoords(e);this.onMouseLeave(t,i,s)}this.isDragging=!1,this.canvas.style.cursor=this.config.get().cursor.default||"default"};handleTouchStart=e=>{let t=this.config.get().eventHandlers;if(e.touches.length===2&&t.zoom){e.preventDefault(),this.isPinching=!0,this.isDragging=!1,this.shouldPreventClick=!0,this.lastPinchDistance=this.getTouchDistance(e.touches),this.lastPinchCenter=this.getTouchCenter(e.touches);return}if(e.touches.length!==1)return;let i=e.touches[0];if(this.onMouseDown){e.preventDefault();let{coords:s,mouse:n,client:r}=this.getEventCoords(i);this.onMouseDown(s,n,r)}t.drag&&(this.isDragging=!0,this.isPinching=!1,this.shouldPreventClick=!1,this.lastPos={x:i.clientX,y:i.clientY})};handleTouchMove=e=>{if(this.isPinching&&e.touches.length===2){e.preventDefault();let n=this.getTouchDistance(e.touches),r=this.getTouchCenter(e.touches),o=this.canvas.getBoundingClientRect(),m=n/this.lastPinchDistance,l=r.x-o.left,d=r.y-o.top;this.camera.zoomByFactor(m,l,d);let h=r.x-this.lastPinchCenter.x,p=r.y-this.lastPinchCenter.y;(h!==0||p!==0)&&this.camera.pan(h,p),this.lastPinchDistance=n,this.lastPinchCenter=r,this.onZoom&&this.onZoom(this.camera.scale),this.onCameraChange();return}if(e.touches.length!==1)return;let t=e.touches[0];if(this.onHover&&this.config.get().eventHandlers.hover){e.preventDefault();let{coords:n,mouse:r,client:o}=this.getEventCoords(t);this.onHover(n,r,o)}if(!this.isDragging)return;e.preventDefault();let i=t.clientX-this.lastPos.x,s=t.clientY-this.lastPos.y;(i!==0||s!==0)&&(this.canvas.style.cursor=this.config.get().cursor.move||"move",this.shouldPreventClick=!0),this.camera.pan(i,s),this.lastPos={x:t.clientX,y:t.clientY},this.onCameraChange()};handleTouchEnd=e=>{if(e.touches.length>=2&&this.isPinching){this.lastPinchDistance=this.getTouchDistance(e.touches),this.lastPinchCenter=this.getTouchCenter(e.touches);return}if(e.touches.length===1&&this.isPinching){if(this.isPinching=!1,this.config.get().eventHandlers.drag){this.isDragging=!0;let t=e.touches[0];this.lastPos={x:t.clientX,y:t.clientY}}return}if(e.changedTouches.length>0&&this.onMouseUp){let t=e.changedTouches[0],{coords:i,mouse:s,client:n}=this.getEventCoords(t);this.onMouseUp(i,s,n)}if(e.changedTouches.length>0&&!this.shouldPreventClick&&this.config.get().eventHandlers.click&&this.onClick){e.preventDefault();let t=e.changedTouches[0],{coords:i,mouse:s,client:n}=this.getEventCoords(t);this.onClick(i,s,n)}this.isDragging=!1,this.isPinching=!1,this.shouldPreventClick=!1,this.canvas.style.cursor=this.config.get().cursor.default||"default"};getTouchDistance(e){let t=e[1].clientX-e[0].clientX,i=e[1].clientY-e[0].clientY;return Math.sqrt(t*t+i*i)}getTouchCenter(e){return{x:(e[0].clientX+e[1].clientX)/2,y:(e[0].clientY+e[1].clientY)/2}}handleWheel=e=>{if(!this.config.get().eventHandlers.zoom)return;e.preventDefault();let t=this.canvas.getBoundingClientRect();this.camera.zoom(e.clientX,e.clientY,e.deltaY,t),this.onZoom&&this.onZoom(this.camera.scale),this.onCameraChange()}};var U=class{constructor(e,t,i,s,n,r){this.wrapper=e;this.canvas=t;this.viewport=i;this.camera=s;this.config=n;this.onCameraChange=r;this.currentDpr=this.viewport.dpr}resizeObserver;handleWindowResize;currentDpr;onResize;start(){this.viewport.updateDpr(),this.currentDpr=this.viewport.dpr;let e=this.viewport.getSize(),t=this.config.get().size,i=t?.maxWidth,s=t?.maxHeight,n=t?.minWidth,r=t?.minHeight;e.width=this.clamp(e.width,n,i),e.height=this.clamp(e.height,r,s),Object.assign(this.wrapper.style,{resize:"both",overflow:"hidden",width:`${e.width}px`,height:`${e.height}px`,touchAction:"none",position:"relative",maxWidth:i?`${i}px`:"",maxHeight:s?`${s}px`:"",minWidth:n?`${n}px`:"",minHeight:r?`${r}px`:""}),this.resizeObserver=new ResizeObserver(o=>{for(let m of o){let{width:l,height:d}=m.contentRect,h=this.clamp(l,n,i),p=this.clamp(d,r,s),f=this.viewport.getSize();if(h===f.width&&p===f.height)continue;let c=h-f.width,u=p-f.height,g=this.viewport.dpr;this.camera.adjustForResize(c,u),this.viewport.setSize(h,p),this.canvas.width=h*g,this.canvas.height=p*g,this.canvas.style.width=`${h}px`,this.canvas.style.height=`${p}px`,this.wrapper.style.width=`${h}px`,this.wrapper.style.height=`${p}px`,this.onResize&&this.onResize(),this.onCameraChange()}}),this.resizeObserver.observe(this.wrapper),this.attachDprWatcher()}stop(){this.resizeObserver&&(this.resizeObserver.unobserve(this.wrapper),this.resizeObserver.disconnect()),this.resizeObserver=void 0,this.handleWindowResize&&(window.removeEventListener("resize",this.handleWindowResize),this.handleWindowResize=void 0)}clamp(e,t,i){let s=e;return t!==void 0&&(s=Math.max(t,s)),i!==void 0&&(s=Math.min(i,s)),s}attachDprWatcher(){typeof window>"u"||(this.handleWindowResize=()=>{let e=this.currentDpr;this.viewport.updateDpr();let t=this.viewport.dpr;if(t===e)return;this.currentDpr=t;let{width:i,height:s}=this.viewport.getSize();this.canvas.width=i*t,this.canvas.height=s*t,this.canvas.style.width=`${i}px`,this.canvas.style.height=`${s}px`,this.onResize&&this.onResize(),this.onCameraChange()},window.addEventListener("resize",this.handleWindowResize,{passive:!0}))}};var V=class{constructor(e,t,i,s,n,r,o){this.canvasWrapper=e;this.canvas=t;this.camera=i;this.viewport=s;this.config=n;this.coordinateTransformer=r;this.onCameraChange=o;this.gestures=new B(this.canvas,this.camera,this.viewport,this.config,this.coordinateTransformer,this.onCameraChange),this.binder=new $(this.canvas,{click:this.gestures.handleClick,contextmenu:this.gestures.handleContextMenu,mousedown:this.gestures.handleMouseDown,mousemove:this.gestures.handleMouseMove,mouseup:this.gestures.handleMouseUp,mouseleave:this.gestures.handleMouseLeave,wheel:this.gestures.handleWheel,touchstart:this.gestures.handleTouchStart,touchmove:this.gestures.handleTouchMove,touchend:this.gestures.handleTouchEnd})}binder;gestures;resizeWatcher;attached=!1;onResize;get onClick(){return this.gestures.onClick}set onClick(e){this.gestures.onClick=e}get onRightClick(){return this.gestures.onRightClick}set onRightClick(e){this.gestures.onRightClick=e}get onHover(){return this.gestures.onHover}set onHover(e){this.gestures.onHover=e}get onMouseDown(){return this.gestures.onMouseDown}set onMouseDown(e){this.gestures.onMouseDown=e}get onMouseUp(){return this.gestures.onMouseUp}set onMouseUp(e){this.gestures.onMouseUp=e}get onMouseLeave(){return this.gestures.onMouseLeave}set onMouseLeave(e){this.gestures.onMouseLeave=e}get onZoom(){return this.gestures.onZoom}set onZoom(e){this.gestures.onZoom=e}setupEvents(){this.attached||(this.binder.attach(),this.attached=!0,this.config.get().responsive?this.config.get().eventHandlers.resize&&console.warn("Canvas Tile Engine: eventHandlers.resize is ignored when responsive mode is enabled. Resizing is handled automatically."):this.config.get().eventHandlers.resize&&this.camera instanceof H&&(this.resizeWatcher=new U(this.canvasWrapper,this.canvas,this.viewport,this.camera,this.config,this.onCameraChange),this.resizeWatcher.onResize=()=>{this.onResize&&this.onResize()},this.resizeWatcher.start()))}destroy(){this.attached&&(this.binder.detach(),this.resizeWatcher?.stop(),this.resizeWatcher=void 0,this.attached=!1)}};var Z=class{cache=new Map;inflight=new Map;listeners=new Set;onLoad(e){return this.listeners.add(e),()=>this.listeners.delete(e)}notifyLoaded(){for(let e of this.listeners)e()}async load(e,t=x.IMAGE_LOAD_RETRY_COUNT){if(this.cache.has(e))return this.cache.get(e);if(this.inflight.has(e))return this.inflight.get(e);let i=new Promise((s,n)=>{let r=new Image;r.crossOrigin="anonymous",r.decoding="async",r.loading="eager",r.onload=async()=>{try{"decode"in r&&await r.decode?.()}catch{}this.cache.set(e,r),this.inflight.delete(e),this.notifyLoaded(),s(r)},r.onerror=o=>{if(this.inflight.delete(e),t>0)console.warn(`Retrying image: ${e}`),s(this.load(e,t-1));else{console.error(`Image failed to load: ${e}`,o);let m=o instanceof Error?o.message:typeof o=="string"?o:JSON.stringify(o);n(new Error(`Image failed to load: ${e}. Reason: ${m}`))}},r.src=e});return this.inflight.set(e,i),i}get(e){return this.cache.get(e)}has(e){return this.cache.has(e)}clear(){this.cache.clear(),this.inflight.clear(),this.listeners.clear()}};var q=class{layers=new Map;add(e,t){let i=Symbol("layer-callback"),s={id:i,fn:t};return this.layers.has(e)||this.layers.set(e,[]),this.layers.get(e).push(s),{layer:e,id:i}}remove(e){let t=this.layers.get(e.layer);t&&this.layers.set(e.layer,t.filter(i=>i.id!==e.id))}clear(e){if(e===void 0){this.layers.clear();return}this.layers.set(e,[])}drawAll(e){let t=[...this.layers.keys()].sort((i,s)=>i-s);for(let i of t){let s=this.layers.get(i);if(s)for(let{fn:n}of s)e.ctx.save(),n(e),e.ctx.restore()}}};var G=class{width;height;_dpr;constructor(e,t){this.width=e,this.height=t,this._dpr=typeof window<"u"&&window.devicePixelRatio||1}getSize(){return{width:this.width,height:this.height}}setSize(e,t){this.width=e,this.height=t}get dpr(){return this._dpr}updateDpr(){this._dpr=typeof window<"u"&&window.devicePixelRatio||1}};var j=class{ctx;camera;config;viewport;constructor(e,t,i,s){this.ctx=e,this.camera=t,this.config=i,this.viewport=s}draw(){this.ctx.save(),this.ctx.fillStyle=`rgba(0, 0, 0, ${E.BORDER_OPACITY})`;let{width:e,height:t}=this.viewport.getSize();this.ctx.fillRect(0,0,E.BORDER_WIDTH,t),this.ctx.fillRect(E.BORDER_WIDTH,t-E.BORDER_WIDTH,e,E.BORDER_WIDTH),this.ctx.fillStyle=`rgba(255, 255, 255, ${E.TEXT_OPACITY})`;let i=Math.min(E.MAX_FONT_SIZE,Math.max(E.MIN_FONT_SIZE,this.camera.scale*E.FONT_SIZE_SCALE_FACTOR));this.ctx.font=`${i}px Arial`,this.ctx.textAlign="center",this.ctx.textBaseline="middle";let s=this.camera.scale,n=e/s,r=t/s;for(let o=0-this.camera.y%1;o<=r+1;o++)this.ctx.fillText(Math.round(this.camera.y+o).toString(),10,s*o+s/2);for(let o=0-this.camera.x%1;o<=n+1;o++)this.ctx.fillText(Math.round(this.camera.x+o).toString(),s*o+s/2,t-10);this.ctx.restore()}shouldDraw(e){let t=this.config.get().coordinates;if(!t.enabled||!t.shownScaleRange)return!1;let{min:i,max:s}=t.shownScaleRange;return e>=i&&e<=s}};var Ee=10,A=class{ctx;camera;transformer;config;viewport;frameTimes=[];lastFrameTime=0;currentFps=0;fpsLoopRunning=!1;onFpsUpdate=null;constructor(e,t,i,s,n){this.ctx=e,this.camera=t,this.transformer=i,this.config=s,this.viewport=n}setFpsUpdateCallback(e){this.onFpsUpdate=e}startFpsLoop(){this.fpsLoopRunning||(this.fpsLoopRunning=!0,this.lastFrameTime=performance.now(),this.fpsLoop())}stopFpsLoop(){this.fpsLoopRunning=!1}fpsLoop(){if(!this.fpsLoopRunning)return;let e=performance.now(),t=e-this.lastFrameTime;this.lastFrameTime=e,this.frameTimes.push(t),this.frameTimes.length>Ee&&this.frameTimes.shift();let i=this.frameTimes.reduce((n,r)=>n+r,0)/this.frameTimes.length,s=Math.round(1e3/i);s!==this.currentFps&&(this.currentFps=s,this.onFpsUpdate?.()),requestAnimationFrame(()=>this.fpsLoop())}draw(){this.drawHud()}destroy(){this.stopFpsLoop(),this.onFpsUpdate=null}drawHud(){let e=this.config.get();if(!e.debug.hud||!e.debug.hud.enabled)return;let t=[],i={x:this.camera.x,y:this.camera.y};if(e.debug.hud.topLeftCoordinates&&t.push(`TopLeft: ${i.x.toFixed(2)}, ${i.y.toFixed(2)}`),e.debug.hud.coordinates){let{width:n,height:r}=this.viewport.getSize(),o=this.camera.getCenter(n,r);t.push(`Coords: ${o.x.toFixed(2)}, ${o.y.toFixed(2)}`)}if(e.debug.hud.scale&&t.push(`Scale: ${this.camera.scale.toFixed(2)}`),e.debug.hud.tilesInView){let{width:n,height:r}=this.viewport.getSize();t.push(`Tiles in view: ${Math.ceil(n/this.camera.scale)} x ${Math.ceil(r/this.camera.scale)}`)}e.debug.hud.fps&&t.push(`FPS: ${this.currentFps}`);let{width:s}=this.viewport.getSize();this.ctx.save(),this.ctx.fillStyle="rgba(0,0,0,0.5)",this.ctx.fillRect(s-z.PANEL_WIDTH-z.PADDING,z.PADDING/2,z.PANEL_WIDTH,t.length*z.LINE_HEIGHT+z.PADDING),this.ctx.fillStyle="#00ff99",this.ctx.font="12px monospace";for(let n=0;n<t.length;n++)this.ctx.fillText(t[n],s-z.PANEL_WIDTH-z.PADDING+5,18+n*z.LINE_HEIGHT);this.ctx.restore()}};var k=class{constructor(e,t,i,s,n,r){this.canvas=e;this.camera=t;this.coordinateTransformer=i;this.config=s;this.viewport=n;this.layers=r;let o=e.getContext("2d");if(!o)throw new Error("Failed to get 2D canvas context");this.ctx=o,this.applyCanvasSize(),this.coordinateOverlayRenderer=new j(this.ctx,this.camera,this.config,this.viewport),this.config.get().debug?.enabled&&(this.debugOverlay=new A(this.ctx,this.camera,this.coordinateTransformer,this.config,this.viewport),this.config.get().debug?.hud?.fps&&(this.debugOverlay.setFpsUpdateCallback(()=>this.render()),this.debugOverlay.startFpsLoop()))}ctx;coordinateOverlayRenderer;debugOverlay;onDraw;init(){this.applyCanvasSize()}applyCanvasSize(){let e=this.viewport.getSize(),t=this.viewport.dpr;this.canvas.width=e.width*t,this.canvas.height=e.height*t,this.canvas.style.width=`${e.width}px`,this.canvas.style.height=`${e.height}px`,this.ctx.setTransform(t,0,0,t,0,0)}render(){let e=this.viewport.getSize(),t=this.viewport.dpr,i={...this.config.get(),size:{...e},scale:this.camera.scale},s={x:this.camera.x,y:this.camera.y};this.ctx.setTransform(t,0,0,t,0,0),this.ctx.clearRect(0,0,i.size.width,i.size.height),this.ctx.fillStyle=i.backgroundColor,this.ctx.fillRect(0,0,i.size.width,i.size.height),this.layers.drawAll({ctx:this.ctx,camera:this.camera,transformer:this.coordinateTransformer,config:i,topLeft:s}),this.onDraw?.(this.ctx,{scale:this.camera.scale,width:i.size.width,height:i.size.height,coords:s}),this.coordinateOverlayRenderer.shouldDraw(this.camera.scale)&&this.coordinateOverlayRenderer.draw(),i.debug?.enabled&&(this.debugOverlay||(this.debugOverlay=new A(this.ctx,this.camera,this.coordinateTransformer,this.config,this.viewport),i.debug?.hud?.fps&&(this.debugOverlay.setFpsUpdateCallback(()=>this.render()),this.debugOverlay.startFpsLoop())),this.debugOverlay.draw())}resize(e,t){let i=this.viewport.dpr;this.viewport.setSize(e,t),this.canvas.width=e*i,this.canvas.height=t*i,this.canvas.style.width=`${e}px`,this.canvas.style.height=`${t}px`,this.ctx.setTransform(i,0,0,i,0,0)}destroy(){this.debugOverlay&&(this.debugOverlay.destroy(),this.debugOverlay=void 0),this.layers.clear()}getContext(){return this.ctx}};var J=class{canvasWrapper;canvas;camera;viewport;renderer;config;onSizeApplied;constructor(e,t,i,s,n,r,o){this.canvasWrapper=e,this.canvas=t,this.camera=i,this.renderer=s,this.viewport=n,this.config=r,this.onSizeApplied=o}resizeWithAnimation(e,t,i,s,n){if(e<=0||t<=0)return;let r=this.config.get().size,o=(m,l,d)=>{let h=m;return l!==void 0&&(h=Math.max(l,h)),d!==void 0&&(h=Math.min(d,h)),h};e=o(e,r?.minWidth,r?.maxWidth),t=o(t,r?.minHeight,r?.maxHeight),s.animateResize(e,t,i,(m,l,d)=>this.applySize(m,l,d),n)}applySize(e,t,i){let s=Math.round(e),n=Math.round(t),r=this.viewport.dpr;this.viewport.setSize(s,n),this.canvasWrapper.style.width=`${s}px`,this.canvasWrapper.style.height=`${n}px`,this.canvas.width=s*r,this.canvas.height=n*r,this.canvas.style.width=`${s}px`,this.canvas.style.height=`${n}px`,this.camera.setCenter(i,s,n),this.renderer.resize(s,n),this.onSizeApplied()}};var K=class{constructor(e,t,i){this.camera=e;this.viewport=t;this.onAnimationFrame=i}moveAnimationId;resizeAnimationId;animateMoveTo(e,t,i=x.ANIMATION_DURATION_MS,s){if(this.cancelMove(),i<=0){let l=this.viewport.getSize();this.camera.setCenter({x:e,y:t},l.width,l.height),this.onAnimationFrame(),s?.();return}let n=this.viewport.getSize(),r=this.camera.getCenter(n.width,n.height),o=performance.now(),m=l=>{let d=l-o,h=Math.min(1,d/i),p=h<.5?2*h*h:1-Math.pow(-2*h+2,2)/2,f=r.x+(e-r.x)*p,c=r.y+(t-r.y)*p,u=this.viewport.getSize();this.camera.setCenter({x:f,y:c},u.width,u.height),this.onAnimationFrame(),h<1?this.moveAnimationId=requestAnimationFrame(m):(this.moveAnimationId=void 0,s?.())};this.moveAnimationId=requestAnimationFrame(m)}animateResize(e,t,i=x.ANIMATION_DURATION_MS,s,n){if(e<=0||t<=0)return;this.cancelResize();let r=this.viewport.getSize(),o=this.camera.getCenter(r.width,r.height);if(i<=0){s(e,t,o),n?.();return}let m=r.width,l=r.height,d=e-r.width,h=t-r.height,p=performance.now(),f=c=>{let u=c-p,g=Math.min(1,u/i),v=m+d*g,w=l+h*g;s(v,w,o),g<1?this.resizeAnimationId=requestAnimationFrame(f):(this.resizeAnimationId=void 0,n?.())};this.resizeAnimationId=requestAnimationFrame(f)}cancelMove(){this.moveAnimationId!==void 0&&(cancelAnimationFrame(this.moveAnimationId),this.moveAnimationId=void 0)}cancelResize(){this.resizeAnimationId!==void 0&&(cancelAnimationFrame(this.resizeAnimationId),this.resizeAnimationId=void 0)}cancelAll(){this.cancelMove(),this.cancelResize()}isAnimating(){return this.moveAnimationId!==void 0||this.resizeAnimationId!==void 0}};var Q=class{static createRenderer(e,t,i,s,n,r,o){switch(e){case"canvas":return new k(t,i,s,n,r,o);default:throw new Error(`Unsupported renderer type: ${e}`)}}static isSupported(e){return e==="canvas"}static getSupportedTypes(){return["canvas"]}};var ee=class{constructor(e,t,i,s,n,r,o){this.wrapper=e;this.canvas=t;this.camera=i;this.renderer=s;this.viewport=n;this.config=r;this.onCameraChange=o;this.currentDpr=this.viewport.dpr;let m=this.config.get();this.initialVisibleTiles={x:m.size.width/m.scale,y:m.size.height/m.scale},this.widthLimits={min:m.minScale*this.initialVisibleTiles.x,max:m.maxScale*this.initialVisibleTiles.x}}resizeObserver;handleWindowResize;currentDpr;initialVisibleTiles;widthLimits;onResize;start(){let e=this.config.get().responsive;if(!e)return;if(this.viewport.updateDpr(),this.currentDpr=this.viewport.dpr,e==="preserve-viewport"){let n=this.initialVisibleTiles.y/this.initialVisibleTiles.x;this.wrapper.style.width="100%",this.wrapper.style.minWidth=`${this.widthLimits.min}px`,this.wrapper.style.maxWidth=`${this.widthLimits.max}px`,this.wrapper.style.minHeight=`${this.widthLimits.min*n}px`,this.wrapper.style.maxHeight=`${this.widthLimits.max*n}px`}else{let n=this.config.get();this.wrapper.style.width="100%",this.wrapper.style.height=`${n.size.height}px`}let t=this.wrapper.getBoundingClientRect(),i=Math.round(t.width),s=Math.round(t.height);this.applySize(i,s,e),this.resizeObserver=new ResizeObserver(n=>{for(let r of n){let{width:o,height:m}=r.contentRect,l=Math.round(o),d=Math.round(m);if(l<=0||d<=0)continue;let h=this.viewport.getSize();l===h.width&&d===h.height||(this.applySize(l,d,e),this.onResize?.(),this.onCameraChange())}}),this.resizeObserver.observe(this.wrapper),this.attachDprWatcher()}stop(){this.resizeObserver&&(this.resizeObserver.unobserve(this.wrapper),this.resizeObserver.disconnect(),this.resizeObserver=void 0),this.handleWindowResize&&(window.removeEventListener("resize",this.handleWindowResize),this.handleWindowResize=void 0)}applySize(e,t,i){let s=this.viewport.dpr,n=this.viewport.getSize();if(i==="preserve-viewport"){let r=e/this.initialVisibleTiles.x,o=Math.round(this.initialVisibleTiles.y*r);t=o,this.wrapper.style.height=`${o}px`;let m=this.camera.getCenter(n.width,n.height);this.camera.setScale(r),this.camera.setCenter(m,e,t)}else{let r=e-n.width,o=t-n.height;this.camera.adjustForResize(r,o)}this.viewport.setSize(e,t),this.canvas.width=e*s,this.canvas.height=t*s,this.canvas.style.width=`${e}px`,this.canvas.style.height=`${t}px`,this.renderer.resize(e,t)}attachDprWatcher(){typeof window>"u"||(this.handleWindowResize=()=>{let e=this.currentDpr;this.viewport.updateDpr();let t=this.viewport.dpr;if(t===e)return;this.currentDpr=t;let{width:i,height:s}=this.viewport.getSize();this.canvas.width=i*t,this.canvas.height=s*t,this.canvas.style.width=`${i}px`,this.canvas.style.height=`${s}px`,this.onResize?.(),this.onCameraChange()},window.addEventListener("resize",this.handleWindowResize,{passive:!0}))}};var te=class{config;camera;viewport;coordinateTransformer;layers;renderer;events;draw;images;sizeController;animationController;responsiveWatcher;canvasWrapper;canvas;onCoordsChange;_onClick;get onClick(){return this._onClick}set onClick(e){this._onClick=e,this.events.onClick=e}_onRightClick;get onRightClick(){return this._onRightClick}set onRightClick(e){this._onRightClick=e,this.events.onRightClick=e}_onHover;get onHover(){return this._onHover}set onHover(e){this._onHover=e,this.events.onHover=e}_onMouseDown;get onMouseDown(){return this._onMouseDown}set onMouseDown(e){this._onMouseDown=e,this.events.onMouseDown=e}_onMouseUp;get onMouseUp(){return this._onMouseUp}set onMouseUp(e){this._onMouseUp=e,this.events.onMouseUp=e}_onMouseLeave;get onMouseLeave(){return this._onMouseLeave}set onMouseLeave(e){this._onMouseLeave=e,this.events.onMouseLeave=e}_onDraw;get onDraw(){return this._onDraw}set onDraw(e){this._onDraw=e,this.getCanvasRenderer().onDraw=e}_onResize;get onResize(){return this._onResize}set onResize(e){this._onResize=e,this.events.onResize=e}_onZoom;get onZoom(){return this._onZoom}set onZoom(e){this._onZoom=e,this.events.onZoom=e}constructor(e,t,i={x:0,y:0}){this.canvasWrapper=e,this.canvas=e.querySelector("canvas"),t.responsive?Object.assign(this.canvasWrapper.style,{position:"relative"}):Object.assign(this.canvasWrapper.style,{position:"relative",width:t.size.width+"px",height:t.size.height+"px"}),Object.assign(this.canvas.style,{position:"absolute",top:"0",left:"0"}),this.config=new Y(t);let s=t.renderer??"canvas",n=t.size.width/t.scale,r=t.size.height/t.scale,o=t.gridAligned&&n%2===0?Math.floor(i.x)+.5:i.x,m=t.gridAligned&&r%2===0?Math.floor(i.y)+.5:i.y,l={x:o-t.size.width/(2*t.scale),y:m-t.size.height/(2*t.scale)};this.viewport=new G(t.size.width,t.size.height),this.camera=new H(l,this.config.get().scale,this.config.get().minScale,this.config.get().maxScale,this.viewport),this.coordinateTransformer=new X(this.camera),this.animationController=new K(this.camera,this.viewport,()=>this.handleCameraChange()),this.renderer=this.createRenderer(s),this.images=new Z,this.events=new V(this.canvasWrapper,this.canvas,this.camera,this.viewport,this.config,this.coordinateTransformer,()=>this.handleCameraChange()),this.sizeController=new J(this.canvasWrapper,this.canvas,this.camera,this.renderer,this.viewport,this.config,()=>this.handleCameraChange()),this.events.setupEvents(),t.responsive&&(this.responsiveWatcher=new ee(this.canvasWrapper,this.canvas,this.camera,this.renderer,this.viewport,this.config,()=>this.handleCameraChange()),this.responsiveWatcher.onResize=()=>{this._onResize?.()},this.responsiveWatcher.start()),t.bounds&&this.camera.setBounds(t.bounds)}destroy(){this.events.destroy(),this.responsiveWatcher?.stop(),this.animationController.cancelAll(),this.draw?.destroy(),this.layers?.clear(),this.images.clear(),this.renderer.destroy()}render(){this.renderer.render()}resize(e,t,i=500,s){if(this.config.get().responsive){console.warn("Canvas Tile Engine: resize() is disabled when responsive mode is enabled. Canvas size is controlled by the wrapper element.");return}this.sizeController.resizeWithAnimation(e,t,i,this.animationController,()=>{this._onResize?.(),s?.()})}getSize(){return this.viewport.getSize()}getScale(){return this.camera.scale}setScale(e){me(e),this.camera.setScale(e),this.handleCameraChange()}zoomIn(e=1.5){let t=this.viewport.getSize();this.camera.zoomByFactor(e,t.width/2,t.height/2),this.handleCameraChange()}zoomOut(e=1.5){let t=this.viewport.getSize();this.camera.zoomByFactor(1/e,t.width/2,t.height/2),this.handleCameraChange()}getConfig(){let e=this.config.get(),t=this.viewport.getSize();return{...e,scale:this.camera.scale,size:{...t}}}getCenterCoords(){let e=this.viewport.getSize();return this.camera.getCenter(e.width,e.height)}getVisibleBounds(){let e=this.viewport.getSize();return this.camera.getVisibleBounds(e.width,e.height)}updateCoords(e){re(e.x,e.y);let t=this.viewport.getSize();this.camera.setCenter(e,t.width,t.height),this.handleCameraChange()}goCoords(e,t,i=500,s){re(e,t),this.animationController.animateMoveTo(e,t,i,s)}setEventHandlers(e){this.config.updateEventHandlers(e)}setBounds(e){this.config.updateBounds(e),this.camera.setBounds(e),this.render()}addDrawFunction(e,t=1){return this.ensureCanvasDraw().addDrawFunction(e,t)}drawRect(e,t=1){return this.ensureCanvasDraw().drawRect(e,t)}drawStaticRect(e,t,i=1){return this.ensureCanvasDraw().drawStaticRect(e,t,i)}drawStaticCircle(e,t,i=1){return this.ensureCanvasDraw().drawStaticCircle(e,t,i)}drawStaticImage(e,t,i=1){return this.ensureCanvasDraw().drawStaticImage(e,t,i)}clearStaticCache(e){this.ensureCanvasDraw().clearStaticCache(e)}drawLine(e,t,i=1){return this.ensureCanvasDraw().drawLine(e,t,i)}drawCircle(e,t=1){return this.ensureCanvasDraw().drawCircle(e,t)}drawText(e,t=2){return this.ensureCanvasDraw().drawText(e,t)}drawPath(e,t,i=1){return this.ensureCanvasDraw().drawPath(e,t,i)}drawImage(e,t=1){return this.ensureCanvasDraw().drawImage(e,t)}drawGridLines(e,t=1,i="black",s=0){return this.ensureCanvasDraw().drawGridLines(e,{lineWidth:t,strokeStyle:i},s)}removeLayerHandle(e){if(!this.layers)throw new Error("removeLayerHandle is only available when renderer is set to 'canvas'.");this.layers.remove(e)}clearLayer(e){if(!this.layers)throw new Error("clearLayer is only available when renderer is set to 'canvas'.");this.layers.clear(e)}clearAll(){if(!this.layers)throw new Error("clearAll is only available when renderer is set to 'canvas'.");this.layers.clear()}createRenderer(e){return e==="canvas"&&(this.layers=new q,this.draw=new N(this.layers,this.coordinateTransformer,this.camera)),Q.createRenderer(e??"canvas",this.canvas,this.camera,this.coordinateTransformer,this.config,this.viewport,this.layers)}ensureCanvasDraw(){if(!this.draw)throw new Error("Draw helpers are only available when renderer is set to 'canvas'.");return this.draw}getCanvasRenderer(){if(!(this.renderer instanceof k))throw new Error("Canvas renderer required for this operation.");return this.renderer}handleCameraChange(){this.onCoordsChange&&this.onCoordsChange(this.getCenterCoords()),this.render()}};function ve(a){return{size:{width:a.columns*a.cellSize,height:a.rows*a.cellSize},scale:a.cellSize}}0&&(module.exports={COORDINATE_OVERLAY,CanvasTileEngine,DEBUG_HUD,DEFAULT_VALUES,RENDER_DEFAULTS,SCALE_LIMITS,SIZE_LIMITS,VISIBILITY_BUFFER,gridToSize});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|