@canvas-tile-engine/core 0.1.0 → 0.3.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 CHANGED
@@ -1,43 +1,223 @@
1
1
  /**
2
- * Simple image loader with in-memory caching to avoid duplicate network requests.
2
+ * Normalizes and stores grid engine configuration with safe defaults.
3
3
  */
4
- declare class ImageLoader {
5
- private cache;
6
- private inflight;
7
- private listeners;
4
+ declare class Config {
5
+ private config;
8
6
  /**
9
- * Register a callback fired when a new image finishes loading.
7
+ * Create a config store with defaults merged from the provided partial config.
8
+ * @param config Incoming configuration values.
9
+ * @throws {ConfigValidationError} If any config value is invalid.
10
10
  */
11
- onLoad(cb: () => void): () => boolean;
12
- private notifyLoaded;
11
+ constructor(config: CanvasTileEngineConfig);
13
12
  /**
14
- * Load an image, reusing cache when possible.
15
- * @param src Image URL.
16
- * @param retry How many times to retry on error (default: 1).
17
- * @returns Promise resolving to the loaded image element.
13
+ * Get a defensive copy of the current configuration.
14
+ * @returns Normalized configuration snapshot e.g. `{ scale: 1, size: { width: 800, height: 600 }, ... }`.
18
15
  */
19
- load(src: string, retry?: number): Promise<HTMLImageElement>;
16
+ get(): Readonly<Required<CanvasTileEngineConfig>>;
20
17
  /**
21
- * Get a cached image without loading.
22
- * @param src Image URL key.
18
+ * Update event handlers at runtime.
19
+ * @param handlers Partial event handlers to update.
23
20
  */
24
- get(src: string): HTMLImageElement | undefined;
21
+ updateEventHandlers(handlers: Partial<EventHandlers>): void;
25
22
  /**
26
- * Check if an image is already cached.
27
- * @param src Image URL key.
23
+ * Update map bounds at runtime.
24
+ * @param bounds New boundary limits. Use Infinity/-Infinity to remove limits on specific axes.
25
+ * @throws {ConfigValidationError} If bounds are invalid.
28
26
  */
29
- has(src: string): boolean;
27
+ updateBounds(bounds: {
28
+ minX: number;
29
+ maxX: number;
30
+ minY: number;
31
+ maxY: number;
32
+ }): void;
33
+ }
34
+
35
+ /**
36
+ * Camera contract used by rendering and coordinate transforms.
37
+ */
38
+ interface ICamera$1 {
39
+ /** Current top-left world x coordinate. */
40
+ readonly x: number;
41
+ /** Current top-left world y coordinate. */
42
+ readonly y: number;
43
+ /** Current zoom scale. */
44
+ readonly scale: number;
30
45
  /**
31
- * Clear all cached and inflight images/listeners to free memory.
46
+ * Pan the camera by screen-space deltas.
47
+ * @param deltaScreenX X delta in pixels.
48
+ * @param deltaScreenY Y delta in pixels.
32
49
  */
33
- clear(): void;
50
+ pan(deltaScreenX: number, deltaScreenY: number): void;
51
+ /**
52
+ * Zoom around a mouse position.
53
+ * @param mouseX Mouse X relative to viewport.
54
+ * @param mouseY Mouse Y relative to viewport.
55
+ * @param deltaY Wheel delta (positive scroll down).
56
+ * @param canvasRect Canvas bounding rect for mouse offset.
57
+ */
58
+ zoom(mouseX: number, mouseY: number, deltaY: number, canvasRect: DOMRect): void;
59
+ /**
60
+ * Canvas center coordinates in world space.
61
+ * @param canvasWidth Canvas width in pixels.
62
+ * @param canvasHeight Canvas height in pixels.
63
+ * @returns Center point in world coordinates.
64
+ */
65
+ getCenter(canvasWidth: number, canvasHeight: number): Coords;
66
+ /**
67
+ * Set top-left coordinates based on a target center.
68
+ * @param center Desired center in world space.
69
+ * @param canvasWidth Canvas width in pixels.
70
+ * @param canvasHeight Canvas height in pixels.
71
+ */
72
+ setCenter(center: Coords, canvasWidth: number, canvasHeight: number): void;
73
+ /**
74
+ * Adjust view so center stays stable on resize.
75
+ * @param deltaWidthPx Change in canvas width (pixels).
76
+ * @param deltaHeightPx Change in canvas height (pixels).
77
+ */
78
+ adjustForResize(deltaWidthPx: number, deltaHeightPx: number): void;
79
+ /**
80
+ * Zoom by a scale factor around a specific point (for pinch-to-zoom).
81
+ * @param factor Scale multiplier (>1 zooms in, <1 zooms out).
82
+ * @param centerX Center X in screen coordinates.
83
+ * @param centerY Center Y in screen coordinates.
84
+ */
85
+ zoomByFactor(factor: number, centerX: number, centerY: number): void;
86
+ /**
87
+ * Set the camera scale directly, clamped to min/max bounds.
88
+ * @param newScale The desired scale value.
89
+ */
90
+ setScale(newScale: number): void;
91
+ /**
92
+ * Get the visible world coordinate bounds of the viewport.
93
+ * @param canvasWidth Canvas width in pixels.
94
+ * @param canvasHeight Canvas height in pixels.
95
+ * @returns Visible bounds with min/max coordinates (floored/ceiled to cell boundaries).
96
+ */
97
+ getVisibleBounds(canvasWidth: number, canvasHeight: number): {
98
+ minX: number;
99
+ maxX: number;
100
+ minY: number;
101
+ maxY: number;
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Transforms coordinates between world space and screen space using the active camera.
107
+ */
108
+ declare class CoordinateTransformer {
109
+ private camera;
110
+ /**
111
+ * @param camera Camera providing origin and scaling for transformations.
112
+ */
113
+ constructor(camera: ICamera$1);
114
+ /**
115
+ * Convert a world grid coordinate to screen pixels, accounting for camera offset and scale.
116
+ * @param worldX Grid X in world space (tile index).
117
+ * @param worldY Grid Y in world space (tile index).
118
+ * @returns Screen-space coordinates in pixels. e.g., (e.g. `{ x: 100.5, y: 200.5 }`).
119
+ */
120
+ worldToScreen(worldX: number, worldY: number): Coords;
121
+ /**
122
+ * Convert screen pixel coordinates back to world space grid coordinates.
123
+ * @param screenX X coordinate in screen space (pixels).
124
+ * @param screenY Y coordinate in screen space (pixels).
125
+ * @returns World-space grid coordinates. (e.g. `{ x: 10, y: 20 }`).
126
+ */
127
+ screenToWorld(screenX: number, screenY: number): Coords;
34
128
  }
35
129
 
36
- interface LayerHandle {
37
- layer: number;
38
- id: symbol;
130
+ /**
131
+ * Holds mutable viewport size for runtime changes (resize, layout).
132
+ * Also tracks device pixel ratio for HiDPI/Retina display support.
133
+ */
134
+ declare class ViewportState {
135
+ private width;
136
+ private height;
137
+ private _dpr;
138
+ constructor(width: number, height: number);
139
+ getSize(): {
140
+ width: number;
141
+ height: number;
142
+ };
143
+ setSize(width: number, height: number): void;
144
+ /**
145
+ * Get the current device pixel ratio.
146
+ * Used for HiDPI/Retina display rendering.
147
+ */
148
+ get dpr(): number;
149
+ /**
150
+ * Update DPR (useful when window moves between displays).
151
+ */
152
+ updateDpr(): void;
39
153
  }
40
154
 
155
+ type onDrawCallback = (ctx: unknown, info: {
156
+ scale: number;
157
+ width: number;
158
+ height: number;
159
+ coords: Coords;
160
+ }) => void;
161
+ type MouseEventCallback = (coords: {
162
+ raw: Coords;
163
+ snapped: Coords;
164
+ }, mouse: {
165
+ raw: Coords;
166
+ snapped: Coords;
167
+ }, client: {
168
+ raw: Coords;
169
+ snapped: Coords;
170
+ }) => void;
171
+ type onClickCallback = MouseEventCallback;
172
+ type onHoverCallback = MouseEventCallback;
173
+ type onMouseDownCallback = MouseEventCallback;
174
+ type onMouseUpCallback = MouseEventCallback;
175
+ type onMouseLeaveCallback = MouseEventCallback;
176
+ type onRightClickCallback = MouseEventCallback;
177
+ type onZoomCallback = (scale: number) => void;
178
+
179
+ type DrawObject = {
180
+ x: number;
181
+ y: number;
182
+ size?: number;
183
+ origin?: {
184
+ mode?: "cell" | "self";
185
+ x?: number;
186
+ y?: number;
187
+ };
188
+ style?: {
189
+ fillStyle?: string;
190
+ strokeStyle?: string;
191
+ lineWidth?: number;
192
+ };
193
+ /** Rotation angle in degrees (0 = no rotation, positive = clockwise) */
194
+ rotate?: number;
195
+ /** Border radius in pixels. Single value for all corners, or array for [topLeft, topRight, bottomRight, bottomLeft] */
196
+ radius?: number | number[];
197
+ };
198
+ type Rect = DrawObject;
199
+ type Circle = Omit<DrawObject, "rotate" | "radius">;
200
+ type ImageItem = Omit<DrawObject, "style"> & {
201
+ img: HTMLImageElement;
202
+ };
203
+ type Text = Omit<DrawObject, "radius" | "size"> & {
204
+ text: string;
205
+ /** Font size in world units (scales with zoom). Default: 1 */
206
+ size?: number;
207
+ style?: {
208
+ fillStyle?: string;
209
+ /** Font family (default: "sans-serif") */
210
+ fontFamily?: string;
211
+ textAlign?: CanvasTextAlign;
212
+ textBaseline?: CanvasTextBaseline;
213
+ };
214
+ };
215
+ type Line = {
216
+ from: Coords;
217
+ to: Coords;
218
+ };
219
+ type Path = Coords[];
220
+
41
221
  type CanvasTileEngineConfig = {
42
222
  renderer?: "canvas";
43
223
  scale: number;
@@ -100,70 +280,121 @@ type EventHandlers = {
100
280
  zoom?: boolean;
101
281
  resize?: boolean;
102
282
  };
283
+
103
284
  type Coords = {
104
285
  x: number;
105
286
  y: number;
106
287
  };
107
- type onDrawCallback = (ctx: CanvasRenderingContext2D, info: {
108
- scale: number;
288
+ interface LineStyle {
289
+ strokeStyle?: string;
290
+ lineWidth?: number;
291
+ }
292
+ interface Bounds {
293
+ minX: number;
294
+ maxX: number;
295
+ minY: number;
296
+ maxY: number;
297
+ }
298
+ interface ViewportBounds {
299
+ left: number;
300
+ top: number;
109
301
  width: number;
110
302
  height: number;
111
- coords: Coords;
112
- }) => void;
113
- type MouseEventCallback = (coords: {
114
- raw: Coords;
115
- snapped: Coords;
116
- }, mouse: {
117
- raw: Coords;
118
- snapped: Coords;
119
- }, client: {
120
- raw: Coords;
121
- snapped: Coords;
122
- }) => void;
123
- type onClickCallback = MouseEventCallback;
124
- type onHoverCallback = MouseEventCallback;
125
- type onMouseDownCallback = MouseEventCallback;
126
- type onMouseUpCallback = MouseEventCallback;
127
- type onMouseLeaveCallback = MouseEventCallback;
128
- type onRightClickCallback = MouseEventCallback;
129
- type onZoomCallback = (scale: number) => void;
130
- type DrawObject = {
131
- x: number;
132
- y: number;
133
- size?: number;
134
- origin?: {
135
- mode?: "cell" | "self";
136
- x?: number;
137
- y?: number;
138
- };
139
- style?: {
140
- fillStyle?: string;
141
- strokeStyle?: string;
142
- lineWidth?: number;
143
- };
144
- /** Rotation angle in degrees (0 = no rotation, positive = clockwise) */
145
- rotate?: number;
146
- /** Border radius in pixels. Single value for all corners, or array for [topLeft, topRight, bottomRight, bottomLeft] */
147
- radius?: number | number[];
148
- };
149
- type Rect = DrawObject;
150
- type Line = {
151
- from: Coords;
152
- to: Coords;
153
- style: {
154
- strokeStyle?: string;
155
- lineWidth?: number;
156
- };
157
- };
158
- type Circle = Omit<DrawObject, "rotate" | "radius">;
159
- type Text = {
160
- coords: Coords;
161
- text: string;
162
- };
163
- type Path = Coords[];
164
- type ImageItem = Omit<DrawObject, "style"> & {
165
- img: HTMLImageElement;
166
- };
303
+ }
304
+ interface RendererDependencies {
305
+ wrapper: HTMLDivElement;
306
+ camera: ICamera;
307
+ viewport: ViewportState;
308
+ config: Config;
309
+ transformer: CoordinateTransformer;
310
+ }
311
+ interface IRenderer {
312
+ init(deps: RendererDependencies): void;
313
+ render(): void;
314
+ resize(width: number, height: number): void;
315
+ resizeWithAnimation(width: number, height: number, durationMs: number, onComplete?: () => void): void;
316
+ destroy(): void;
317
+ getDrawAPI(): IDrawAPI;
318
+ getImageLoader(): IImageLoader;
319
+ setupEvents(): void;
320
+ onClick?: onClickCallback;
321
+ onRightClick?: onRightClickCallback;
322
+ onHover?: onHoverCallback;
323
+ onMouseDown?: onMouseDownCallback;
324
+ onMouseUp?: onMouseUpCallback;
325
+ onMouseLeave?: onMouseLeaveCallback;
326
+ onZoom?: onZoomCallback;
327
+ onResize?: () => void;
328
+ onCameraChange?: () => void;
329
+ onDraw?: onDrawCallback;
330
+ }
331
+ interface IDrawAPI {
332
+ addDrawFunction(fn: (ctx: unknown, coords: Coords, config: Required<CanvasTileEngineConfig>) => void, layer?: number): DrawHandle;
333
+ drawRect(items: Rect | Rect[], layer?: number): DrawHandle;
334
+ drawCircle(items: Circle | Circle[], layer?: number): DrawHandle;
335
+ drawLine(items: Line | Line[], style?: LineStyle, layer?: number): DrawHandle;
336
+ drawText(items: Text | Text[], layer?: number): DrawHandle;
337
+ drawImage(items: ImageItem | ImageItem[], layer?: number): DrawHandle;
338
+ drawPath(items: Path | Path[], style?: LineStyle, layer?: number): DrawHandle;
339
+ drawGridLines(cellSize: number, style: {
340
+ lineWidth: number;
341
+ strokeStyle: string;
342
+ }, layer?: number): DrawHandle;
343
+ drawStaticRect(items: Rect[], cacheKey: string, layer?: number): DrawHandle;
344
+ drawStaticCircle(items: Circle[], cacheKey: string, layer?: number): DrawHandle;
345
+ drawStaticImage(items: ImageItem[], cacheKey: string, layer?: number): DrawHandle;
346
+ removeDrawHandle(handle: DrawHandle): void;
347
+ clearLayer(layer: number): void;
348
+ clearAll(): void;
349
+ clearStaticCache(cacheKey?: string): void;
350
+ }
351
+ interface DrawHandle {
352
+ readonly id: symbol;
353
+ readonly layer: number;
354
+ }
355
+ /**
356
+ * Platform-agnostic image loader interface.
357
+ * Each renderer implements this with platform-specific image handling.
358
+ */
359
+ interface IImageLoader<TImage = unknown> {
360
+ /**
361
+ * Load an image from URL, with caching.
362
+ * @param src Image URL.
363
+ * @param retry Retry count on failure.
364
+ */
365
+ load(src: string, retry?: number): Promise<TImage>;
366
+ /**
367
+ * Get a cached image without loading.
368
+ */
369
+ get(src: string): TImage | undefined;
370
+ /**
371
+ * Check if an image is already cached.
372
+ */
373
+ has(src: string): boolean;
374
+ /**
375
+ * Clear all cached images.
376
+ */
377
+ clear(): void;
378
+ /**
379
+ * Register a callback fired when a new image finishes loading.
380
+ * @returns Unsubscribe function.
381
+ */
382
+ onLoad(cb: () => void): () => void;
383
+ }
384
+ interface ICamera {
385
+ readonly x: number;
386
+ readonly y: number;
387
+ readonly scale: number;
388
+ pan(dx: number, dy: number): void;
389
+ zoom(screenX: number, screenY: number, deltaY: number, bounds: ViewportBounds): void;
390
+ zoomByFactor(factor: number, centerX: number, centerY: number): void;
391
+ setScale(scale: number): void;
392
+ setCenter(center: Coords, viewportWidth: number, viewportHeight: number): void;
393
+ getCenter(viewportWidth: number, viewportHeight: number): Coords;
394
+ getVisibleBounds(viewportWidth: number, viewportHeight: number): Bounds;
395
+ setBounds(bounds: Bounds): void;
396
+ adjustForResize(dw: number, dh: number): void;
397
+ }
167
398
 
168
399
  /**
169
400
  * Core engine wiring camera, config, renderer, events, and draw helpers.
@@ -173,52 +404,163 @@ declare class CanvasTileEngine {
173
404
  private camera;
174
405
  private viewport;
175
406
  private coordinateTransformer;
176
- private layers?;
177
407
  private renderer;
178
- private events;
179
- private draw?;
180
- images: ImageLoader;
181
- private sizeController;
182
408
  private animationController;
183
- private responsiveWatcher?;
184
409
  canvasWrapper: HTMLDivElement;
185
410
  canvas: HTMLCanvasElement;
186
- /** Callback: center coordinates change */
411
+ /**
412
+ * Image loader for loading and caching images.
413
+ * Uses the renderer's platform-specific implementation.
414
+ */
415
+ get images(): IImageLoader;
416
+ /**
417
+ * Callback when center coordinates change (pan or zoom).
418
+ * @param coords - Center world coordinates: `{ x, y }`
419
+ * @example
420
+ * ```ts
421
+ * engine.onCoordsChange = (coords) => {
422
+ * console.log(`Center: ${coords.x}, ${coords.y}`);
423
+ * };
424
+ * ```
425
+ */
187
426
  onCoordsChange?: (coords: Coords) => void;
188
427
  private _onClick?;
428
+ /**
429
+ * Callback when a tile is clicked (mouse or touch tap).
430
+ * @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
431
+ * @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
432
+ * @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
433
+ * @example
434
+ * ```ts
435
+ * engine.onClick = (coords, mouse, client) => {
436
+ * console.log(`Clicked tile: ${coords.snapped.x}, ${coords.snapped.y}`);
437
+ * };
438
+ * ```
439
+ */
189
440
  get onClick(): onClickCallback | undefined;
190
441
  set onClick(cb: onClickCallback | undefined);
191
442
  private _onRightClick?;
443
+ /**
444
+ * Callback when a tile is right-clicked.
445
+ * @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
446
+ * @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
447
+ * @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
448
+ * @example
449
+ * ```ts
450
+ * engine.onRightClick = (coords) => {
451
+ * showContextMenu(coords.snapped.x, coords.snapped.y);
452
+ * };
453
+ * ```
454
+ */
192
455
  get onRightClick(): onRightClickCallback | undefined;
193
456
  set onRightClick(cb: onRightClickCallback | undefined);
194
457
  private _onHover?;
458
+ /**
459
+ * Callback when hovering over tiles.
460
+ * @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
461
+ * @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
462
+ * @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
463
+ * @example
464
+ * ```ts
465
+ * engine.onHover = (coords) => {
466
+ * setHoveredTile({ x: coords.snapped.x, y: coords.snapped.y });
467
+ * };
468
+ * ```
469
+ */
195
470
  get onHover(): onHoverCallback | undefined;
196
471
  set onHover(cb: onHoverCallback | undefined);
197
472
  private _onMouseDown?;
473
+ /**
474
+ * Callback on mouse/touch down.
475
+ * @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
476
+ * @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
477
+ * @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
478
+ * @example
479
+ * ```ts
480
+ * engine.onMouseDown = (coords) => {
481
+ * startPainting(coords.snapped.x, coords.snapped.y);
482
+ * };
483
+ * ```
484
+ */
198
485
  get onMouseDown(): onMouseDownCallback | undefined;
199
486
  set onMouseDown(cb: onMouseDownCallback | undefined);
200
487
  private _onMouseUp?;
488
+ /**
489
+ * Callback on mouse/touch up.
490
+ * @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
491
+ * @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
492
+ * @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
493
+ * @example
494
+ * ```ts
495
+ * engine.onMouseUp = (coords) => {
496
+ * stopPainting();
497
+ * };
498
+ * ```
499
+ */
201
500
  get onMouseUp(): onMouseUpCallback | undefined;
202
501
  set onMouseUp(cb: onMouseUpCallback | undefined);
203
502
  private _onMouseLeave?;
503
+ /**
504
+ * Callback when mouse/touch leaves the canvas.
505
+ * @param coords - World coordinates: `raw` (exact), `snapped` (floored to tile)
506
+ * @param mouse - Canvas-relative position: `raw` (exact), `snapped` (tile-aligned)
507
+ * @param client - Viewport position: `raw` (exact), `snapped` (tile-aligned)
508
+ * @example
509
+ * ```ts
510
+ * engine.onMouseLeave = () => {
511
+ * clearHoveredTile();
512
+ * };
513
+ * ```
514
+ */
204
515
  get onMouseLeave(): onMouseLeaveCallback | undefined;
205
516
  set onMouseLeave(cb: onMouseLeaveCallback | undefined);
206
517
  private _onDraw?;
518
+ /**
519
+ * Callback after each draw frame. Use for custom canvas drawing.
520
+ * @param ctx - The canvas 2D rendering context
521
+ * @param info - Frame info: `scale`, `width`, `height`, `coords` (center)
522
+ * @example
523
+ * ```ts
524
+ * engine.onDraw = (ctx, info) => {
525
+ * ctx.fillStyle = "red";
526
+ * ctx.fillText(`Scale: ${info.scale}`, 10, 20);
527
+ * };
528
+ * ```
529
+ */
207
530
  get onDraw(): onDrawCallback | undefined;
208
531
  set onDraw(cb: onDrawCallback | undefined);
209
532
  private _onResize?;
533
+ /**
534
+ * Callback on canvas resize.
535
+ * @example
536
+ * ```ts
537
+ * engine.onResize = () => {
538
+ * console.log("Canvas resized:", engine.getSize());
539
+ * };
540
+ * ```
541
+ */
210
542
  get onResize(): (() => void) | undefined;
211
543
  set onResize(cb: (() => void) | undefined);
212
544
  private _onZoom?;
213
- /** Callback: zoom level changes (wheel or pinch) */
545
+ /**
546
+ * Callback when zoom level changes (wheel or pinch).
547
+ * @param scale - The new scale value
548
+ * @example
549
+ * ```ts
550
+ * engine.onZoom = (scale) => {
551
+ * console.log(`Zoom level: ${scale}`);
552
+ * };
553
+ * ```
554
+ */
214
555
  get onZoom(): ((scale: number) => void) | undefined;
215
556
  set onZoom(cb: ((scale: number) => void) | undefined);
216
557
  /**
217
- * @param canvas Target canvas element.
558
+ * @param canvasWrapper Canvas wrapper element containing a canvas child.
218
559
  * @param config Initial engine configuration.
560
+ * @param renderer The renderer implementation to use (e.g., RendererCanvas).
219
561
  * @param center Initial center in world space.
220
562
  */
221
- constructor(canvasWrapper: HTMLDivElement, config: CanvasTileEngineConfig, center?: Coords);
563
+ constructor(canvasWrapper: HTMLDivElement, config: CanvasTileEngineConfig, renderer: IRenderer, center?: Coords);
222
564
  /** Tear down listeners and observers. */
223
565
  destroy(): void;
224
566
  /** Render a frame using the active renderer. */
@@ -247,6 +589,7 @@ declare class CanvasTileEngine {
247
589
  /**
248
590
  * Set the canvas scale directly, clamped to min/max bounds.
249
591
  * @param newScale The desired scale value.
592
+ * @throws {ConfigValidationError} If scale is not a positive finite number.
250
593
  */
251
594
  setScale(newScale: number): void;
252
595
  /**
@@ -282,7 +625,11 @@ declare class CanvasTileEngine {
282
625
  minY: number;
283
626
  maxY: number;
284
627
  };
285
- /** Set center coordinates from outside (adjusts the camera accordingly). */
628
+ /**
629
+ * Set center coordinates from outside (adjusts the camera accordingly).
630
+ * @param newCenter The new center coordinates.
631
+ * @throws {ConfigValidationError} If coordinates are not finite numbers.
632
+ */
286
633
  updateCoords(newCenter: Coords): void;
287
634
  /**
288
635
  * Smoothly move the camera center to target coordinates over the given duration.
@@ -290,6 +637,7 @@ declare class CanvasTileEngine {
290
637
  * @param y Target world y.
291
638
  * @param durationMs Animation duration in milliseconds (default: 500ms). Set to 0 for instant move.
292
639
  * @param onComplete Optional callback fired when animation completes.
640
+ * @throws {ConfigValidationError} If coordinates are not finite numbers.
293
641
  */
294
642
  goCoords(x: number, y: number, durationMs?: number, onComplete?: () => void): void;
295
643
  /**
@@ -331,20 +679,14 @@ declare class CanvasTileEngine {
331
679
  maxY: number;
332
680
  }): void;
333
681
  /**
334
- * Register a generic draw callback (canvas renderer only).
335
- * @param fn Callback invoked with context, top-left coords, and config.
336
- * @param layer Layer order (lower draws first).
337
- */
338
- addDrawFunction(fn: (ctx: CanvasRenderingContext2D, coords: Coords, config: Required<CanvasTileEngineConfig>) => void, layer?: number): LayerHandle;
339
- /**
340
- * Draw one or many rectangles in world space (canvas renderer only).
682
+ * Draw one or many rectangles in world space.
341
683
  * Supports rotation via the `rotate` property (degrees, positive = clockwise).
342
684
  * @param items Rectangle definitions.
343
685
  * @param layer Layer order (lower draws first).
344
686
  */
345
- drawRect(items: DrawObject | Array<DrawObject>, layer?: number): LayerHandle;
687
+ drawRect(items: Rect | Array<Rect>, layer?: number): DrawHandle;
346
688
  /**
347
- * Draw rectangles with pre-rendering cache (canvas renderer only).
689
+ * Draw rectangles with pre-rendering cache.
348
690
  * Renders all items once to an offscreen canvas, then blits the visible portion each frame.
349
691
  * Ideal for large static datasets like mini-maps where items don't change.
350
692
  * Supports rotation via the `rotate` property (degrees, positive = clockwise).
@@ -352,18 +694,18 @@ declare class CanvasTileEngine {
352
694
  * @param cacheKey Unique key for this cache (e.g., "minimap-items").
353
695
  * @param layer Layer order (lower draws first).
354
696
  */
355
- drawStaticRect(items: Array<DrawObject>, cacheKey: string, layer?: number): LayerHandle;
697
+ drawStaticRect(items: Array<Rect>, cacheKey: string, layer?: number): DrawHandle;
356
698
  /**
357
- * Draw circles with pre-rendering cache (canvas renderer only).
699
+ * Draw circles with pre-rendering cache.
358
700
  * Renders all items once to an offscreen canvas, then blits the visible portion each frame.
359
701
  * Ideal for large static datasets like mini-maps where items don't change.
360
702
  * @param items Array of circle definitions.
361
703
  * @param cacheKey Unique key for this cache (e.g., "minimap-circles").
362
704
  * @param layer Layer order (lower draws first).
363
705
  */
364
- drawStaticCircle(items: Array<DrawObject>, cacheKey: string, layer?: number): LayerHandle;
706
+ drawStaticCircle(items: Array<Circle>, cacheKey: string, layer?: number): DrawHandle;
365
707
  /**
366
- * Draw images with pre-rendering cache (canvas renderer only).
708
+ * Draw images with pre-rendering cache.
367
709
  * Renders all items once to an offscreen canvas, then blits the visible portion each frame.
368
710
  * Ideal for large static datasets like terrain tiles or static decorations.
369
711
  * Supports rotation via the `rotate` property (degrees, positive = clockwise).
@@ -371,91 +713,91 @@ declare class CanvasTileEngine {
371
713
  * @param cacheKey Unique key for this cache (e.g., "terrain-cache").
372
714
  * @param layer Layer order (lower draws first).
373
715
  */
374
- drawStaticImage(items: Array<Omit<DrawObject, "style"> & {
375
- img: HTMLImageElement;
376
- }>, cacheKey: string, layer?: number): LayerHandle;
716
+ drawStaticImage(items: Array<ImageItem>, cacheKey: string, layer?: number): DrawHandle;
377
717
  /**
378
718
  * Clear a static rendering cache.
379
719
  * @param cacheKey The cache key to clear, or undefined to clear all caches.
380
720
  */
381
721
  clearStaticCache(cacheKey?: string): void;
382
722
  /**
383
- * Draw one or many lines between world points (canvas renderer only).
723
+ * Draw one or many lines between world points.
384
724
  * @param items Line segments.
385
725
  * @param style Line style overrides.
386
726
  * @param layer Layer order.
387
727
  */
388
- drawLine(items: Array<{
389
- from: Coords;
390
- to: Coords;
391
- }> | {
392
- from: Coords;
393
- to: Coords;
394
- }, style?: {
728
+ drawLine(items: Array<Line> | Line, style?: {
395
729
  strokeStyle?: string;
396
730
  lineWidth?: number;
397
- }, layer?: number): LayerHandle;
731
+ }, layer?: number): DrawHandle;
398
732
  /**
399
- * Draw one or many circles sized in world units (canvas renderer only).
733
+ * Draw one or many circles sized in world units.
400
734
  * @param items Circle definitions.
401
735
  * @param layer Layer order.
402
736
  */
403
- drawCircle(items: DrawObject | Array<DrawObject>, layer?: number): LayerHandle;
737
+ drawCircle(items: Circle | Array<Circle>, layer?: number): DrawHandle;
404
738
  /**
405
- * Draw one or many texts at world positions (canvas renderer only).
406
- * @param items Text definitions.
407
- * @param style Text style overrides.
739
+ * Draw one or many texts at world positions.
740
+ * @param items Text definitions with position, text, size, and style.
408
741
  * @param layer Layer order.
742
+ * @example
743
+ * ```ts
744
+ * engine.drawText({
745
+ * x: 0,
746
+ * y: 0,
747
+ * text: "Hello",
748
+ * size: 1, // 1 tile height
749
+ * style: { fillStyle: "black", fontFamily: "Arial" }
750
+ * });
751
+ *
752
+ * // Multiple texts
753
+ * engine.drawText([
754
+ * { x: 0, y: 0, text: "A", size: 2 },
755
+ * { x: 1, y: 0, text: "B", size: 2 }
756
+ * ]);
757
+ * ```
409
758
  */
410
- drawText(items: Array<{
411
- coords: Coords;
412
- text: string;
413
- }> | {
414
- coords: Coords;
415
- text: string;
416
- }, style?: {
417
- fillStyle?: string;
418
- font?: string;
419
- textAlign?: CanvasTextAlign;
420
- textBaseline?: CanvasTextBaseline;
421
- }, layer?: number): LayerHandle;
759
+ drawText(items: Array<Text> | Text, layer?: number): DrawHandle;
422
760
  /**
423
- * Draw one or many polylines through world points (canvas renderer only).
761
+ * Draw one or many polylines through world points.
424
762
  * @param items Polyline point collections.
425
763
  * @param style Stroke style overrides.
426
764
  * @param layer Layer order.
427
765
  */
428
- drawPath(items: Array<Coords[]> | Coords[], style?: {
766
+ drawPath(items: Array<Path> | Path, style?: {
429
767
  strokeStyle?: string;
430
768
  lineWidth?: number;
431
- }, layer?: number): LayerHandle;
769
+ }, layer?: number): DrawHandle;
432
770
  /**
433
- * Draw one or many images scaled in world units (canvas renderer only).
771
+ * Draw one or many images scaled in world units.
434
772
  * Supports rotation via the `rotate` property (degrees, positive = clockwise).
435
773
  * @param items Image definitions.
436
774
  * @param layer Layer order.
437
775
  */
438
- drawImage(items: Array<Omit<DrawObject, "style"> & {
439
- img: HTMLImageElement;
440
- }> | (Omit<DrawObject, "style"> & {
441
- img: HTMLImageElement;
442
- }), layer?: number): LayerHandle;
776
+ drawImage(items: Array<ImageItem> | ImageItem, layer?: number): DrawHandle;
443
777
  /**
444
- * Draw grid lines at specified cell size (canvas renderer only).
778
+ * Draw grid lines at specified cell size.
445
779
  * @param cellSize Size of each grid cell in world units.
446
780
  * @example
447
781
  * ```ts
448
782
  * engine.drawGridLines(50);
449
783
  * ```
450
784
  */
451
- drawGridLines(cellSize: number, lineWidth?: number, strokeStyle?: string, layer?: number): LayerHandle;
785
+ drawGridLines(cellSize: number, lineWidth?: number, strokeStyle?: string, layer?: number): DrawHandle;
786
+ /**
787
+ * Register a custom draw function for complete rendering control.
788
+ * Useful for complex or one-off drawing operations.
789
+ * @param fn Function receiving canvas context, top-left coords, and config.
790
+ * @param layer Layer index (default 1).
791
+ * @returns DrawHandle for removal.
792
+ */
793
+ addDrawFunction(fn: (ctx: unknown, coords: Coords, config: Required<CanvasTileEngineConfig>) => void, layer?: number): DrawHandle;
452
794
  /**
453
- * Remove a specific draw callback by handle (canvas renderer only).
795
+ * Remove a specific draw callback by handle.
454
796
  * Does not clear other callbacks on the same layer.
455
797
  */
456
- removeLayerHandle(handle: LayerHandle): void;
798
+ removeDrawHandle(handle: DrawHandle): void;
457
799
  /**
458
- * Clear all draw callbacks from a specific layer (canvas renderer only).
800
+ * Clear all draw callbacks from a specific layer.
459
801
  * Use this before redrawing dynamic content to prevent accumulation.
460
802
  * @param layer Layer index to clear.
461
803
  * @example
@@ -467,7 +809,7 @@ declare class CanvasTileEngine {
467
809
  */
468
810
  clearLayer(layer: number): void;
469
811
  /**
470
- * Clear all draw callbacks from all layers (canvas renderer only).
812
+ * Clear all draw callbacks from all layers.
471
813
  * Useful for complete scene reset.
472
814
  * @example
473
815
  * ```ts
@@ -476,13 +818,6 @@ declare class CanvasTileEngine {
476
818
  * ```
477
819
  */
478
820
  clearAll(): void;
479
- /**
480
- * Build the active renderer based on config.
481
- * @param type Renderer type requested.
482
- */
483
- private createRenderer;
484
- private ensureCanvasDraw;
485
- private getCanvasRenderer;
486
821
  private handleCameraChange;
487
822
  }
488
823
 
@@ -573,4 +908,187 @@ declare function gridToSize(options: {
573
908
  cellSize: number;
574
909
  }): Pick<CanvasTileEngineConfig, "size" | "scale">;
575
910
 
576
- 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 };
911
+ /**
912
+ * Spatial indexing wrapper using RBush (R-Tree) for fast viewport queries
913
+ * @internal
914
+ */
915
+ interface SpatialItem {
916
+ x: number;
917
+ y: number;
918
+ /**
919
+ * Optional world-space size (width/height). Defaults to 0 for point-like items.
920
+ */
921
+ size?: number;
922
+ }
923
+ declare class SpatialIndex<T extends SpatialItem> {
924
+ private tree;
925
+ constructor();
926
+ /**
927
+ * Bulk load items into the R-Tree (much faster than individual inserts)
928
+ */
929
+ load(items: T[]): void;
930
+ /**
931
+ * Query all items within a rectangular range
932
+ */
933
+ query(minX: number, minY: number, maxX: number, maxY: number): T[];
934
+ /**
935
+ * Clear all items
936
+ */
937
+ clear(): void;
938
+ /**
939
+ * Create SpatialIndex from array of items
940
+ */
941
+ static fromArray<T extends SpatialItem>(items: T[]): SpatialIndex<T>;
942
+ }
943
+
944
+ /**
945
+ * Normalized pointer input - renderer-agnostic format.
946
+ * All coordinates should be canvas-relative.
947
+ */
948
+ interface NormalizedPointer {
949
+ /** X position relative to canvas */
950
+ x: number;
951
+ /** Y position relative to canvas */
952
+ y: number;
953
+ /** X position relative to viewport (for callbacks) */
954
+ clientX: number;
955
+ /** Y position relative to viewport (for callbacks) */
956
+ clientY: number;
957
+ }
958
+ /**
959
+ * Normalized multi-pointer input for pinch gestures.
960
+ */
961
+ interface NormalizedPinch {
962
+ /** First pointer */
963
+ pointer1: NormalizedPointer;
964
+ /** Second pointer */
965
+ pointer2: NormalizedPointer;
966
+ }
967
+ /**
968
+ * Processed coordinate result for callbacks.
969
+ */
970
+ interface ProcessedCoords {
971
+ coords: {
972
+ raw: Coords;
973
+ snapped: Coords;
974
+ };
975
+ mouse: {
976
+ raw: Coords;
977
+ snapped: Coords;
978
+ };
979
+ client: {
980
+ raw: Coords;
981
+ snapped: Coords;
982
+ };
983
+ }
984
+ /**
985
+ * Canvas bounds for zoom calculation.
986
+ * Compatible with DOMRect subset needed by Camera.zoom
987
+ */
988
+ interface CanvasBounds {
989
+ left: number;
990
+ top: number;
991
+ width: number;
992
+ height: number;
993
+ x: number;
994
+ y: number;
995
+ bottom: number;
996
+ right: number;
997
+ }
998
+ /**
999
+ * Handles gesture logic (click, hover, drag, zoom) independent of DOM/platform.
1000
+ * Receives normalized input from renderer and performs calculations.
1001
+ */
1002
+ declare class GestureProcessor {
1003
+ private camera;
1004
+ private config;
1005
+ private transformer;
1006
+ private canvasBoundsGetter;
1007
+ private onCameraChange;
1008
+ private isDragging;
1009
+ private shouldPreventClick;
1010
+ private lastPos;
1011
+ private isPinching;
1012
+ private lastPinchDistance;
1013
+ private lastPinchCenter;
1014
+ onClick?: onClickCallback;
1015
+ onRightClick?: onRightClickCallback;
1016
+ onHover?: onHoverCallback;
1017
+ onMouseDown?: onMouseDownCallback;
1018
+ onMouseUp?: onMouseUpCallback;
1019
+ onMouseLeave?: onMouseLeaveCallback;
1020
+ onZoom?: onZoomCallback;
1021
+ constructor(camera: ICamera$1, config: Config, transformer: CoordinateTransformer, canvasBoundsGetter: () => CanvasBounds, onCameraChange: () => void);
1022
+ /**
1023
+ * Process pointer coordinates into world/screen coords for callbacks.
1024
+ */
1025
+ private processCoords;
1026
+ /**
1027
+ * Calculate distance between two pointers.
1028
+ */
1029
+ private getPointerDistance;
1030
+ /**
1031
+ * Calculate center point between two pointers (in client coords).
1032
+ */
1033
+ private getPointerCenter;
1034
+ handleClick: (pointer: NormalizedPointer) => void;
1035
+ handleRightClick: (pointer: NormalizedPointer) => void;
1036
+ handlePointerDown: (pointer: NormalizedPointer) => void;
1037
+ handlePointerMove: (pointer: NormalizedPointer) => void;
1038
+ handlePointerUp: (pointer: NormalizedPointer) => void;
1039
+ handlePointerLeave: (pointer: NormalizedPointer) => void;
1040
+ handleTouchStart: (pointers: NormalizedPointer[]) => void;
1041
+ handleTouchMove: (pointers: NormalizedPointer[]) => void;
1042
+ handleTouchEnd: (remainingPointers: NormalizedPointer[], changedPointer?: NormalizedPointer) => void;
1043
+ handleWheel: (pointer: NormalizedPointer, deltaY: number) => void;
1044
+ get dragging(): boolean;
1045
+ get pinching(): boolean;
1046
+ }
1047
+
1048
+ /**
1049
+ * Manages smooth animations for camera movements and canvas resizing.
1050
+ * Handles animation frame scheduling and cleanup.
1051
+ */
1052
+ declare class AnimationController {
1053
+ private camera;
1054
+ private viewport;
1055
+ private onAnimationFrame;
1056
+ private moveAnimationId?;
1057
+ private resizeAnimationId?;
1058
+ constructor(camera: ICamera$1, viewport: ViewportState, onAnimationFrame: () => void);
1059
+ /**
1060
+ * Smoothly animate camera movement to target coordinates.
1061
+ * @param targetX Target world x coordinate.
1062
+ * @param targetY Target world y coordinate.
1063
+ * @param durationMs Animation duration in milliseconds (default: 500ms). Set to 0 for instant move.
1064
+ * @param onComplete Optional callback fired when animation completes.
1065
+ */
1066
+ animateMoveTo(targetX: number, targetY: number, durationMs?: number, onComplete?: () => void): void;
1067
+ /**
1068
+ * Smoothly animate canvas size change while keeping view centered.
1069
+ * @param targetWidth New canvas width in pixels.
1070
+ * @param targetHeight New canvas height in pixels.
1071
+ * @param durationMs Animation duration in milliseconds (default: 500ms). Set to 0 for instant resize.
1072
+ * @param onApplySize Callback to apply the new size (updates wrapper, canvas, renderer).
1073
+ * @param onComplete Optional callback fired when animation completes.
1074
+ */
1075
+ animateResize(targetWidth: number, targetHeight: number, durationMs: number | undefined, onApplySize: (width: number, height: number, center: Coords) => void, onComplete?: () => void): void;
1076
+ /**
1077
+ * Cancel the current move animation if running.
1078
+ */
1079
+ cancelMove(): void;
1080
+ /**
1081
+ * Cancel the current resize animation if running.
1082
+ */
1083
+ cancelResize(): void;
1084
+ /**
1085
+ * Cancel all running animations.
1086
+ */
1087
+ cancelAll(): void;
1088
+ /**
1089
+ * Check if any animation is currently running.
1090
+ */
1091
+ isAnimating(): boolean;
1092
+ }
1093
+
1094
+ export { AnimationController, type Bounds, COORDINATE_OVERLAY, type CanvasBounds, CanvasTileEngine, type CanvasTileEngineConfig, type Circle, Config, CoordinateTransformer, type Coords, DEBUG_HUD, DEFAULT_VALUES, type DrawHandle, type DrawObject, type EventHandlers, GestureProcessor, type ICamera, type IDrawAPI, type IImageLoader, type IRenderer, type ImageItem, type Line, type LineStyle, type NormalizedPinch, type NormalizedPointer, type Path, type ProcessedCoords, RENDER_DEFAULTS, type Rect, type RendererDependencies, SCALE_LIMITS, SIZE_LIMITS, SpatialIndex, type Text, VISIBILITY_BUFFER, type ViewportBounds, ViewportState, gridToSize, type onClickCallback, type onDrawCallback, type onHoverCallback, type onMouseDownCallback, type onMouseLeaveCallback, type onMouseUpCallback, type onRightClickCallback, type onZoomCallback };