@canvas-tile-engine/core 0.0.1 → 0.0.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ENES YÜKSEL
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
+ DEALINGS IN THE SOFTWARE.
package/dist/index.d.mts CHANGED
@@ -44,6 +44,8 @@ type CanvasTileEngineConfig = {
44
44
  maxScale?: number;
45
45
  minScale?: number;
46
46
  backgroundColor?: string;
47
+ /** When true, center coordinates are snapped to cell centers (x.5, y.5) for pixel-perfect grid alignment */
48
+ gridAligned?: boolean;
47
49
  size: {
48
50
  width: number;
49
51
  height: number;
@@ -91,6 +93,7 @@ type CanvasTileEngineConfig = {
91
93
  };
92
94
  type EventHandlers = {
93
95
  click?: boolean;
96
+ rightClick?: boolean;
94
97
  hover?: boolean;
95
98
  drag?: boolean;
96
99
  zoom?: boolean;
@@ -106,7 +109,7 @@ type onDrawCallback = (ctx: CanvasRenderingContext2D, info: {
106
109
  height: number;
107
110
  coords: Coords;
108
111
  }) => void;
109
- type onClickCallback = (coords: {
112
+ type MouseEventCallback = (coords: {
110
113
  raw: Coords;
111
114
  snapped: Coords;
112
115
  }, mouse: {
@@ -116,7 +119,13 @@ type onClickCallback = (coords: {
116
119
  raw: Coords;
117
120
  snapped: Coords;
118
121
  }) => void;
119
- type onHoverCallback = onClickCallback;
122
+ type onClickCallback = MouseEventCallback;
123
+ type onHoverCallback = MouseEventCallback;
124
+ type onMouseDownCallback = MouseEventCallback;
125
+ type onMouseUpCallback = MouseEventCallback;
126
+ type onMouseLeaveCallback = MouseEventCallback;
127
+ type onRightClickCallback = MouseEventCallback;
128
+ type onZoomCallback = (scale: number) => void;
120
129
  type DrawObject = {
121
130
  x: number;
122
131
  y: number;
@@ -177,24 +186,31 @@ declare class CanvasTileEngine {
177
186
  private _onClick?;
178
187
  get onClick(): onClickCallback | undefined;
179
188
  set onClick(cb: onClickCallback | undefined);
189
+ private _onRightClick?;
190
+ get onRightClick(): onRightClickCallback | undefined;
191
+ set onRightClick(cb: onRightClickCallback | undefined);
180
192
  private _onHover?;
181
193
  get onHover(): onHoverCallback | undefined;
182
194
  set onHover(cb: onHoverCallback | undefined);
183
195
  private _onMouseDown?;
184
- get onMouseDown(): (() => void) | undefined;
185
- set onMouseDown(cb: (() => void) | undefined);
196
+ get onMouseDown(): onMouseDownCallback | undefined;
197
+ set onMouseDown(cb: onMouseDownCallback | undefined);
186
198
  private _onMouseUp?;
187
- get onMouseUp(): (() => void) | undefined;
188
- set onMouseUp(cb: (() => void) | undefined);
199
+ get onMouseUp(): onMouseUpCallback | undefined;
200
+ set onMouseUp(cb: onMouseUpCallback | undefined);
189
201
  private _onMouseLeave?;
190
- get onMouseLeave(): (() => void) | undefined;
191
- set onMouseLeave(cb: (() => void) | undefined);
202
+ get onMouseLeave(): onMouseLeaveCallback | undefined;
203
+ set onMouseLeave(cb: onMouseLeaveCallback | undefined);
192
204
  private _onDraw?;
193
205
  get onDraw(): onDrawCallback | undefined;
194
206
  set onDraw(cb: onDrawCallback | undefined);
195
207
  private _onResize?;
196
208
  get onResize(): (() => void) | undefined;
197
209
  set onResize(cb: (() => void) | undefined);
210
+ private _onZoom?;
211
+ /** Callback: zoom level changes (wheel or pinch) */
212
+ get onZoom(): ((scale: number) => void) | undefined;
213
+ set onZoom(cb: ((scale: number) => void) | undefined);
198
214
  /**
199
215
  * @param canvas Target canvas element.
200
216
  * @param config Initial engine configuration.
@@ -240,6 +256,25 @@ declare class CanvasTileEngine {
240
256
  getConfig(): Required<CanvasTileEngineConfig>;
241
257
  /** Center coordinates of the map. */
242
258
  getCenterCoords(): Coords;
259
+ /**
260
+ * Get the visible world coordinate bounds of the viewport.
261
+ * Returns floored/ceiled values representing which cells are visible.
262
+ * @returns Visible bounds with min/max coordinates.
263
+ * @example
264
+ * ```ts
265
+ * const bounds = engine.getVisibleBounds();
266
+ * // { minX: 0, maxX: 10, minY: 0, maxY: 10 }
267
+ *
268
+ * // Use for random placement within visible area
269
+ * const x = bounds.minX + Math.floor(Math.random() * (bounds.maxX - bounds.minX));
270
+ * ```
271
+ */
272
+ getVisibleBounds(): {
273
+ minX: number;
274
+ maxX: number;
275
+ minY: number;
276
+ maxY: number;
277
+ };
243
278
  /** Set center coordinates from outside (adjusts the camera accordingly). */
244
279
  updateCoords(newCenter: Coords): void;
245
280
  /**
@@ -511,4 +546,4 @@ declare const VISIBILITY_BUFFER: {
511
546
  readonly TILE_BUFFER: 1;
512
547
  };
513
548
 
514
- 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, type onClickCallback, type onDrawCallback, type onHoverCallback };
549
+ 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, type onClickCallback, type onDrawCallback, type onHoverCallback, type onMouseDownCallback, type onMouseLeaveCallback, type onMouseUpCallback, type onRightClickCallback, type onZoomCallback };
package/dist/index.d.ts CHANGED
@@ -44,6 +44,8 @@ type CanvasTileEngineConfig = {
44
44
  maxScale?: number;
45
45
  minScale?: number;
46
46
  backgroundColor?: string;
47
+ /** When true, center coordinates are snapped to cell centers (x.5, y.5) for pixel-perfect grid alignment */
48
+ gridAligned?: boolean;
47
49
  size: {
48
50
  width: number;
49
51
  height: number;
@@ -91,6 +93,7 @@ type CanvasTileEngineConfig = {
91
93
  };
92
94
  type EventHandlers = {
93
95
  click?: boolean;
96
+ rightClick?: boolean;
94
97
  hover?: boolean;
95
98
  drag?: boolean;
96
99
  zoom?: boolean;
@@ -106,7 +109,7 @@ type onDrawCallback = (ctx: CanvasRenderingContext2D, info: {
106
109
  height: number;
107
110
  coords: Coords;
108
111
  }) => void;
109
- type onClickCallback = (coords: {
112
+ type MouseEventCallback = (coords: {
110
113
  raw: Coords;
111
114
  snapped: Coords;
112
115
  }, mouse: {
@@ -116,7 +119,13 @@ type onClickCallback = (coords: {
116
119
  raw: Coords;
117
120
  snapped: Coords;
118
121
  }) => void;
119
- type onHoverCallback = onClickCallback;
122
+ type onClickCallback = MouseEventCallback;
123
+ type onHoverCallback = MouseEventCallback;
124
+ type onMouseDownCallback = MouseEventCallback;
125
+ type onMouseUpCallback = MouseEventCallback;
126
+ type onMouseLeaveCallback = MouseEventCallback;
127
+ type onRightClickCallback = MouseEventCallback;
128
+ type onZoomCallback = (scale: number) => void;
120
129
  type DrawObject = {
121
130
  x: number;
122
131
  y: number;
@@ -177,24 +186,31 @@ declare class CanvasTileEngine {
177
186
  private _onClick?;
178
187
  get onClick(): onClickCallback | undefined;
179
188
  set onClick(cb: onClickCallback | undefined);
189
+ private _onRightClick?;
190
+ get onRightClick(): onRightClickCallback | undefined;
191
+ set onRightClick(cb: onRightClickCallback | undefined);
180
192
  private _onHover?;
181
193
  get onHover(): onHoverCallback | undefined;
182
194
  set onHover(cb: onHoverCallback | undefined);
183
195
  private _onMouseDown?;
184
- get onMouseDown(): (() => void) | undefined;
185
- set onMouseDown(cb: (() => void) | undefined);
196
+ get onMouseDown(): onMouseDownCallback | undefined;
197
+ set onMouseDown(cb: onMouseDownCallback | undefined);
186
198
  private _onMouseUp?;
187
- get onMouseUp(): (() => void) | undefined;
188
- set onMouseUp(cb: (() => void) | undefined);
199
+ get onMouseUp(): onMouseUpCallback | undefined;
200
+ set onMouseUp(cb: onMouseUpCallback | undefined);
189
201
  private _onMouseLeave?;
190
- get onMouseLeave(): (() => void) | undefined;
191
- set onMouseLeave(cb: (() => void) | undefined);
202
+ get onMouseLeave(): onMouseLeaveCallback | undefined;
203
+ set onMouseLeave(cb: onMouseLeaveCallback | undefined);
192
204
  private _onDraw?;
193
205
  get onDraw(): onDrawCallback | undefined;
194
206
  set onDraw(cb: onDrawCallback | undefined);
195
207
  private _onResize?;
196
208
  get onResize(): (() => void) | undefined;
197
209
  set onResize(cb: (() => void) | undefined);
210
+ private _onZoom?;
211
+ /** Callback: zoom level changes (wheel or pinch) */
212
+ get onZoom(): ((scale: number) => void) | undefined;
213
+ set onZoom(cb: ((scale: number) => void) | undefined);
198
214
  /**
199
215
  * @param canvas Target canvas element.
200
216
  * @param config Initial engine configuration.
@@ -240,6 +256,25 @@ declare class CanvasTileEngine {
240
256
  getConfig(): Required<CanvasTileEngineConfig>;
241
257
  /** Center coordinates of the map. */
242
258
  getCenterCoords(): Coords;
259
+ /**
260
+ * Get the visible world coordinate bounds of the viewport.
261
+ * Returns floored/ceiled values representing which cells are visible.
262
+ * @returns Visible bounds with min/max coordinates.
263
+ * @example
264
+ * ```ts
265
+ * const bounds = engine.getVisibleBounds();
266
+ * // { minX: 0, maxX: 10, minY: 0, maxY: 10 }
267
+ *
268
+ * // Use for random placement within visible area
269
+ * const x = bounds.minX + Math.floor(Math.random() * (bounds.maxX - bounds.minX));
270
+ * ```
271
+ */
272
+ getVisibleBounds(): {
273
+ minX: number;
274
+ maxX: number;
275
+ minY: number;
276
+ maxY: number;
277
+ };
243
278
  /** Set center coordinates from outside (adjusts the camera accordingly). */
244
279
  updateCoords(newCenter: Coords): void;
245
280
  /**
@@ -511,4 +546,4 @@ declare const VISIBILITY_BUFFER: {
511
546
  readonly TILE_BUFFER: 1;
512
547
  };
513
548
 
514
- 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, type onClickCallback, type onDrawCallback, type onHoverCallback };
549
+ 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, 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 he=Object.create;var A=Object.defineProperty;var le=Object.getOwnPropertyDescriptor;var ce=Object.getOwnPropertyNames;var de=Object.getPrototypeOf,me=Object.prototype.hasOwnProperty;var ue=(p,e)=>{for(var t in e)A(p,t,{get:e[t],enumerable:!0})},te=(p,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ce(e))!me.call(p,i)&&i!==t&&A(p,i,{get:()=>e[i],enumerable:!(r=le(e,i))||r.enumerable});return p};var fe=(p,e,t)=>(t=p!=null?he(de(p)):{},te(e||!p||!p.__esModule?A(t,"default",{value:p,enumerable:!0}):t,p)),ve=p=>te(A({},"__esModule",{value:!0}),p);var ge={};ue(ge,{COORDINATE_OVERLAY:()=>x,CanvasTileEngine:()=>J,DEBUG_HUD:()=>T,DEFAULT_VALUES:()=>w,RENDER_DEFAULTS:()=>k,SCALE_LIMITS:()=>O,SIZE_LIMITS:()=>R,VISIBILITY_BUFFER:()=>S});module.exports=ve(ge);var w={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},k={BACKGROUND_COLOR:"#ffffff",RENDERER_TYPE:"canvas"},x={BORDER_WIDTH:20,TEXT_OPACITY:.8,BORDER_OPACITY:.1,MIN_FONT_SIZE:8,MAX_FONT_SIZE:12,FONT_SIZE_SCALE_FACTOR:.25},T={PANEL_WIDTH:160,PADDING:8,LINE_HEIGHT:16},S={TILE_BUFFER:1};function re(p,e,t,r){return{x:p.x-t/e,y:p.y-r/e}}function ie(p,e,t,r,i,n){let s=Math.min(Math.max(t,w.MIN_WHEEL_DELTA),w.MAX_WHEEL_DELTA),a=Math.exp(-s*w.ZOOM_SENSITIVITY),o=Math.min(i,Math.max(r,e*a));return o===e?{topLeft:p,scale:e}:{topLeft:{x:p.x+n.x*(1/e-1/o),y:p.y+n.y*(1/e-1/o)},scale:o}}function se(p,e){return{x:(p.x+w.CELL_CENTER_OFFSET-e.x)*e.scale,y:(p.y+w.CELL_CENTER_OFFSET-e.y)*e.scale}}function ne(p,e){return{x:e.x+p.x/e.scale,y:e.y+p.y/e.scale}}var D=class{_x;_y;_scale;minScale;maxScale;bounds;viewport;constructor(e,t=1,r=.1,i=10,n){this._x=e.x+w.CELL_CENTER_OFFSET,this._y=e.y+w.CELL_CENTER_OFFSET,this._scale=t,this.minScale=r,this.maxScale=i,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(),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 n=i-r;return t>=n?r-(t-n)/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=re({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 n=e-i.left,s=t-i.top,a=ie({x:this._x,y:this._y},this._scale,r,this.minScale,this.maxScale,{x:n,y:s});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()}};var F=class{config;constructor(e){let t={renderer:k.RENDERER_TYPE,scale:e.scale,minScale:e.minScale??e.scale*O.MIN_SCALE_MULTIPLIER,maxScale:e.maxScale??e.scale*O.MAX_SCALE_MULTIPLIER,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??k.BACKGROUND_COLOR,eventHandlers:{click:e.eventHandlers?.click??!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 W=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 ne({x:e,y:t},{x:this.camera.x,y:this.camera.y,scale:this.camera.scale})}};var ae=fe(require("rbush")),_=class p{tree;constructor(){this.tree=new ae.default}load(e){let t=e.map(r=>{let n=(typeof r.size=="number"?r.size:0)/2;return{minX:r.x-n,minY:r.y-n,maxX:r.x+n,maxY:r.y+n,item:r}});this.tree.load(t)}query(e,t,r,i){return this.tree.search({minX:e,minY:t,maxX:r,maxY:i}).map(s=>s.item)}clear(){this.tree.clear()}static fromArray(e){let t=new p;return t.load(e),t}};var ee=500,oe=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,n){let s=n.size.width/n.scale,a=n.size.height/n.scale,o=i.x-S.TILE_BUFFER,l=i.y-S.TILE_BUFFER,c=i.x+s+S.TILE_BUFFER,h=i.y+a+S.TILE_BUFFER;return e+r>=o&&e-r<=c&&t+r>=l&&t-r<=h}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:n})=>{e(r,n,i)})}drawRect(e,t=1){let r=Array.isArray(e)?e:[e],n=r.length>ee?_.fromArray(r):null;return this.layers.add(t,({ctx:s,config:a,topLeft:o})=>{let l=this.getViewportBounds(o,a),c=n?n.query(l.minX,l.minY,l.maxX,l.maxY):r;s.save();let h,v,u;for(let d of c){let m=d.size??1,C={mode:d.origin?.mode==="self"?"self":"cell",x:d.origin?.x??.5,y:d.origin?.y??.5},f=d.style;if(!n&&!this.isVisible(d.x,d.y,m/2,o,a))continue;let y=this.transformer.worldToScreen(d.x,d.y),g=m*this.camera.scale,{x:b,y:E}=this.computeOriginOffset(y,g,C,this.camera);f?.fillStyle&&f.fillStyle!==h&&(s.fillStyle=f.fillStyle,h=f.fillStyle),f?.strokeStyle&&f.strokeStyle!==v&&(s.strokeStyle=f.strokeStyle,v=f.strokeStyle),f?.lineWidth&&f.lineWidth!==u&&(s.lineWidth=f.lineWidth,u=f.lineWidth);let I=d.rotate??0,z=I*(Math.PI/180),L=d.radius;if(I!==0){let K=b+g/2,Q=E+g/2;s.save(),s.translate(K,Q),s.rotate(z),s.beginPath(),L&&s.roundRect?s.roundRect(-g/2,-g/2,g,g,L):s.rect(-g/2,-g/2,g,g),f?.fillStyle&&s.fill(),f?.strokeStyle&&s.stroke(),s.restore()}else s.beginPath(),L&&s.roundRect?s.roundRect(b,E,g,g,L):s.rect(b,E,g,g),f?.fillStyle&&s.fill(),f?.strokeStyle&&s.stroke()}s.restore()})}drawLine(e,t,r=1){let i=Array.isArray(e)?e:[e];return this.layers.add(r,({ctx:n,config:s,topLeft:a})=>{n.save(),t?.strokeStyle&&(n.strokeStyle=t.strokeStyle),t?.lineWidth&&(n.lineWidth=t.lineWidth),n.beginPath();for(let o of i){let l=(o.from.x+o.to.x)/2,c=(o.from.y+o.to.y)/2,h=Math.max(Math.abs(o.from.x-o.to.x),Math.abs(o.from.y-o.to.y))/2;if(!this.isVisible(l,c,h,a,s))continue;let v=this.transformer.worldToScreen(o.from.x,o.from.y),u=this.transformer.worldToScreen(o.to.x,o.to.y);n.moveTo(v.x,v.y),n.lineTo(u.x,u.y)}n.stroke(),n.restore()})}drawCircle(e,t=1){let r=Array.isArray(e)?e:[e],n=r.length>ee?_.fromArray(r):null;return this.layers.add(t,({ctx:s,config:a,topLeft:o})=>{let l=this.getViewportBounds(o,a),c=n?n.query(l.minX,l.minY,l.maxX,l.maxY):r;s.save();let h,v,u;for(let d of c){let m=d.size??1,C={mode:d.origin?.mode==="self"?"self":"cell",x:d.origin?.x??.5,y:d.origin?.y??.5},f=d.style;if(!n&&!this.isVisible(d.x,d.y,m/2,o,a))continue;let y=this.transformer.worldToScreen(d.x,d.y),g=m*this.camera.scale,b=g/2,{x:E,y:I}=this.computeOriginOffset(y,g,C,this.camera);f?.fillStyle&&f.fillStyle!==h&&(s.fillStyle=f.fillStyle,h=f.fillStyle),f?.strokeStyle&&f.strokeStyle!==v&&(s.strokeStyle=f.strokeStyle,v=f.strokeStyle),f?.lineWidth&&f.lineWidth!==u&&(s.lineWidth=f.lineWidth,u=f.lineWidth),s.beginPath(),s.arc(E+b,I+b,b,0,Math.PI*2),f?.fillStyle&&s.fill(),f?.strokeStyle&&s.stroke()}s.restore()})}drawText(e,t,r=2){let i=Array.isArray(e)?e:[e];return this.layers.add(r,({ctx:n,config:s,topLeft:a})=>{n.save(),t?.font&&(n.font=t.font),t?.fillStyle&&(n.fillStyle=t.fillStyle),n.textAlign=t?.textAlign??"center",n.textBaseline=t?.textBaseline??"middle";for(let o of i){if(!this.isVisible(o.coords.x,o.coords.y,0,a,s))continue;let l=this.transformer.worldToScreen(o.coords.x,o.coords.y);n.fillText(o.text,l.x,l.y)}n.restore()})}drawPath(e,t,r=1){let i=Array.isArray(e[0])?e:[e];return this.layers.add(r,({ctx:n,config:s,topLeft:a})=>{n.save(),t?.strokeStyle&&(n.strokeStyle=t.strokeStyle),t?.lineWidth&&(n.lineWidth=t.lineWidth),n.beginPath();for(let o of i){if(!o.length)continue;let l=o.map(g=>g.x),c=o.map(g=>g.y),h=Math.min(...l),v=Math.max(...l),u=Math.min(...c),d=Math.max(...c),m=(h+v)/2,C=(u+d)/2,f=Math.max(v-h,d-u)/2;if(!this.isVisible(m,C,f,a,s))continue;let y=this.transformer.worldToScreen(o[0].x,o[0].y);n.moveTo(y.x,y.y);for(let g=1;g<o.length;g++){let b=this.transformer.worldToScreen(o[g].x,o[g].y);n.lineTo(b.x,b.y)}}n.stroke(),n.restore()})}drawImage(e,t=1){let r=Array.isArray(e)?e:[e],n=r.length>ee?_.fromArray(r):null;return this.layers.add(t,({ctx:s,config:a,topLeft:o})=>{let l=this.getViewportBounds(o,a),c=n?n.query(l.minX,l.minY,l.maxX,l.maxY):r;for(let h of c){let v=h.size??1,u={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,v/2,o,a))continue;let d=this.transformer.worldToScreen(h.x,h.y),m=v*this.camera.scale,C=h.img.width/h.img.height,f=m,y=m;C>1?y=m/C:f=m*C;let{x:g,y:b}=this.computeOriginOffset(d,m,u,this.camera),E=g+(m-f)/2,I=b+(m-y)/2,z=h.rotate??0,L=z*(Math.PI/180);if(z!==0){let K=E+f/2,Q=I+y/2;s.save(),s.translate(K,Q),s.rotate(L),s.drawImage(h.img,-f/2,-y/2,f,y),s.restore()}else s.drawImage(h.img,E,I,f,y)}})}drawGridLines(e,t,r=0){return this.layers.add(r,({ctx:i,config:n,topLeft:s})=>{let a=n.size.width/n.scale,o=n.size.height/n.scale,l=Math.floor(s.x/e)*e-w.CELL_CENTER_OFFSET,c=Math.ceil((s.x+a)/e)*e-w.CELL_CENTER_OFFSET,h=Math.floor(s.y/e)*e-w.CELL_CENTER_OFFSET,v=Math.ceil((s.y+o)/e)*e-w.CELL_CENTER_OFFSET;i.save(),i.strokeStyle=t.strokeStyle,i.lineWidth=t.lineWidth,i.beginPath();for(let u=l;u<=c;u+=e){let d=this.transformer.worldToScreen(u,h),m=this.transformer.worldToScreen(u,v);i.moveTo(d.x,d.y),i.lineTo(m.x,m.y)}for(let u=h;u<=v;u+=e){let d=this.transformer.worldToScreen(l,u),m=this.transformer.worldToScreen(c,u);i.moveTo(d.x,d.y),i.lineTo(m.x,m.y)}i.stroke(),i.restore()})}computeOriginOffset(e,t,r,i){if(r.mode==="cell"){let n=i.scale;return{x:e.x-n/2+r.x*n-t/2,y:e.y-n/2+r.y*n-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,n=-1/0,s=1/0,a=-1/0;for(let m of e){let C=m.size??1;m.x-C/2<i&&(i=m.x-C/2),m.x+C/2>n&&(n=m.x+C/2),m.y-C/2<s&&(s=m.y-C/2),m.y+C/2>a&&(a=m.y+C/2)}i-=1,s-=1,n+=1,a+=1;let o=n-i,l=a-s,c=this.camera.scale,h=Math.ceil(o*c),v=Math.ceil(l*c);if(h>oe||v>oe)return this.warnedStaticCacheDisabled||(console.warn(`Static cache disabled: offscreen canvas too large (${h}x${v}).`),this.warnedStaticCacheDisabled=!0),null;let u=this.staticCaches.get(t);if(!u||u.scale!==c||u.worldBounds.minX!==i||u.worldBounds.maxX!==n||u.worldBounds.minY!==s||u.worldBounds.maxY!==a){let m=typeof OffscreenCanvas<"u"?new OffscreenCanvas(h,v):document.createElement("canvas");typeof OffscreenCanvas<"u"&&m instanceof OffscreenCanvas||(m.width=h,m.height=v);let f=m.getContext("2d");if(!f)return this.warnedStaticCacheDisabled||(console.warn("[CanvasDraw] Static cache disabled: 2D context unavailable."),this.warnedStaticCacheDisabled=!0),null;for(let y of e){let b=(y.size??1)*c,E=(y.x+w.CELL_CENTER_OFFSET-i)*c-b/2,I=(y.y+w.CELL_CENTER_OFFSET-s)*c-b/2;r(f,y,E,I,b)}u={canvas:m,ctx:f,worldBounds:{minX:i,minY:s,maxX:n,maxY:a},scale:c},this.staticCaches.set(t,u)}return u||null}addStaticCacheLayer(e,t){if(!e)return null;let r=e.canvas,i=e.worldBounds,n=e.scale;return this.layers.add(t,({ctx:s,config:a,topLeft:o})=>{let l=a.size.width/a.scale,c=a.size.height/a.scale,h=(o.x-i.minX)*n,v=(o.y-i.minY)*n,u=l*n,d=c*n;s.drawImage(r,h,v,u,d,0,0,a.size.width,a.size.height)})}drawStaticRect(e,t,r=1){let i,n=this.getOrCreateStaticCache(e,t,(s,a,o,l,c)=>{let h=a.style,v=a.rotate??0,u=v*(Math.PI/180),d=a.radius;if(h?.fillStyle&&h.fillStyle!==i&&(s.fillStyle=h.fillStyle,i=h.fillStyle),v!==0){let m=o+c/2,C=l+c/2;s.save(),s.translate(m,C),s.rotate(u),d&&s.roundRect?(s.beginPath(),s.roundRect(-c/2,-c/2,c,c,d),s.fill()):s.fillRect(-c/2,-c/2,c,c),s.restore()}else d&&s.roundRect?(s.beginPath(),s.roundRect(o,l,c,c,d),s.fill()):s.fillRect(o,l,c,c)});return n?this.addStaticCacheLayer(n,r):this.drawRect(e,r)}drawStaticImage(e,t,r=1){let i=this.getOrCreateStaticCache(e,t,(n,s,a,o,l)=>{let c=s.img,h=s.rotate??0,v=h*(Math.PI/180),u=c.width/c.height,d=l,m=l;u>1?m=l/u:d=l*u;let C=a+(l-d)/2,f=o+(l-m)/2;if(h!==0){let y=C+d/2,g=f+m/2;n.save(),n.translate(y,g),n.rotate(v),n.drawImage(c,-d/2,-m/2,d,m),n.restore()}else n.drawImage(c,C,f,d,m)});return i?this.addStaticCacheLayer(i,r):this.drawImage(e,r)}drawStaticCircle(e,t,r=1){let i,n=this.getOrCreateStaticCache(e,t,(s,a,o,l,c)=>{let h=a.style,v=c/2;h?.fillStyle&&h.fillStyle!==i&&(s.fillStyle=h.fillStyle,i=h.fillStyle),s.beginPath(),s.arc(o+v,l+v,v,0,Math.PI*2),s.fill()});return n?this.addStaticCacheLayer(n,r):this.drawCircle(e,r)}clearStaticCache(e){e?this.staticCaches.delete(e):this.staticCaches.clear()}destroy(){this.staticCaches.clear(),this.layers.clear()}};var Y=class{constructor(e,t){this.canvas=e;this.handlers=t}attach(){this.handlers.click&&this.canvas.addEventListener("click",this.handlers.click),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.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 X=class{constructor(e,t,r,i,n,s){this.canvas=e;this.camera=t;this.viewport=r;this.config=i;this.transformer=n;this.onCameraChange=s}isDragging=!1;shouldPreventClick=!1;lastPos={x:0,y:0};isPinching=!1;lastPinchDistance=0;lastPinchCenter={x:0,y:0};onClick;onHover;onMouseDown;onMouseUp;onMouseLeave;handleClick=e=>{if(this.shouldPreventClick){this.shouldPreventClick=!1;return}if(!this.config.get().eventHandlers.click||!this.onClick)return;let t=this.canvas.getBoundingClientRect(),r=e.clientX-t.left,i=e.clientY-t.top,n=this.transformer.screenToWorld(r,i),s=this.transformer.worldToScreen(Math.floor(n.x),Math.floor(n.y));this.onClick({raw:n,snapped:{x:Math.floor(n.x),y:Math.floor(n.y)}},{raw:{x:e.clientX-t.left,y:e.clientY-t.top},snapped:{x:s.x,y:s.y}},{raw:{x:e.clientX,y:e.clientY},snapped:{x:s.x+t.left,y:s.y+t.top}})};handleMouseDown=e=>{this.onMouseDown&&this.onMouseDown(),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 i=this.canvas.getBoundingClientRect(),n=e.clientX-i.left,s=e.clientY-i.top,a=this.transformer.screenToWorld(n,s),o=this.transformer.worldToScreen(Math.floor(a.x),Math.floor(a.y));this.onHover({raw:a,snapped:{x:Math.floor(a.x),y:Math.floor(a.y)}},{raw:{x:e.clientX-i.left,y:e.clientY-i.top},snapped:{x:o.x,y:o.y}},{raw:{x:e.clientX,y:e.clientY},snapped:{x:o.x+i.left,y:o.y+i.top}})}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=()=>{this.onMouseUp&&this.onMouseUp(),this.isDragging=!1,this.canvas.style.cursor=this.config.get().cursor.default||"default"};handleMouseLeave=()=>{this.onMouseLeave&&this.onMouseLeave(),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 n=this.getTouchDistance(e.touches),s=this.getTouchCenter(e.touches),a=this.canvas.getBoundingClientRect(),o=n/this.lastPinchDistance,l=s.x-a.left,c=s.y-a.top;this.camera.zoomByFactor(o,l,c);let h=s.x-this.lastPinchCenter.x,v=s.y-this.lastPinchCenter.y;(h!==0||v!==0)&&this.camera.pan(h,v),this.lastPinchDistance=n,this.lastPinchCenter=s,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.onCameraChange()}};var B=class{constructor(e,t,r,i,n,s){this.wrapper=e;this.canvas=t;this.viewport=r;this.camera=i;this.config=n;this.onCameraChange=s;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,n=t?.minWidth,s=t?.minHeight;e.width=this.clamp(e.width,n,r),e.height=this.clamp(e.height,s,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:n?`${n}px`:"",minHeight:s?`${s}px`:""}),this.resizeObserver=new ResizeObserver(a=>{for(let o of a){let{width:l,height:c}=o.contentRect,h=this.clamp(l,n,r),v=this.clamp(c,s,i),u=this.viewport.getSize();if(h===u.width&&v===u.height)continue;let d=h-u.width,m=v-u.height,C=this.viewport.dpr;this.camera.adjustForResize(d,m),this.viewport.setSize(h,v),this.canvas.width=h*C,this.canvas.height=v*C,this.canvas.style.width=`${h}px`,this.canvas.style.height=`${v}px`,this.wrapper.style.width=`${h}px`,this.wrapper.style.height=`${v}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,n,s,a){this.canvasWrapper=e;this.canvas=t;this.camera=r;this.viewport=i;this.config=n;this.coordinateTransformer=s;this.onCameraChange=a;this.gestures=new X(this.canvas,this.camera,this.viewport,this.config,this.coordinateTransformer,this.onCameraChange),this.binder=new Y(this.canvas,{click:this.gestures.handleClick,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 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}setupEvents(){this.attached||(this.binder.attach(),this.attached=!0,this.config.get().eventHandlers.resize&&this.camera instanceof D&&(this.resizeWatcher=new B(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 U=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=w.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,n)=>{let s=new Image;s.crossOrigin="anonymous",s.decoding="async",s.loading="eager",s.onload=async()=>{try{"decode"in s&&await s.decode?.()}catch{}this.cache.set(e,s),this.inflight.delete(e),this.notifyLoaded(),i(s)},s.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 o=a instanceof Error?a.message:typeof a=="string"?a:JSON.stringify(a);n(new Error(`Image failed to load: ${e}. Reason: ${o}`))}},s.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 V=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:n}of i)e.ctx.save(),n(e),e.ctx.restore()}}};var $=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, ${x.BORDER_OPACITY})`;let{width:e,height:t}=this.viewport.getSize();this.ctx.fillRect(0,0,x.BORDER_WIDTH,t),this.ctx.fillRect(x.BORDER_WIDTH,t-x.BORDER_WIDTH,e,x.BORDER_WIDTH),this.ctx.fillStyle=`rgba(255, 255, 255, ${x.TEXT_OPACITY})`;let r=Math.min(x.MAX_FONT_SIZE,Math.max(x.MIN_FONT_SIZE,this.camera.scale*x.FONT_SIZE_SCALE_FACTOR));this.ctx.font=`${r}px Arial`,this.ctx.textAlign="center",this.ctx.textBaseline="middle";let i=this.camera.scale,n=e/i,s=t/i;for(let a=0-this.camera.y%1;a<=s+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<=n+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 pe=10,H=class{ctx;camera;transformer;config;viewport;frameTimes=[];lastFrameTime=0;currentFps=0;fpsLoopRunning=!1;onFpsUpdate=null;constructor(e,t,r,i,n){this.ctx=e,this.camera=t,this.transformer=r,this.config=i,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>pe&&this.frameTimes.shift();let r=this.frameTimes.reduce((n,s)=>n+s,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:n,height:s}=this.viewport.getSize(),a=this.camera.getCenter(n,s);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:n,height:s}=this.viewport.getSize();t.push(`Tiles in view: ${Math.ceil(n/this.camera.scale)} x ${Math.ceil(s/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-T.PANEL_WIDTH-T.PADDING,T.PADDING/2,T.PANEL_WIDTH,t.length*T.LINE_HEIGHT+T.PADDING),this.ctx.fillStyle="#00ff99",this.ctx.font="12px monospace";for(let n=0;n<t.length;n++)this.ctx.fillText(t[n],i-T.PANEL_WIDTH-T.PADDING+5,18+n*T.LINE_HEIGHT);this.ctx.restore()}};var M=class{constructor(e,t,r,i,n,s){this.canvas=e;this.camera=t;this.coordinateTransformer=r;this.config=i;this.viewport=n;this.layers=s;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 H(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 H(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,n,s,a){this.canvasWrapper=e,this.canvas=t,this.camera=r,this.renderer=i,this.viewport=n,this.config=s,this.onSizeApplied=a}resizeWithAnimation(e,t,r,i,n){if(e<=0||t<=0)return;let s=this.config.get().size,a=(o,l,c)=>{let h=o;return l!==void 0&&(h=Math.max(l,h)),c!==void 0&&(h=Math.min(c,h)),h};e=a(e,s?.minWidth,s?.maxWidth),t=a(t,s?.minHeight,s?.maxHeight),i.animateResize(e,t,r,(o,l,c)=>this.applySize(o,l,c),n)}applySize(e,t,r){let i=Math.round(e),n=Math.round(t),s=this.viewport.dpr;this.viewport.setSize(i,n),this.canvasWrapper.style.width=`${i}px`,this.canvasWrapper.style.height=`${n}px`,this.canvas.width=i*s,this.canvas.height=n*s,this.canvas.style.width=`${i}px`,this.canvas.style.height=`${n}px`,this.camera.setCenter(r,i,n),this.renderer.resize(i,n),this.onSizeApplied()}};var q=class{constructor(e,t,r){this.camera=e;this.viewport=t;this.onAnimationFrame=r}moveAnimationId;resizeAnimationId;animateMoveTo(e,t,r=w.ANIMATION_DURATION_MS,i){if(this.cancelMove(),r<=0){let l=this.viewport.getSize();this.camera.setCenter({x:e,y:t},l.width,l.height),this.onAnimationFrame(),i?.();return}let n=this.viewport.getSize(),s=this.camera.getCenter(n.width,n.height),a=performance.now(),o=l=>{let c=l-a,h=Math.min(1,c/r),v=h<.5?2*h*h:1-Math.pow(-2*h+2,2)/2,u=s.x+(e-s.x)*v,d=s.y+(t-s.y)*v,m=this.viewport.getSize();this.camera.setCenter({x:u,y:d},m.width,m.height),this.onAnimationFrame(),h<1?this.moveAnimationId=requestAnimationFrame(o):(this.moveAnimationId=void 0,i?.())};this.moveAnimationId=requestAnimationFrame(o)}animateResize(e,t,r=w.ANIMATION_DURATION_MS,i,n){if(e<=0||t<=0)return;this.cancelResize();let s=this.viewport.getSize(),a=this.camera.getCenter(s.width,s.height);if(r<=0){i(e,t,a),n?.();return}let o=s.width,l=s.height,c=e-s.width,h=t-s.height,v=performance.now(),u=d=>{let m=d-v,C=Math.min(1,m/r),f=o+c*C,y=l+h*C;i(f,y,a),C<1?this.resizeAnimationId=requestAnimationFrame(u):(this.resizeAnimationId=void 0,n?.())};this.resizeAnimationId=requestAnimationFrame(u)}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 Z=class{static createRenderer(e,t,r,i,n,s,a){switch(e){case"canvas":return new M(t,r,i,n,s,a);default:throw new Error(`Unsupported renderer type: ${e}`)}}static isSupported(e){return e==="canvas"}static getSupportedTypes(){return["canvas"]}};var J=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}_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}constructor(e,t,r={x:0,y:0}){this.canvasWrapper=e,this.canvas=e.querySelector("canvas"),this.canvasWrapper.style.position="relative",this.canvasWrapper.style.width=t.size.width+"px",this.canvasWrapper.style.height=t.size.height+"px",this.canvas.style.position="absolute",this.config=new F(t);let i=t.renderer??"canvas",n={x:r.x-t.size.width/(2*t.scale),y:r.y-t.size.height/(2*t.scale)};this.viewport=new $(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 W(this.camera),this.animationController=new q(this.camera,this.viewport,()=>this.handleCameraChange()),this.renderer=this.createRenderer(i),this.images=new U,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)}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 V,this.draw=new P(this.layers,this.coordinateTransformer,this.camera)),Z.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 M))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 le=Object.create;var z=Object.defineProperty;var he=Object.getOwnPropertyDescriptor;var ce=Object.getOwnPropertyNames;var de=Object.getPrototypeOf,me=Object.prototype.hasOwnProperty;var ue=(p,e)=>{for(var t in e)z(p,t,{get:e[t],enumerable:!0})},te=(p,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of ce(e))!me.call(p,r)&&r!==t&&z(p,r,{get:()=>e[r],enumerable:!(i=he(e,r))||i.enumerable});return p};var ve=(p,e,t)=>(t=p!=null?le(de(p)):{},te(e||!p||!p.__esModule?z(t,"default",{value:p,enumerable:!0}):t,p)),fe=p=>te(z({},"__esModule",{value:!0}),p);var ge={};ue(ge,{COORDINATE_OVERLAY:()=>x,CanvasTileEngine:()=>J,DEBUG_HUD:()=>T,DEFAULT_VALUES:()=>b,RENDER_DEFAULTS:()=>O,SCALE_LIMITS:()=>A,SIZE_LIMITS:()=>M,VISIBILITY_BUFFER:()=>S});module.exports=fe(ge);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},A={MIN_SCALE_MULTIPLIER:.5,MAX_SCALE_MULTIPLIER:2},M={MIN_WIDTH:100,MIN_HEIGHT:100,MAX_WIDTH:1/0,MAX_HEIGHT:1/0},O={BACKGROUND_COLOR:"#ffffff",RENDERER_TYPE:"canvas"},x={BORDER_WIDTH:20,TEXT_OPACITY:.8,BORDER_OPACITY:.1,MIN_FONT_SIZE:8,MAX_FONT_SIZE:12,FONT_SIZE_SCALE_FACTOR:.25},T={PANEL_WIDTH:160,PADDING:8,LINE_HEIGHT:16},S={TILE_BUFFER:1};function ie(p,e,t,i){return{x:p.x-t/e,y:p.y-i/e}}function re(p,e,t,i,r,s){let n=Math.min(Math.max(t,b.MIN_WHEEL_DELTA),b.MAX_WHEEL_DELTA),a=Math.exp(-n*b.ZOOM_SENSITIVITY),o=Math.min(r,Math.max(i,e*a));return o===e?{topLeft:p,scale:e}:{topLeft:{x:p.x+s.x*(1/e-1/o),y:p.y+s.y*(1/e-1/o)},scale:o}}function ne(p,e){return{x:(p.x+b.CELL_CENTER_OFFSET-e.x)*e.scale,y:(p.y+b.CELL_CENTER_OFFSET-e.y)*e.scale}}function se(p,e){return{x:e.x+p.x/e.scale,y:e.y+p.y/e.scale}}var R=class{_x;_y;_scale;minScale;maxScale;bounds;viewport;constructor(e,t=1,i=.1,r=10,s){this._x=e.x+b.CELL_CENTER_OFFSET,this._y=e.y+b.CELL_CENTER_OFFSET,this._scale=t,this.minScale=i,this.maxScale=r,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(),i=e/this._scale,r=t/this._scale;this._x=this.clampAxis(this._x,i,this.bounds.minX,this.bounds.maxX),this._y=this.clampAxis(this._y,r,this.bounds.minY,this.bounds.maxY)}clampAxis(e,t,i,r){let s=r-i;return t>=s?i-(t-s)/2:e<i?i:e+t>r?r-t:e}get x(){return this._x}get y(){return this._y}get scale(){return this._scale}pan(e,t){let i=ie({x:this._x,y:this._y},this._scale,e,t);this._x=i.x,this._y=i.y,this.clampToBounds()}zoom(e,t,i,r){let s=e-r.left,n=t-r.top,a=re({x:this._x,y:this._y},this._scale,i,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,i){let r=Math.min(this.maxScale,Math.max(this.minScale,this._scale*e));r!==this._scale&&(this._x=this._x+t*(1/this._scale-1/r),this._y=this._y+i*(1/this._scale-1/r),this._scale=r,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-b.CELL_CENTER_OFFSET,r=this._y-b.CELL_CENTER_OFFSET,s=i+e/this._scale,n=r+t/this._scale;return{minX:Math.floor(i),maxX:Math.ceil(s),minY:Math.floor(r),maxY:Math.ceil(n)}}};var F=class{config;constructor(e){let t={renderer:O.RENDERER_TYPE,scale:e.scale,minScale:e.minScale??e.scale*A.MIN_SCALE_MULTIPLIER,maxScale:e.maxScale??e.scale*A.MAX_SCALE_MULTIPLIER,gridAligned:e.gridAligned??!1,size:{width:e.size.width,height:e.size.height,maxHeight:e.size.maxHeight??M.MAX_HEIGHT,maxWidth:e.size.maxWidth??M.MAX_WIDTH,minHeight:e.size.minHeight??M.MIN_HEIGHT,minWidth:e.size.minWidth??M.MIN_WIDTH},backgroundColor:e.backgroundColor??O.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 W=class{constructor(e){this.camera=e}worldToScreen(e,t){return ne({x:e,y:t},{x:this.camera.x,y:this.camera.y,scale:this.camera.scale})}screenToWorld(e,t){return se({x:e,y:t},{x:this.camera.x,y:this.camera.y,scale:this.camera.scale})}};var ae=ve(require("rbush")),D=class p{tree;constructor(){this.tree=new ae.default}load(e){let t=e.map(i=>{let s=(typeof i.size=="number"?i.size:0)/2;return{minX:i.x-s,minY:i.y-s,maxX:i.x+s,maxY:i.y+s,item:i}});this.tree.load(t)}query(e,t,i,r){return this.tree.search({minX:e,minY:t,maxX:i,maxY:r}).map(n=>n.item)}clear(){this.tree.clear()}static fromArray(e){let t=new p;return t.load(e),t}};var ee=500,oe=16384,Y=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,r,s){let n=s.size.width/s.scale,a=s.size.height/s.scale,o=r.x-S.TILE_BUFFER,h=r.y-S.TILE_BUFFER,c=r.x+n+S.TILE_BUFFER,l=r.y+a+S.TILE_BUFFER;return e+i>=o&&e-i<=c&&t+i>=h&&t-i<=l}getViewportBounds(e,t){let i=t.size.width/t.scale,r=t.size.height/t.scale;return{minX:e.x-S.TILE_BUFFER,minY:e.y-S.TILE_BUFFER,maxX:e.x+i+S.TILE_BUFFER,maxY:e.y+r+S.TILE_BUFFER}}addDrawFunction(e,t=1){return this.layers.add(t,({ctx:i,config:r,topLeft:s})=>{e(i,s,r)})}drawRect(e,t=1){let i=Array.isArray(e)?e:[e],s=i.length>ee?D.fromArray(i):null;return this.layers.add(t,({ctx:n,config:a,topLeft:o})=>{let h=this.getViewportBounds(o,a),c=s?s.query(h.minX,h.minY,h.maxX,h.maxY):i;n.save();let l,f,u;for(let d of c){let m=d.size??1,C={mode:d.origin?.mode==="self"?"self":"cell",x:d.origin?.x??.5,y:d.origin?.y??.5},v=d.style;if(!s&&!this.isVisible(d.x,d.y,m/2,o,a))continue;let w=this.transformer.worldToScreen(d.x,d.y),g=m*this.camera.scale,{x:y,y:E}=this.computeOriginOffset(w,g,C,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),v?.lineWidth&&v.lineWidth!==u&&(n.lineWidth=v.lineWidth,u=v.lineWidth);let I=d.rotate??0,k=I*(Math.PI/180),L=d.radius;if(I!==0){let K=y+g/2,Q=E+g/2;n.save(),n.translate(K,Q),n.rotate(k),n.beginPath(),L&&n.roundRect?n.roundRect(-g/2,-g/2,g,g,L):n.rect(-g/2,-g/2,g,g),v?.fillStyle&&n.fill(),v?.strokeStyle&&n.stroke(),n.restore()}else n.beginPath(),L&&n.roundRect?n.roundRect(y,E,g,g,L):n.rect(y,E,g,g),v?.fillStyle&&n.fill(),v?.strokeStyle&&n.stroke()}n.restore()})}drawLine(e,t,i=1){let r=Array.isArray(e)?e:[e];return this.layers.add(i,({ctx:s,config:n,topLeft:a})=>{s.save(),t?.strokeStyle&&(s.strokeStyle=t.strokeStyle),t?.lineWidth&&(s.lineWidth=t.lineWidth),s.beginPath();for(let o of r){let h=(o.from.x+o.to.x)/2,c=(o.from.y+o.to.y)/2,l=Math.max(Math.abs(o.from.x-o.to.x),Math.abs(o.from.y-o.to.y))/2;if(!this.isVisible(h,c,l,a,n))continue;let f=this.transformer.worldToScreen(o.from.x,o.from.y),u=this.transformer.worldToScreen(o.to.x,o.to.y);s.moveTo(f.x,f.y),s.lineTo(u.x,u.y)}s.stroke(),s.restore()})}drawCircle(e,t=1){let i=Array.isArray(e)?e:[e],s=i.length>ee?D.fromArray(i):null;return this.layers.add(t,({ctx:n,config:a,topLeft:o})=>{let h=this.getViewportBounds(o,a),c=s?s.query(h.minX,h.minY,h.maxX,h.maxY):i;n.save();let l,f,u;for(let d of c){let m=d.size??1,C={mode:d.origin?.mode==="self"?"self":"cell",x:d.origin?.x??.5,y:d.origin?.y??.5},v=d.style;if(!s&&!this.isVisible(d.x,d.y,m/2,o,a))continue;let w=this.transformer.worldToScreen(d.x,d.y),g=m*this.camera.scale,y=g/2,{x:E,y:I}=this.computeOriginOffset(w,g,C,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),v?.lineWidth&&v.lineWidth!==u&&(n.lineWidth=v.lineWidth,u=v.lineWidth),n.beginPath(),n.arc(E+y,I+y,y,0,Math.PI*2),v?.fillStyle&&n.fill(),v?.strokeStyle&&n.stroke()}n.restore()})}drawText(e,t,i=2){let r=Array.isArray(e)?e:[e];return this.layers.add(i,({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 o of r){if(!this.isVisible(o.coords.x,o.coords.y,0,a,n))continue;let h=this.transformer.worldToScreen(o.coords.x,o.coords.y);s.fillText(o.text,h.x,h.y)}s.restore()})}drawPath(e,t,i=1){let r=Array.isArray(e[0])?e:[e];return this.layers.add(i,({ctx:s,config:n,topLeft:a})=>{s.save(),t?.strokeStyle&&(s.strokeStyle=t.strokeStyle),t?.lineWidth&&(s.lineWidth=t.lineWidth),s.beginPath();for(let o of r){if(!o.length)continue;let h=o.map(g=>g.x),c=o.map(g=>g.y),l=Math.min(...h),f=Math.max(...h),u=Math.min(...c),d=Math.max(...c),m=(l+f)/2,C=(u+d)/2,v=Math.max(f-l,d-u)/2;if(!this.isVisible(m,C,v,a,n))continue;let w=this.transformer.worldToScreen(o[0].x,o[0].y);s.moveTo(w.x,w.y);for(let g=1;g<o.length;g++){let y=this.transformer.worldToScreen(o[g].x,o[g].y);s.lineTo(y.x,y.y)}}s.stroke(),s.restore()})}drawImage(e,t=1){let i=Array.isArray(e)?e:[e],s=i.length>ee?D.fromArray(i):null;return this.layers.add(t,({ctx:n,config:a,topLeft:o})=>{let h=this.getViewportBounds(o,a),c=s?s.query(h.minX,h.minY,h.maxX,h.maxY):i;for(let l of c){let f=l.size??1,u={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,o,a))continue;let d=this.transformer.worldToScreen(l.x,l.y),m=f*this.camera.scale,C=l.img.width/l.img.height,v=m,w=m;C>1?w=m/C:v=m*C;let{x:g,y}=this.computeOriginOffset(d,m,u,this.camera),E=g+(m-v)/2,I=y+(m-w)/2,k=l.rotate??0,L=k*(Math.PI/180);if(k!==0){let K=E+v/2,Q=I+w/2;n.save(),n.translate(K,Q),n.rotate(L),n.drawImage(l.img,-v/2,-w/2,v,w),n.restore()}else n.drawImage(l.img,E,I,v,w)}})}drawGridLines(e,t,i=0){return this.layers.add(i,({ctx:r,config:s,topLeft:n})=>{let a=s.size.width/s.scale,o=s.size.height/s.scale,h=Math.floor(n.x/e)*e-b.CELL_CENTER_OFFSET,c=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+o)/e)*e-b.CELL_CENTER_OFFSET;r.save(),r.strokeStyle=t.strokeStyle,r.lineWidth=t.lineWidth,r.beginPath();for(let u=h;u<=c;u+=e){let d=this.transformer.worldToScreen(u,l),m=this.transformer.worldToScreen(u,f);r.moveTo(d.x,d.y),r.lineTo(m.x,m.y)}for(let u=l;u<=f;u+=e){let d=this.transformer.worldToScreen(h,u),m=this.transformer.worldToScreen(c,u);r.moveTo(d.x,d.y),r.lineTo(m.x,m.y)}r.stroke(),r.restore()})}computeOriginOffset(e,t,i,r){if(i.mode==="cell"){let s=r.scale;return{x:e.x-s/2+i.x*s-t/2,y:e.y-s/2+i.y*s-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 r=1/0,s=-1/0,n=1/0,a=-1/0;for(let m of e){let C=m.size??1;m.x-C/2<r&&(r=m.x-C/2),m.x+C/2>s&&(s=m.x+C/2),m.y-C/2<n&&(n=m.y-C/2),m.y+C/2>a&&(a=m.y+C/2)}r-=1,n-=1,s+=1,a+=1;let o=s-r,h=a-n,c=this.camera.scale,l=Math.ceil(o*c),f=Math.ceil(h*c);if(l>oe||f>oe)return this.warnedStaticCacheDisabled||(console.warn(`Static cache disabled: offscreen canvas too large (${l}x${f}).`),this.warnedStaticCacheDisabled=!0),null;let u=this.staticCaches.get(t);if(!u||u.scale!==c||u.worldBounds.minX!==r||u.worldBounds.maxX!==s||u.worldBounds.minY!==n||u.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 w of e){let y=(w.size??1)*c,E=(w.x+b.CELL_CENTER_OFFSET-r)*c-y/2,I=(w.y+b.CELL_CENTER_OFFSET-n)*c-y/2;i(v,w,E,I,y)}u={canvas:m,ctx:v,worldBounds:{minX:r,minY:n,maxX:s,maxY:a},scale:c},this.staticCaches.set(t,u)}return u||null}addStaticCacheLayer(e,t){if(!e)return null;let i=e.canvas,r=e.worldBounds,s=e.scale;return this.layers.add(t,({ctx:n,config:a,topLeft:o})=>{let h=a.size.width/a.scale,c=a.size.height/a.scale,l=(o.x-r.minX)*s,f=(o.y-r.minY)*s,u=h*s,d=c*s;n.drawImage(i,l,f,u,d,0,0,a.size.width,a.size.height)})}drawStaticRect(e,t,i=1){let r,s=this.getOrCreateStaticCache(e,t,(n,a,o,h,c)=>{let l=a.style,f=a.rotate??0,u=f*(Math.PI/180),d=a.radius;if(l?.fillStyle&&l.fillStyle!==r&&(n.fillStyle=l.fillStyle,r=l.fillStyle),f!==0){let m=o+c/2,C=h+c/2;n.save(),n.translate(m,C),n.rotate(u),d&&n.roundRect?(n.beginPath(),n.roundRect(-c/2,-c/2,c,c,d),n.fill()):n.fillRect(-c/2,-c/2,c,c),n.restore()}else d&&n.roundRect?(n.beginPath(),n.roundRect(o,h,c,c,d),n.fill()):n.fillRect(o,h,c,c)});return s?this.addStaticCacheLayer(s,i):this.drawRect(e,i)}drawStaticImage(e,t,i=1){let r=this.getOrCreateStaticCache(e,t,(s,n,a,o,h)=>{let c=n.img,l=n.rotate??0,f=l*(Math.PI/180),u=c.width/c.height,d=h,m=h;u>1?m=h/u:d=h*u;let C=a+(h-d)/2,v=o+(h-m)/2;if(l!==0){let w=C+d/2,g=v+m/2;s.save(),s.translate(w,g),s.rotate(f),s.drawImage(c,-d/2,-m/2,d,m),s.restore()}else s.drawImage(c,C,v,d,m)});return r?this.addStaticCacheLayer(r,i):this.drawImage(e,i)}drawStaticCircle(e,t,i=1){let r,s=this.getOrCreateStaticCache(e,t,(n,a,o,h,c)=>{let l=a.style,f=c/2;l?.fillStyle&&l.fillStyle!==r&&(n.fillStyle=l.fillStyle,r=l.fillStyle),n.beginPath(),n.arc(o+f,h+f,f,0,Math.PI*2),n.fill()});return s?this.addStaticCacheLayer(s,i):this.drawCircle(e,i)}clearStaticCache(e){e?this.staticCaches.delete(e):this.staticCaches.clear()}destroy(){this.staticCaches.clear(),this.layers.clear()}};var P=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 X=class{constructor(e,t,i,r,s,n){this.canvas=e;this.camera=t;this.viewport=i;this.config=r;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(),i=e.clientX-t.left,r=e.clientY-t.top,s=this.transformer.screenToWorld(i,r),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:i,y:r},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:i,client:r}=this.getEventCoords(e);this.onClick(t,i,r)};handleContextMenu=e=>{if(!this.config.get().eventHandlers.rightClick||!this.onRightClick)return;e.preventDefault();let{coords:t,mouse:i,client:r}=this.getEventCoords(e);this.onRightClick(t,i,r)};handleMouseDown=e=>{if(this.onMouseDown){let{coords:t,mouse:i,client:r}=this.getEventCoords(e);this.onMouseDown(t,i,r)}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:r,mouse:s,client:n}=this.getEventCoords(e);this.onHover(r,s,n)}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:r}=this.getEventCoords(e);this.onMouseUp(t,i,r)}this.isDragging=!1,this.canvas.style.cursor=this.config.get().cursor.default||"default"};handleMouseLeave=e=>{if(this.onMouseLeave){let{coords:t,mouse:i,client:r}=this.getEventCoords(e);this.onMouseLeave(t,i,r)}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 i=e.touches[0];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 s=this.getTouchDistance(e.touches),n=this.getTouchCenter(e.touches),a=this.canvas.getBoundingClientRect(),o=s/this.lastPinchDistance,h=n.x-a.left,c=n.y-a.top;this.camera.zoomByFactor(o,h,c);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],i=t.clientX-this.lastPos.x,r=t.clientY-this.lastPos.y;(i!==0||r!==0)&&(this.canvas.style.cursor=this.config.get().cursor.move||"move",this.shouldPreventClick=!0),this.camera.pan(i,r),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,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 B=class{constructor(e,t,i,r,s,n){this.wrapper=e;this.canvas=t;this.viewport=i;this.camera=r;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,i=t?.maxWidth,r=t?.maxHeight,s=t?.minWidth,n=t?.minHeight;e.width=this.clamp(e.width,s,i),e.height=this.clamp(e.height,n,r),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:r?`${r}px`:"",minWidth:s?`${s}px`:"",minHeight:n?`${n}px`:""}),this.resizeObserver=new ResizeObserver(a=>{for(let o of a){let{width:h,height:c}=o.contentRect,l=this.clamp(h,s,i),f=this.clamp(c,n,r),u=this.viewport.getSize();if(l===u.width&&f===u.height)continue;let d=l-u.width,m=f-u.height,C=this.viewport.dpr;this.camera.adjustForResize(d,m),this.viewport.setSize(l,f),this.canvas.width=l*C,this.canvas.height=f*C,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,i){let r=e;return t!==void 0&&(r=Math.max(t,r)),i!==void 0&&(r=Math.min(i,r)),r}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:r}=this.viewport.getSize();this.canvas.width=i*t,this.canvas.height=r*t,this.canvas.style.width=`${i}px`,this.canvas.style.height=`${r}px`,this.onResize&&this.onResize(),this.onCameraChange()},window.addEventListener("resize",this.handleWindowResize,{passive:!0}))}};var U=class{constructor(e,t,i,r,s,n,a){this.canvasWrapper=e;this.canvas=t;this.camera=i;this.viewport=r;this.config=s;this.coordinateTransformer=n;this.onCameraChange=a;this.gestures=new X(this.canvas,this.camera,this.viewport,this.config,this.coordinateTransformer,this.onCameraChange),this.binder=new P(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 R&&(this.resizeWatcher=new B(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 N=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 i=new Promise((r,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(),r(n)},n.onerror=a=>{if(this.inflight.delete(e),t>0)console.warn(`Retrying image: ${e}`),r(this.load(e,t-1));else{console.error(`Image failed to load: ${e}`,a);let o=a instanceof Error?a.message:typeof a=="string"?a:JSON.stringify(a);s(new Error(`Image failed to load: ${e}. Reason: ${o}`))}},n.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 V=class{layers=new Map;add(e,t){let i=Symbol("layer-callback"),r={id:i,fn:t};return this.layers.has(e)||this.layers.set(e,[]),this.layers.get(e).push(r),{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,r)=>i-r);for(let i of t){let r=this.layers.get(i);if(r)for(let{fn:s}of r)e.ctx.save(),s(e),e.ctx.restore()}}};var $=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 Z=class{ctx;camera;config;viewport;constructor(e,t,i,r){this.ctx=e,this.camera=t,this.config=i,this.viewport=r}draw(){this.ctx.save(),this.ctx.fillStyle=`rgba(0, 0, 0, ${x.BORDER_OPACITY})`;let{width:e,height:t}=this.viewport.getSize();this.ctx.fillRect(0,0,x.BORDER_WIDTH,t),this.ctx.fillRect(x.BORDER_WIDTH,t-x.BORDER_WIDTH,e,x.BORDER_WIDTH),this.ctx.fillStyle=`rgba(255, 255, 255, ${x.TEXT_OPACITY})`;let i=Math.min(x.MAX_FONT_SIZE,Math.max(x.MIN_FONT_SIZE,this.camera.scale*x.FONT_SIZE_SCALE_FACTOR));this.ctx.font=`${i}px Arial`,this.ctx.textAlign="center",this.ctx.textBaseline="middle";let r=this.camera.scale,s=e/r,n=t/r;for(let a=0-this.camera.y%1;a<=n+1;a++)this.ctx.fillText(Math.round(this.camera.y+a).toString(),10,r*a+r/2);for(let a=0-this.camera.x%1;a<=s+1;a++)this.ctx.fillText(Math.round(this.camera.x+a).toString(),r*a+r/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:r}=t.shownScaleRange;return e>=i&&e<=r}};var pe=10,H=class{ctx;camera;transformer;config;viewport;frameTimes=[];lastFrameTime=0;currentFps=0;fpsLoopRunning=!1;onFpsUpdate=null;constructor(e,t,i,r,s){this.ctx=e,this.camera=t,this.transformer=i,this.config=r,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>pe&&this.frameTimes.shift();let i=this.frameTimes.reduce((s,n)=>s+n,0)/this.frameTimes.length,r=Math.round(1e3/i);r!==this.currentFps&&(this.currentFps=r,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: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:r}=this.viewport.getSize();this.ctx.save(),this.ctx.fillStyle="rgba(0,0,0,0.5)",this.ctx.fillRect(r-T.PANEL_WIDTH-T.PADDING,T.PADDING/2,T.PANEL_WIDTH,t.length*T.LINE_HEIGHT+T.PADDING),this.ctx.fillStyle="#00ff99",this.ctx.font="12px monospace";for(let s=0;s<t.length;s++)this.ctx.fillText(t[s],r-T.PANEL_WIDTH-T.PADDING+5,18+s*T.LINE_HEIGHT);this.ctx.restore()}};var _=class{constructor(e,t,i,r,s,n){this.canvas=e;this.camera=t;this.coordinateTransformer=i;this.config=r;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 Z(this.ctx,this.camera,this.config,this.viewport),this.config.get().debug?.enabled&&(this.debugOverlay=new H(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},r={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:r}),this.onDraw?.(this.ctx,{scale:this.camera.scale,width:i.size.width,height:i.size.height,coords:r}),this.coordinateOverlayRenderer.shouldDraw(this.camera.scale)&&this.coordinateOverlayRenderer.draw(),i.debug?.enabled&&(this.debugOverlay||(this.debugOverlay=new H(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,r,s,n,a){this.canvasWrapper=e,this.canvas=t,this.camera=i,this.renderer=r,this.viewport=s,this.config=n,this.onSizeApplied=a}resizeWithAnimation(e,t,i,r,s){if(e<=0||t<=0)return;let n=this.config.get().size,a=(o,h,c)=>{let l=o;return h!==void 0&&(l=Math.max(h,l)),c!==void 0&&(l=Math.min(c,l)),l};e=a(e,n?.minWidth,n?.maxWidth),t=a(t,n?.minHeight,n?.maxHeight),r.animateResize(e,t,i,(o,h,c)=>this.applySize(o,h,c),s)}applySize(e,t,i){let r=Math.round(e),s=Math.round(t),n=this.viewport.dpr;this.viewport.setSize(r,s),this.canvasWrapper.style.width=`${r}px`,this.canvasWrapper.style.height=`${s}px`,this.canvas.width=r*n,this.canvas.height=s*n,this.canvas.style.width=`${r}px`,this.canvas.style.height=`${s}px`,this.camera.setCenter(i,r,s),this.renderer.resize(r,s),this.onSizeApplied()}};var G=class{constructor(e,t,i){this.camera=e;this.viewport=t;this.onAnimationFrame=i}moveAnimationId;resizeAnimationId;animateMoveTo(e,t,i=b.ANIMATION_DURATION_MS,r){if(this.cancelMove(),i<=0){let h=this.viewport.getSize();this.camera.setCenter({x:e,y:t},h.width,h.height),this.onAnimationFrame(),r?.();return}let s=this.viewport.getSize(),n=this.camera.getCenter(s.width,s.height),a=performance.now(),o=h=>{let c=h-a,l=Math.min(1,c/i),f=l<.5?2*l*l:1-Math.pow(-2*l+2,2)/2,u=n.x+(e-n.x)*f,d=n.y+(t-n.y)*f,m=this.viewport.getSize();this.camera.setCenter({x:u,y:d},m.width,m.height),this.onAnimationFrame(),l<1?this.moveAnimationId=requestAnimationFrame(o):(this.moveAnimationId=void 0,r?.())};this.moveAnimationId=requestAnimationFrame(o)}animateResize(e,t,i=b.ANIMATION_DURATION_MS,r,s){if(e<=0||t<=0)return;this.cancelResize();let n=this.viewport.getSize(),a=this.camera.getCenter(n.width,n.height);if(i<=0){r(e,t,a),s?.();return}let o=n.width,h=n.height,c=e-n.width,l=t-n.height,f=performance.now(),u=d=>{let m=d-f,C=Math.min(1,m/i),v=o+c*C,w=h+l*C;r(v,w,a),C<1?this.resizeAnimationId=requestAnimationFrame(u):(this.resizeAnimationId=void 0,s?.())};this.resizeAnimationId=requestAnimationFrame(u)}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,r,s,n,a){switch(e){case"canvas":return new _(t,i,r,s,n,a);default:throw new Error(`Unsupported renderer type: ${e}`)}}static isSupported(e){return e==="canvas"}static getSupportedTypes(){return["canvas"]}};var J=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,i={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 F(t);let r=t.renderer??"canvas",s=t.gridAligned?{x:Math.floor(i.x)+.5,y:Math.floor(i.y)+.5}:i,n={x:s.x-t.size.width/(2*t.scale),y:s.y-t.size.height/(2*t.scale)};this.viewport=new $(t.size.width,t.size.height),this.camera=new R(n,this.config.get().scale,this.config.get().minScale,this.config.get().maxScale,this.viewport),this.coordinateTransformer=new W(this.camera),this.animationController=new G(this.camera,this.viewport,()=>this.handleCameraChange()),this.renderer=this.createRenderer(r),this.images=new N,this.events=new U(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.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,i=500,r){this.sizeController.resizeWithAnimation(e,t,i,this.animationController,()=>{this._onResize?.(),r?.()})}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,i=500,r){this.animationController.animateMoveTo(e,t,i,r)}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,i=2){return this.ensureCanvasDraw().drawText(e,t,i)}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",r=0){return this.ensureCanvasDraw().drawGridLines(e,{lineWidth:t,strokeStyle:i},r)}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 V,this.draw=new Y(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 _))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});
2
2
  //# sourceMappingURL=index.js.map