@canvas-tile-engine/core 0.2.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
+ };
34
103
  }
35
104
 
36
- interface LayerHandle {
37
- layer: number;
38
- id: symbol;
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;
128
+ }
129
+
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,74 +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 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
- };
166
- type Line = {
167
- from: Coords;
168
- to: Coords;
169
- };
170
- type Path = Coords[];
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
+ }
171
398
 
172
399
  /**
173
400
  * Core engine wiring camera, config, renderer, events, and draw helpers.
@@ -177,16 +404,15 @@ declare class CanvasTileEngine {
177
404
  private camera;
178
405
  private viewport;
179
406
  private coordinateTransformer;
180
- private layers?;
181
407
  private renderer;
182
- private events;
183
- private draw?;
184
- images: ImageLoader;
185
- private sizeController;
186
408
  private animationController;
187
- private responsiveWatcher?;
188
409
  canvasWrapper: HTMLDivElement;
189
410
  canvas: HTMLCanvasElement;
411
+ /**
412
+ * Image loader for loading and caching images.
413
+ * Uses the renderer's platform-specific implementation.
414
+ */
415
+ get images(): IImageLoader;
190
416
  /**
191
417
  * Callback when center coordinates change (pan or zoom).
192
418
  * @param coords - Center world coordinates: `{ x, y }`
@@ -329,11 +555,12 @@ declare class CanvasTileEngine {
329
555
  get onZoom(): ((scale: number) => void) | undefined;
330
556
  set onZoom(cb: ((scale: number) => void) | undefined);
331
557
  /**
332
- * @param canvas Target canvas element.
558
+ * @param canvasWrapper Canvas wrapper element containing a canvas child.
333
559
  * @param config Initial engine configuration.
560
+ * @param renderer The renderer implementation to use (e.g., RendererCanvas).
334
561
  * @param center Initial center in world space.
335
562
  */
336
- constructor(canvasWrapper: HTMLDivElement, config: CanvasTileEngineConfig, center?: Coords);
563
+ constructor(canvasWrapper: HTMLDivElement, config: CanvasTileEngineConfig, renderer: IRenderer, center?: Coords);
337
564
  /** Tear down listeners and observers. */
338
565
  destroy(): void;
339
566
  /** Render a frame using the active renderer. */
@@ -452,20 +679,14 @@ declare class CanvasTileEngine {
452
679
  maxY: number;
453
680
  }): void;
454
681
  /**
455
- * Register a generic draw callback (canvas renderer only).
456
- * @param fn Callback invoked with context, top-left coords, and config.
457
- * @param layer Layer order (lower draws first).
458
- */
459
- addDrawFunction(fn: (ctx: CanvasRenderingContext2D, coords: Coords, config: Required<CanvasTileEngineConfig>) => void, layer?: number): LayerHandle;
460
- /**
461
- * Draw one or many rectangles in world space (canvas renderer only).
682
+ * Draw one or many rectangles in world space.
462
683
  * Supports rotation via the `rotate` property (degrees, positive = clockwise).
463
684
  * @param items Rectangle definitions.
464
685
  * @param layer Layer order (lower draws first).
465
686
  */
466
- drawRect(items: Rect | Array<Rect>, layer?: number): LayerHandle;
687
+ drawRect(items: Rect | Array<Rect>, layer?: number): DrawHandle;
467
688
  /**
468
- * Draw rectangles with pre-rendering cache (canvas renderer only).
689
+ * Draw rectangles with pre-rendering cache.
469
690
  * Renders all items once to an offscreen canvas, then blits the visible portion each frame.
470
691
  * Ideal for large static datasets like mini-maps where items don't change.
471
692
  * Supports rotation via the `rotate` property (degrees, positive = clockwise).
@@ -473,18 +694,18 @@ declare class CanvasTileEngine {
473
694
  * @param cacheKey Unique key for this cache (e.g., "minimap-items").
474
695
  * @param layer Layer order (lower draws first).
475
696
  */
476
- drawStaticRect(items: Array<Rect>, cacheKey: string, layer?: number): LayerHandle;
697
+ drawStaticRect(items: Array<Rect>, cacheKey: string, layer?: number): DrawHandle;
477
698
  /**
478
- * Draw circles with pre-rendering cache (canvas renderer only).
699
+ * Draw circles with pre-rendering cache.
479
700
  * Renders all items once to an offscreen canvas, then blits the visible portion each frame.
480
701
  * Ideal for large static datasets like mini-maps where items don't change.
481
702
  * @param items Array of circle definitions.
482
703
  * @param cacheKey Unique key for this cache (e.g., "minimap-circles").
483
704
  * @param layer Layer order (lower draws first).
484
705
  */
485
- drawStaticCircle(items: Array<Circle>, cacheKey: string, layer?: number): LayerHandle;
706
+ drawStaticCircle(items: Array<Circle>, cacheKey: string, layer?: number): DrawHandle;
486
707
  /**
487
- * Draw images with pre-rendering cache (canvas renderer only).
708
+ * Draw images with pre-rendering cache.
488
709
  * Renders all items once to an offscreen canvas, then blits the visible portion each frame.
489
710
  * Ideal for large static datasets like terrain tiles or static decorations.
490
711
  * Supports rotation via the `rotate` property (degrees, positive = clockwise).
@@ -492,14 +713,14 @@ declare class CanvasTileEngine {
492
713
  * @param cacheKey Unique key for this cache (e.g., "terrain-cache").
493
714
  * @param layer Layer order (lower draws first).
494
715
  */
