@canvas-tile-engine/core 0.0.1 → 0.0.2

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
@@ -117,6 +117,7 @@ type onClickCallback = (coords: {
117
117
  snapped: Coords;
118
118
  }) => void;
119
119
  type onHoverCallback = onClickCallback;
120
+ type onZoomCallback = (scale: number) => void;
120
121
  type DrawObject = {
121
122
  x: number;
122
123
  y: number;
@@ -195,6 +196,10 @@ declare class CanvasTileEngine {
195
196
  private _onResize?;
196
197
  get onResize(): (() => void) | undefined;
197
198
  set onResize(cb: (() => void) | undefined);
199
+ private _onZoom?;
200
+ /** Callback: zoom level changes (wheel or pinch) */
201
+ get onZoom(): ((scale: number) => void) | undefined;
202
+ set onZoom(cb: ((scale: number) => void) | undefined);
198
203
  /**
199
204
  * @param canvas Target canvas element.
200
205
  * @param config Initial engine configuration.
@@ -511,4 +516,4 @@ declare const VISIBILITY_BUFFER: {
511
516
  readonly TILE_BUFFER: 1;
512
517
  };
513
518
 
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 };
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 };
package/dist/index.d.ts CHANGED
@@ -117,6 +117,7 @@ type onClickCallback = (coords: {
117
117
  snapped: Coords;
118
118
  }) => void;
119
119
  type onHoverCallback = onClickCallback;
120
+ type onZoomCallback = (scale: number) => void;
120
121
  type DrawObject = {
121
122
  x: number;
122
123
  y: number;
@@ -195,6 +196,10 @@ declare class CanvasTileEngine {
195
196
  private _onResize?;
196
197
  get onResize(): (() => void) | undefined;
197
198
  set onResize(cb: (() => void) | undefined);
199
+ private _onZoom?;
200
+ /** Callback: zoom level changes (wheel or pinch) */
201
+ get onZoom(): ((scale: number) => void) | undefined;
202
+ set onZoom(cb: ((scale: number) => void) | undefined);
198
203
  /**
199
204
  * @param canvas Target canvas element.
200
205
  * @param config Initial engine configuration.
@@ -511,4 +516,4 @@ declare const VISIBILITY_BUFFER: {
511
516
  readonly TILE_BUFFER: 1;
512
517
  };
513
518
 
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 };
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 };
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 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});
2
2
  //# sourceMappingURL=index.js.map