@canvas-tile-engine/core 0.0.2 → 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/dist/index.d.mts +39 -9
- package/dist/index.d.ts +39 -9
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.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
|
|
112
|
+
type MouseEventCallback = (coords: {
|
|
110
113
|
raw: Coords;
|
|
111
114
|
snapped: Coords;
|
|
112
115
|
}, mouse: {
|
|
@@ -116,7 +119,12 @@ type onClickCallback = (coords: {
|
|
|
116
119
|
raw: Coords;
|
|
117
120
|
snapped: Coords;
|
|
118
121
|
}) => void;
|
|
119
|
-
type
|
|
122
|
+
type onClickCallback = MouseEventCallback;
|
|
123
|
+
type onHoverCallback = MouseEventCallback;
|
|
124
|
+
type onMouseDownCallback = MouseEventCallback;
|
|
125
|
+
type onMouseUpCallback = MouseEventCallback;
|
|
126
|
+
type onMouseLeaveCallback = MouseEventCallback;
|
|
127
|
+
type onRightClickCallback = MouseEventCallback;
|
|
120
128
|
type onZoomCallback = (scale: number) => void;
|
|
121
129
|
type DrawObject = {
|
|
122
130
|
x: number;
|
|
@@ -178,18 +186,21 @@ declare class CanvasTileEngine {
|
|
|
178
186
|
private _onClick?;
|
|
179
187
|
get onClick(): onClickCallback | undefined;
|
|
180
188
|
set onClick(cb: onClickCallback | undefined);
|
|
189
|
+
private _onRightClick?;
|
|
190
|
+
get onRightClick(): onRightClickCallback | undefined;
|
|
191
|
+
set onRightClick(cb: onRightClickCallback | undefined);
|
|
181
192
|
private _onHover?;
|
|
182
193
|
get onHover(): onHoverCallback | undefined;
|
|
183
194
|
set onHover(cb: onHoverCallback | undefined);
|
|
184
195
|
private _onMouseDown?;
|
|
185
|
-
get onMouseDown():
|
|
186
|
-
set onMouseDown(cb:
|
|
196
|
+
get onMouseDown(): onMouseDownCallback | undefined;
|
|
197
|
+
set onMouseDown(cb: onMouseDownCallback | undefined);
|
|
187
198
|
private _onMouseUp?;
|
|
188
|
-
get onMouseUp():
|
|
189
|
-
set onMouseUp(cb:
|
|
199
|
+
get onMouseUp(): onMouseUpCallback | undefined;
|
|
200
|
+
set onMouseUp(cb: onMouseUpCallback | undefined);
|
|
190
201
|
private _onMouseLeave?;
|
|
191
|
-
get onMouseLeave():
|
|
192
|
-
set onMouseLeave(cb:
|
|
202
|
+
get onMouseLeave(): onMouseLeaveCallback | undefined;
|
|
203
|
+
set onMouseLeave(cb: onMouseLeaveCallback | undefined);
|
|
193
204
|
private _onDraw?;
|
|
194
205
|
get onDraw(): onDrawCallback | undefined;
|
|
195
206
|
set onDraw(cb: onDrawCallback | undefined);
|
|
@@ -245,6 +256,25 @@ declare class CanvasTileEngine {
|
|
|
245
256
|
getConfig(): Required<CanvasTileEngineConfig>;
|
|
246
257
|
/** Center coordinates of the map. */
|
|
247
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
|
+
};
|
|
248
278
|
/** Set center coordinates from outside (adjusts the camera accordingly). */
|
|
249
279
|
updateCoords(newCenter: Coords): void;
|
|
250
280
|
/**
|
|
@@ -516,4 +546,4 @@ declare const VISIBILITY_BUFFER: {
|
|
|
516
546
|
readonly TILE_BUFFER: 1;
|
|
517
547
|
};
|
|
518
548
|
|
|
519
|
-
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 onZoomCallback };
|
|
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
|
|
112
|
+
type MouseEventCallback = (coords: {
|
|
110
113
|
raw: Coords;
|
|
111
114
|
snapped: Coords;
|
|
112
115
|
}, mouse: {
|
|
@@ -116,7 +119,12 @@ type onClickCallback = (coords: {
|
|
|
116
119
|
raw: Coords;
|
|
117
120
|
snapped: Coords;
|
|
118
121
|
}) => void;
|
|
119
|
-
type
|
|
122
|
+
type onClickCallback = MouseEventCallback;
|
|
123
|
+
type onHoverCallback = MouseEventCallback;
|
|
124
|
+
type onMouseDownCallback = MouseEventCallback;
|
|
125
|
+
type onMouseUpCallback = MouseEventCallback;
|
|
126
|
+
type onMouseLeaveCallback = MouseEventCallback;
|
|
127
|
+
type onRightClickCallback = MouseEventCallback;
|
|
120
128
|
type onZoomCallback = (scale: number) => void;
|
|
121
129
|
type DrawObject = {
|
|
122
130
|
x: number;
|
|
@@ -178,18 +186,21 @@ declare class CanvasTileEngine {
|
|
|
178
186
|
private _onClick?;
|
|
179
187
|
get onClick(): onClickCallback | undefined;
|
|
180
188
|
set onClick(cb: onClickCallback | undefined);
|
|
189
|
+
private _onRightClick?;
|
|
190
|
+
get onRightClick(): onRightClickCallback | undefined;
|
|
191
|
+
set onRightClick(cb: onRightClickCallback | undefined);
|
|
181
192
|
private _onHover?;
|
|
182
193
|
get onHover(): onHoverCallback | undefined;
|
|
183
194
|
set onHover(cb: onHoverCallback | undefined);
|
|
184
195
|
private _onMouseDown?;
|
|
185
|
-
get onMouseDown():
|
|
186
|
-
set onMouseDown(cb:
|
|
196
|
+
get onMouseDown(): onMouseDownCallback | undefined;
|
|
197
|
+
set onMouseDown(cb: onMouseDownCallback | undefined);
|
|
187
198
|
private _onMouseUp?;
|
|
188
|
-
get onMouseUp():
|
|
189
|
-
set onMouseUp(cb:
|
|
199
|
+
get onMouseUp(): onMouseUpCallback | undefined;
|
|
200
|
+
set onMouseUp(cb: onMouseUpCallback | undefined);
|
|
190
201
|
private _onMouseLeave?;
|
|
191
|
-
get onMouseLeave():
|
|
192
|
-
set onMouseLeave(cb:
|
|
202
|
+
get onMouseLeave(): onMouseLeaveCallback | undefined;
|
|
203
|
+
set onMouseLeave(cb: onMouseLeaveCallback | undefined);
|
|
193
204
|
private _onDraw?;
|
|
194
205
|
get onDraw(): onDrawCallback | undefined;
|
|
195
206
|
set onDraw(cb: onDrawCallback | undefined);
|
|
@@ -245,6 +256,25 @@ declare class CanvasTileEngine {
|
|
|
245
256
|
getConfig(): Required<CanvasTileEngineConfig>;
|
|
246
257
|
/** Center coordinates of the map. */
|
|
247
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
|
+
};
|
|
248
278
|
/** Set center coordinates from outside (adjusts the camera accordingly). */
|
|
249
279
|
updateCoords(newCenter: Coords): void;
|
|
250
280
|
/**
|
|
@@ -516,4 +546,4 @@ declare const VISIBILITY_BUFFER: {
|
|
|
516
546
|
readonly TILE_BUFFER: 1;
|
|
517
547
|
};
|
|
518
548
|
|
|
519
|
-
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 onZoomCallback };
|
|
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:()=>b,RENDER_DEFAULTS:()=>k,SCALE_LIMITS:()=>O,SIZE_LIMITS:()=>R,VISIBILITY_BUFFER:()=>S});module.exports=ve(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},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,b.MIN_WHEEL_DELTA),b.MAX_WHEEL_DELTA),a=Math.exp(-s*b.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+b.CELL_CENTER_OFFSET-e.x)*e.scale,y:(p.y+b.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+b.CELL_CENTER_OFFSET,this._y=e.y+b.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:w,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=w+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(w,E,g,g,L):s.rect(w,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,w=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+w,I+w,w,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 w=this.transformer.worldToScreen(o[g].x,o[g].y);n.lineTo(w.x,w.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:w}=this.computeOriginOffset(d,m,u,this.camera),E=g+(m-f)/2,I=w+(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-b.CELL_CENTER_OFFSET,c=Math.ceil((s.x+a)/e)*e-b.CELL_CENTER_OFFSET,h=Math.floor(s.y/e)*e-b.CELL_CENTER_OFFSET,v=Math.ceil((s.y+o)/e)*e-b.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 w=(y.size??1)*c,E=(y.x+b.CELL_CENTER_OFFSET-i)*c-w/2,I=(y.y+b.CELL_CENTER_OFFSET-s)*c-w/2;r(f,y,E,I,w)}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;onZoom;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.onZoom&&this.onZoom(this.camera.scale),this.onCameraChange();return}if(!this.isDragging||e.touches.length!==1)return;e.preventDefault();let t=e.touches[0],r=t.clientX-this.lastPos.x,i=t.clientY-this.lastPos.y;(r!==0||i!==0)&&(this.canvas.style.cursor=this.config.get().cursor.move||"move",this.shouldPreventClick=!0),this.camera.pan(r,i),this.lastPos={x:t.clientX,y:t.clientY},this.onCameraChange()};handleTouchEnd=e=>{if(e.touches.length>=2&&this.isPinching){this.lastPinchDistance=this.getTouchDistance(e.touches),this.lastPinchCenter=this.getTouchCenter(e.touches);return}if(e.touches.length===1&&this.isPinching){if(this.isPinching=!1,this.config.get().eventHandlers.drag){this.isDragging=!0;let t=e.touches[0];this.lastPos={x:t.clientX,y:t.clientY}}return}this.isDragging=!1,this.isPinching=!1,this.canvas.style.cursor=this.config.get().cursor.default||"default"};getTouchDistance(e){let t=e[1].clientX-e[0].clientX,r=e[1].clientY-e[0].clientY;return Math.sqrt(t*t+r*r)}getTouchCenter(e){return{x:(e[0].clientX+e[1].clientX)/2,y:(e[0].clientY+e[1].clientY)/2}}handleWheel=e=>{if(!this.config.get().eventHandlers.zoom)return;e.preventDefault();let t=this.canvas.getBoundingClientRect();this.camera.zoom(e.clientX,e.clientY,e.deltaY,t),this.onZoom&&this.onZoom(this.camera.scale),this.onCameraChange()}};var 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}get onZoom(){return this.gestures.onZoom}set onZoom(e){this.gestures.onZoom=e}setupEvents(){this.attached||(this.binder.attach(),this.attached=!0,this.config.get().eventHandlers.resize&&this.camera instanceof D&&(this.resizeWatcher=new 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=b.IMAGE_LOAD_RETRY_COUNT){if(this.cache.has(e))return this.cache.get(e);if(this.inflight.has(e))return this.inflight.get(e);let r=new Promise((i,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 Z=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 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,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 j=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 G=class{constructor(e,t,r){this.camera=e;this.viewport=t;this.onAnimationFrame=r}moveAnimationId;resizeAnimationId;animateMoveTo(e,t,r=b.ANIMATION_DURATION_MS,i){if(this.cancelMove(),r<=0){let 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=b.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 q=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}_onZoom;get onZoom(){return this._onZoom}set onZoom(e){this._onZoom=e,this.events.onZoom=e}constructor(e,t,r={x:0,y:0}){this.canvasWrapper=e,this.canvas=e.querySelector("canvas"),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 G(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 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,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)),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 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
|