495
- drawStaticImage(items: Array<ImageItem>, cacheKey: string, layer?: number): LayerHandle;
716
+ drawStaticImage(items: Array<ImageItem>, cacheKey: string, layer?: number): DrawHandle;
496
717
  /**
497
718
  * Clear a static rendering cache.
498
719
  * @param cacheKey The cache key to clear, or undefined to clear all caches.
499
720
  */
500
721
  clearStaticCache(cacheKey?: string): void;
501
722
  /**
502
- * Draw one or many lines between world points (canvas renderer only).
723
+ * Draw one or many lines between world points.
503
724
  * @param items Line segments.
504
725
  * @param style Line style overrides.
505
726
  * @param layer Layer order.
@@ -507,15 +728,15 @@ declare class CanvasTileEngine {
507
728
  drawLine(items: Array<Line> | Line, style?: {
508
729
  strokeStyle?: string;
509
730
  lineWidth?: number;
510
- }, layer?: number): LayerHandle;
731
+ }, layer?: number): DrawHandle;
511
732
  /**
512
- * Draw one or many circles sized in world units (canvas renderer only).
733
+ * Draw one or many circles sized in world units.
513
734
  * @param items Circle definitions.
514
735
  * @param layer Layer order.
515
736
  */
516
- drawCircle(items: Circle | Array<Circle>, layer?: number): LayerHandle;
737
+ drawCircle(items: Circle | Array<Circle>, layer?: number): DrawHandle;
517
738
  /**
518
- * Draw one or many texts at world positions (canvas renderer only).
739
+ * Draw one or many texts at world positions.
519
740
  * @param items Text definitions with position, text, size, and style.
520
741
  * @param layer Layer order.
521
742
  * @example
@@ -535,9 +756,9 @@ declare class CanvasTileEngine {
535
756
  * ]);
536
757
  * ```
537
758
  */
538
- drawText(items: Array<Text> | Text, layer?: number): LayerHandle;
759
+ drawText(items: Array<Text> | Text, layer?: number): DrawHandle;
539
760
  /**
540
- * Draw one or many polylines through world points (canvas renderer only).
761
+ * Draw one or many polylines through world points.
541
762
  * @param items Polyline point collections.
542
763
  * @param style Stroke style overrides.
543
764
  * @param layer Layer order.
@@ -545,30 +766,38 @@ declare class CanvasTileEngine {
545
766
  drawPath(items: Array<Path> | Path, style?: {
546
767
  strokeStyle?: string;
547
768
  lineWidth?: number;
548
- }, layer?: number): LayerHandle;
769
+ }, layer?: number): DrawHandle;
549
770
  /**
550
- * Draw one or many images scaled in world units (canvas renderer only).
771
+ * Draw one or many images scaled in world units.
551
772
  * Supports rotation via the `rotate` property (degrees, positive = clockwise).
552
773
  * @param items Image definitions.
553
774
  * @param layer Layer order.
554
775
  */
555
- drawImage(items: Array<ImageItem> | ImageItem, layer?: number): LayerHandle;
776
+ drawImage(items: Array<ImageItem> | ImageItem, layer?: number): DrawHandle;
556
777
  /**
557
- * Draw grid lines at specified cell size (canvas renderer only).
778
+ * Draw grid lines at specified cell size.
558
779
  * @param cellSize Size of each grid cell in world units.
559
780
  * @example
560
781
  * ```ts
561
782
  * engine.drawGridLines(50);
562
783
  * ```
563
784
  */
564
- 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;
565
794
  /**
566
- * Remove a specific draw callback by handle (canvas renderer only).
795
+ * Remove a specific draw callback by handle.
567
796
  * Does not clear other callbacks on the same layer.
568
797
  */
569
- removeLayerHandle(handle: LayerHandle): void;
798
+ removeDrawHandle(handle: DrawHandle): void;
570
799
  /**
571
- * Clear all draw callbacks from a specific layer (canvas renderer only).
800
+ * Clear all draw callbacks from a specific layer.
572
801
  * Use this before redrawing dynamic content to prevent accumulation.
573
802
  * @param layer Layer index to clear.
574
803
  * @example
@@ -580,7 +809,7 @@ declare class CanvasTileEngine {
580
809
  */
581
810
  clearLayer(layer: number): void;
582
811
  /**
583
- * Clear all draw callbacks from all layers (canvas renderer only).
812
+ * Clear all draw callbacks from all layers.
584
813
  * Useful for complete scene reset.
585
814
  * @example
586
815
  * ```ts
@@ -589,13 +818,6 @@ declare class CanvasTileEngine {
589
818
  * ```
590
819
  */
591
820
  clearAll(): void;
592
- /**
593
- * Build the active renderer based on config.
594
- * @param type Renderer type requested.
595
- */
596
- private createRenderer;
597
- private ensureCanvasDraw;
598
- private getCanvasRenderer;
599
821
  private handleCameraChange;
600
822
  }
601
823
 
@@ -686,4 +908,187 @@ declare function gridToSize(options: {
686
908
  cellSize: number;
687
909
  }): Pick<CanvasTileEngineConfig, "size" | "scale">;
688
910
 
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 };
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 };