open-plant 1.2.2 → 1.2.4

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/README.md CHANGED
@@ -3,7 +3,8 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- WebGL2 기반 고성능 WSI(Whole Slide Image) 뷰어 라이브러리
6
+ WebGL2 기반 고성능 WSI(Whole Slide Image) 뷰어 라이브러리<br/>
7
+ 고사양 PC가 아니어도, 고작, 고~오작 iPhone 15에서 수백만 cell을 부드럽게 렌더링
7
8
  </p>
8
9
 
9
10
  <p align="center">
@@ -17,15 +18,24 @@
17
18
 
18
19
  ---
19
20
 
20
- <h3 align="center">10,000,000 cells · ~300 MB RAM · 60 fps</h3>
21
+ <h3 align="center">10,000,000 cells · ~300 MB RAM · 60 fps · iPhone 15 ready</h3>
21
22
 
22
23
  https://github.com/user-attachments/assets/5a6b5deb-7442-4389-908f-bf2c69348824
23
24
 
25
+ > 핵심 포지셔닝: Open Plant는 데스크톱 전용 엔진이 아닙니다. iPhone 15급 모바일 환경에서도 수백만 cell pan/zoom 워크로드를 체감 렉 없이 다루는 것을 목표로 설계했습니다.
26
+
24
27
  ## Why Open Plant
25
28
 
26
29
  범용 시각화 프레임워크 위에 병리 뷰어를 올리면 추상화 비용을 그대로 떠안게 됩니다.
27
30
  Open Plant는 WSI 렌더링 **한 가지만** 하도록 설계되었고, 그래서 아래가 가능합니다.
28
31
 
32
+ ### 모바일 실전 성능 (iPhone 15)
33
+
34
+ Open Plant는 “고사양 PC에서만 빠른 뷰어”가 아니라, iPhone 15 같은 일반 플래그십 모바일에서도
35
+ 수백만 cell을 pan/zoom하면서 작업 가능한 성능을 목표로 최적화되어 있습니다.
36
+ 타일 스케줄러 + fallback 렌더링 + TypedArray 포인트 파이프라인 덕분에, 실제 사용 시에도 뷰 전환 안정성을 유지합니다.
37
+ (`실효 성능은 데이터 밀도/타일 서버 응답/네트워크 상태에 따라 달라질 수 있습니다.`)
38
+
29
39
  ### 포인트 1개당 10바이트
30
40
 
31
41
  범용 라이브러리는 포인트마다 인스턴스 버퍼에 position + RGBA를 넣어 **20바이트 이상** 씁니다.
@@ -65,6 +75,7 @@ draw mode에 진입하면 `setPointerCapture`로 입력을 독점한 뒤 `intera
65
75
  | **WebGL2 타일 렌더링** | 멀티 티어 타일 피라미드, LRU 캐시(320장), 저해상도 fallback 렌더링 |
66
76
  | **회전 인터랙션** | `WsiViewState.rotationDeg`, `Ctrl/Cmd + drag` 회전, `resetRotation` 경로 |
67
77
  | **포인트 오버레이** | WebGL2 `gl.POINTS`로 수십, 수백만 개 포인트를 팔레트 텍스처 기반 컬러링. 파싱된 TypedArray만 입력 |
78
+ | **모바일 타겟 성능** | iPhone 15급 환경에서 수백만 cell 워크로드를 전제로 pan/zoom 응답성을 유지하도록 설계 |
68
79
  | **드로잉 / ROI 도구** | Freehand · Rectangle · Circular + Stamp(사각형/원, mm² 지정) |
69
80
  | **고정 픽셀 스탬프** | `stamp-rectangle-4096px` + `stampOptions.rectanglePixelSize` |
70
81
  | **ROI 포인트 클리핑** | `clipMode`: `sync` / `worker` / `hybrid-webgpu` (실험) |
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var mn=Object.defineProperty;var gn=(t,e,n)=>e in t?mn(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var d=(t,e,n)=>gn(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const me=require("react/jsx-runtime"),f=require("react");var nt=typeof document<"u"?document.currentScript:null;function Rt(t,e,n){const r=t.createShader(e);if(!r)throw new Error("Failed to create shader.");if(t.shaderSource(r,n),t.compileShader(r),!t.getShaderParameter(r,t.COMPILE_STATUS)){const o=t.getShaderInfoLog(r)??"unknown shader error";throw t.deleteShader(r),new Error(o)}return r}function pn(t,e,n){const r=Rt(t,t.VERTEX_SHADER,e),i=Rt(t,t.FRAGMENT_SHADER,n),o=t.createProgram();if(!o)throw t.deleteShader(r),t.deleteShader(i),new Error("Failed to create program.");if(t.attachShader(o,r),t.attachShader(o,i),t.linkProgram(o),t.deleteShader(r),t.deleteShader(i),!t.getProgramParameter(o,t.LINK_STATUS)){const a=t.getProgramInfoLog(o)??"unknown link error";throw t.deleteProgram(o),new Error(a)}return o}function rt(t,e,n){const r=t.getUniformLocation(e,n);if(!r)throw new Error(`Failed to get uniform location: ${n}`);return r}function bn(t){const e=t.getContext("webgl2",{alpha:!1,antialias:!1,depth:!1,stencil:!1,preserveDrawingBuffer:!1,powerPreference:"high-performance"});if(!e)throw new Error("WebGL2 is not available.");return e}let wn=class{constructor(){d(this,"viewportWidth",1);d(this,"viewportHeight",1);d(this,"viewState",{offsetX:0,offsetY:0,zoom:1})}setViewport(e,n){this.viewportWidth=Math.max(1,e),this.viewportHeight=Math.max(1,n)}getViewportSize(){return{width:this.viewportWidth,height:this.viewportHeight}}setViewState(e){e.offsetX!==void 0&&(this.viewState.offsetX=e.offsetX),e.offsetY!==void 0&&(this.viewState.offsetY=e.offsetY),e.zoom!==void 0&&(this.viewState.zoom=Math.max(1e-4,e.zoom))}getViewState(){return{...this.viewState}}getMatrix(){const e=this.viewportWidth/this.viewState.zoom,n=this.viewportHeight/this.viewState.zoom,r=2/e,i=-2/n,o=-1-this.viewState.offsetX*r,s=1-this.viewState.offsetY*i;return new Float32Array([r,0,0,0,i,0,o,s,1])}};const yn=`#version 300 es
1
+ "use strict";var wn=Object.defineProperty;var yn=(t,e,n)=>e in t?wn(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var d=(t,e,n)=>yn(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const me=require("react/jsx-runtime"),f=require("react");var nt=typeof document<"u"?document.currentScript:null;function At(t,e,n){const r=t.createShader(e);if(!r)throw new Error("Failed to create shader.");if(t.shaderSource(r,n),t.compileShader(r),!t.getShaderParameter(r,t.COMPILE_STATUS)){const o=t.getShaderInfoLog(r)??"unknown shader error";throw t.deleteShader(r),new Error(o)}return r}function xn(t,e,n){const r=At(t,t.VERTEX_SHADER,e),i=At(t,t.FRAGMENT_SHADER,n),o=t.createProgram();if(!o)throw t.deleteShader(r),t.deleteShader(i),new Error("Failed to create program.");if(t.attachShader(o,r),t.attachShader(o,i),t.linkProgram(o),t.deleteShader(r),t.deleteShader(i),!t.getProgramParameter(o,t.LINK_STATUS)){const a=t.getProgramInfoLog(o)??"unknown link error";throw t.deleteProgram(o),new Error(a)}return o}function rt(t,e,n){const r=t.getUniformLocation(e,n);if(!r)throw new Error(`Failed to get uniform location: ${n}`);return r}function Tn(t){const e=t.getContext("webgl2",{alpha:!1,antialias:!1,depth:!1,stencil:!1,preserveDrawingBuffer:!1,powerPreference:"high-performance"});if(!e)throw new Error("WebGL2 is not available.");return e}let vn=class{constructor(){d(this,"viewportWidth",1);d(this,"viewportHeight",1);d(this,"viewState",{offsetX:0,offsetY:0,zoom:1})}setViewport(e,n){this.viewportWidth=Math.max(1,e),this.viewportHeight=Math.max(1,n)}getViewportSize(){return{width:this.viewportWidth,height:this.viewportHeight}}setViewState(e){e.offsetX!==void 0&&(this.viewState.offsetX=e.offsetX),e.offsetY!==void 0&&(this.viewState.offsetY=e.offsetY),e.zoom!==void 0&&(this.viewState.zoom=Math.max(1e-4,e.zoom))}getViewState(){return{...this.viewState}}getMatrix(){const e=this.viewportWidth/this.viewState.zoom,n=this.viewportHeight/this.viewState.zoom,r=2/e,i=-2/n,o=-1-this.viewState.offsetX*r,s=1-this.viewState.offsetY*i;return new Float32Array([r,0,0,0,i,0,o,s,1])}};const Cn=`#version 300 es
2
2
  precision highp float;
3
3
 
4
4
  in vec2 aUnit;
@@ -18,7 +18,7 @@ void main() {
18
18
  gl_Position = vec4(clip.xy, 0.0, 1.0);
19
19
  vUv = aUv;
20
20
  }
21
- `,xn=`#version 300 es
21
+ `,Rn=`#version 300 es
22
22
  precision highp float;
23
23
 
24
24
  in vec2 vUv;
@@ -29,7 +29,7 @@ out vec4 outColor;
29
29
  void main() {
30
30
  outColor = texture(uTexture, vUv);
31
31
  }
32
- `;class Yt{constructor(e){d(this,"canvas");d(this,"gl");d(this,"camera",new wn);d(this,"imageWidth");d(this,"imageHeight");d(this,"clearColor");d(this,"program");d(this,"vao");d(this,"quadBuffer");d(this,"uCameraLocation");d(this,"uBoundsLocation");d(this,"uTextureLocation");d(this,"resizeObserver");d(this,"tiles",[]);d(this,"frameId",null);d(this,"loadVersion",0);d(this,"destroyed",!1);d(this,"fitted",!1);d(this,"controlledViewState",!1);this.canvas=e.canvas,this.imageWidth=Math.max(1,e.imageWidth),this.imageHeight=Math.max(1,e.imageHeight),this.clearColor=e.clearColor??[.03,.05,.08,1],this.gl=bn(this.canvas),this.program=pn(this.gl,yn,xn);const n=this.gl.createVertexArray(),r=this.gl.createBuffer();if(!n||!r)throw new Error("Failed to create WebGL buffers.");this.vao=n,this.quadBuffer=r,this.gl.bindVertexArray(this.vao),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.quadBuffer);const i=new Float32Array([0,0,0,0,1,0,1,0,0,1,0,1,1,1,1,1]);this.gl.bufferData(this.gl.ARRAY_BUFFER,i,this.gl.STATIC_DRAW);const o=this.gl.getAttribLocation(this.program,"aUnit"),s=this.gl.getAttribLocation(this.program,"aUv");if(o<0||s<0)throw new Error("Failed to get attribute locations.");const a=4*Float32Array.BYTES_PER_ELEMENT;this.gl.enableVertexAttribArray(o),this.gl.vertexAttribPointer(o,2,this.gl.FLOAT,!1,a,0),this.gl.enableVertexAttribArray(s),this.gl.vertexAttribPointer(s,2,this.gl.FLOAT,!1,a,2*Float32Array.BYTES_PER_ELEMENT),this.gl.bindVertexArray(null),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null),this.uCameraLocation=rt(this.gl,this.program,"uCamera"),this.uBoundsLocation=rt(this.gl,this.program,"uBounds"),this.uTextureLocation=rt(this.gl,this.program,"uTexture"),e.initialViewState&&(this.controlledViewState=!0,this.camera.setViewState(e.initialViewState)),this.resizeObserver=new ResizeObserver(()=>{this.resize()}),this.resizeObserver.observe(this.canvas),this.resize()}async setTiles(e){if(this.destroyed)return;const n=++this.loadVersion,r=await Promise.all(e.map(async i=>await this.loadTile(i,n)));if(this.destroyed||n!==this.loadVersion){for(const i of r)i&&this.gl.deleteTexture(i.texture);return}this.disposeTiles(this.tiles),this.tiles=r.filter(i=>i!==null),this.requestRender()}setViewState(e){this.controlledViewState=!0,this.camera.setViewState(e),this.requestRender()}getViewState(){return this.camera.getViewState()}destroy(){this.destroyed||(this.destroyed=!0,this.loadVersion+=1,this.frameId!==null&&(cancelAnimationFrame(this.frameId),this.frameId=null),this.resizeObserver.disconnect(),this.disposeTiles(this.tiles),this.tiles=[],this.gl.deleteBuffer(this.quadBuffer),this.gl.deleteVertexArray(this.vao),this.gl.deleteProgram(this.program))}async loadTile(e,n){try{const r=await fetch(e.url);if(!r.ok)throw new Error(`Tile fetch failed: ${r.status} ${r.statusText}`);const i=await r.blob(),o=await createImageBitmap(i);if(this.destroyed||n!==this.loadVersion)return o.close(),null;const s=this.gl.createTexture();if(!s)throw o.close(),new Error("Failed to create tile texture.");return this.gl.bindTexture(this.gl.TEXTURE_2D,s),this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,1),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,o),this.gl.bindTexture(this.gl.TEXTURE_2D,null),o.close(),{id:e.id,bounds:e.bounds,texture:s}}catch(r){return console.error(`[M1TileRenderer] tile load failed: ${e.id}`,r),null}}resize(){if(this.destroyed)return;const e=this.canvas.getBoundingClientRect(),n=Math.max(1,e.width||this.canvas.clientWidth||1),r=Math.max(1,e.height||this.canvas.clientHeight||1),i=Math.max(1,window.devicePixelRatio||1),o=Math.max(1,Math.round(n*i)),s=Math.max(1,Math.round(r*i));(this.canvas.width!==o||this.canvas.height!==s)&&(this.canvas.width=o,this.canvas.height=s),this.camera.setViewport(n,r),this.gl.viewport(0,0,this.canvas.width,this.canvas.height),!this.fitted&&!this.controlledViewState&&(this.fitToImage(),this.fitted=!0),this.requestRender()}fitToImage(){const e=this.camera.getViewportSize(),n=Math.min(e.width/this.imageWidth,e.height/this.imageHeight),r=Number.isFinite(n)&&n>0?n:1,i=e.width/r,o=e.height/r,s=(this.imageWidth-i)*.5,a=(this.imageHeight-o)*.5;this.camera.setViewState({zoom:r,offsetX:s,offsetY:a})}requestRender(){this.frameId!==null||this.destroyed||(this.frameId=requestAnimationFrame(()=>{this.frameId=null,this.render()}))}render(){if(!this.destroyed){this.gl.clearColor(this.clearColor[0],this.clearColor[1],this.clearColor[2],this.clearColor[3]),this.gl.clear(this.gl.COLOR_BUFFER_BIT),this.gl.useProgram(this.program),this.gl.bindVertexArray(this.vao),this.gl.uniformMatrix3fv(this.uCameraLocation,!1,this.camera.getMatrix()),this.gl.uniform1i(this.uTextureLocation,0);for(const e of this.tiles)this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,e.texture),this.gl.uniform4f(this.uBoundsLocation,e.bounds[0],e.bounds[1],e.bounds[2],e.bounds[3]),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4);this.gl.bindTexture(this.gl.TEXTURE_2D,null),this.gl.bindVertexArray(null)}}disposeTiles(e){for(const n of e)this.gl.deleteTexture(n.texture)}}const pt=[160,160,160,255];function $(t,e,n){return Math.max(e,Math.min(n,t))}function bt(t,e,n){const r=Number(t),i=Number(e),o=Number(n);return!Number.isFinite(r)||r<=0?1:!Number.isFinite(i)||!Number.isFinite(o)?r:Math.pow(2,i-o)*r}function Tn(t,e,n){let i=100*bt(t,e,n);if(Number(t)){let o="μm";return i>1e3&&(i/=1e3,o="mm"),`${i.toPrecision(3)} ${o}`}return`${Math.round(i*1e3)/1e3} pixels`}function vn(t,e){return!t&&!e?!0:!t||!e?!1:Math.abs((t.zoom??0)-(e.zoom??0))<1e-6&&Math.abs((t.offsetX??0)-(e.offsetX??0))<1e-6&&Math.abs((t.offsetY??0)-(e.offsetY??0))<1e-6&&Math.abs((t.rotationDeg??0)-(e.rotationDeg??0))<1e-6}function Mn(t){const e=String(t??"").trim();if(!e)return"";if(/^bearer\s+/i.test(e)){const n=e.replace(/^bearer\s+/i,"").trim();return n?`Bearer ${n}`:""}return`Bearer ${e}`}function Xt(t){const n=String(t??"").trim().match(/^#?([0-9a-fA-F]{6})$/);if(!n)return[...pt];const r=Number.parseInt(n[1],16);return[r>>16&255,r>>8&255,r&255,255]}function En(t){const e=[[...pt]],n=new Map;for(const i of t??[]){const o=String(i?.termId??"");!o||n.has(o)||(n.set(o,e.length),e.push(Xt(i?.termColor)))}const r=new Uint8Array(e.length*4);for(let i=0;i<e.length;i+=1)r[i*4]=e[i][0],r[i*4+1]=e[i][1],r[i*4+2]=e[i][2],r[i*4+3]=e[i][3];return{colors:r,termToPaletteIndex:n}}function At(t,e,n){const r=t.createShader(t.VERTEX_SHADER),i=t.createShader(t.FRAGMENT_SHADER);if(!r||!i)throw new Error("Shader allocation failed");if(t.shaderSource(r,e),t.compileShader(r),!t.getShaderParameter(r,t.COMPILE_STATUS))throw new Error(t.getShaderInfoLog(r)||"vertex compile failed");if(t.shaderSource(i,n),t.compileShader(i),!t.getShaderParameter(i,t.COMPILE_STATUS))throw new Error(t.getShaderInfoLog(i)||"fragment compile failed");const o=t.createProgram();if(!o)throw new Error("Program allocation failed");if(t.attachShader(o,r),t.attachShader(o,i),t.linkProgram(o),t.deleteShader(r),t.deleteShader(i),!t.getProgramParameter(o,t.LINK_STATUS))throw new Error(t.getProgramInfoLog(o)||"program link failed");return o}const Cn="rgba(255, 77, 79, 0.16)",Pn=3,Sn=2,Vt=96,Rn=1,It=[],lt=[],_t=1e3,Ot=2,Gt=2,An=4096,In=.2,Ae={color:"#ff4d4f",width:2,lineJoin:"round",lineCap:"round",shadowColor:"rgba(0, 0, 0, 0)",shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0},_n={color:"#4cc9f0",width:2,lineDash:[10,8],lineJoin:"round",lineCap:"round",shadowColor:"rgba(0, 0, 0, 0)",shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0},we={fontFamily:"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",fontSize:12,fontWeight:500,textColor:"#ffffff",backgroundColor:"rgba(8, 14, 22, 0.88)",borderColor:"rgba(255, 77, 79, 0.85)",borderWidth:1,paddingX:6,paddingY:4,offsetY:10,borderRadius:3};function Qe(t,e,n){return Math.max(e,Math.min(n,t))}function qe(t){return t==="stamp-rectangle"||t==="stamp-circle"||t==="stamp-rectangle-4096px"||t==="stamp-rectangle-2mm2"||t==="stamp-circle-2mm2"||t==="stamp-circle-hpf-0.2mm2"}function it(t,e){return typeof t!="number"||!Number.isFinite(t)||t<=0?e:t}function Bn(t){return{rectangleAreaMm2:it(t?.rectangleAreaMm2,Ot),circleAreaMm2:it(t?.circleAreaMm2,Gt),rectanglePixelSize:it(t?.rectanglePixelSize,An)}}function Un(t){return t*_t*_t}function Bt(t,e){return!t||!Number.isFinite(e)||e<=0?[]:ye([[t[0]-e,t[1]-e],[t[0]+e,t[1]-e],[t[0]+e,t[1]+e],[t[0]-e,t[1]+e]])}function Fn(t,e,n=Vt){if(!t||!Number.isFinite(e)||e<=0)return[];const r=[];for(let i=0;i<=n;i+=1){const o=i/n*Math.PI*2;r.push([t[0]+Math.cos(o)*e,t[1]+Math.sin(o)*e])}return ye(r)}function ye(t){if(!Array.isArray(t)||t.length<3)return[];const e=t.map(([i,o])=>[i,o]),n=e[0],r=e[e.length-1];return!n||!r?[]:((n[0]!==r[0]||n[1]!==r[1])&&e.push([n[0],n[1]]),e)}function ht(t,e){return!t||!e?[]:ye([[t[0],t[1]],[e[0],t[1]],[e[0],e[1]],[t[0],e[1]]])}function ft(t,e,n=Vt){if(!t||!e)return[];const r=(t[0]+e[0])*.5,i=(t[1]+e[1])*.5,o=Math.hypot(e[0]-t[0],e[1]-t[1])*.5;if(o<1)return[];const s=[];for(let a=0;a<=n;a+=1){const u=a/n*Math.PI*2;s.push([r+Math.cos(u)*o,i+Math.sin(u)*o])}return ye(s)}function dt(t){if(!Array.isArray(t)||t.length<4)return 0;let e=0;for(let n=0;n<t.length-1;n+=1){const r=t[n],i=t[n+1];e+=r[0]*i[1]-i[0]*r[1]}return Math.abs(e*.5)}function Ut(t){if(!Array.isArray(t)||t.length===0)return[0,0,0,0];let e=1/0,n=1/0,r=-1/0,i=-1/0;for(const[o,s]of t)o<e&&(e=o),o>r&&(r=o),s<n&&(n=s),s>i&&(i=s);return[e,n,r,i]}function Ft(t){return Array.isArray(t)&&t.length>=4&&dt(t)>Rn}function ke(t,e,n,r=!1,i=!1){if(e.length!==0){t.beginPath(),t.moveTo(e[0][0],e[0][1]);for(let o=1;o<e.length;o+=1)t.lineTo(e[o][0],e[o][1]);r&&t.closePath(),i&&r&&(t.fillStyle=Cn,t.fill()),t.strokeStyle=n.color,t.lineWidth=n.width,t.lineJoin=n.lineJoin,t.lineCap=n.lineCap,t.shadowColor=n.shadowColor,t.shadowBlur=n.shadowBlur,t.shadowOffsetX=n.shadowOffsetX,t.shadowOffsetY=n.shadowOffsetY,t.setLineDash(n.lineDash),t.stroke(),t.setLineDash(lt),t.shadowColor="rgba(0, 0, 0, 0)",t.shadowBlur=0,t.shadowOffsetX=0,t.shadowOffsetY=0}}function qt(t){const e=Array.isArray(t?.lineDash)?t.lineDash.filter(s=>Number.isFinite(s)&&s>=0):lt,n=typeof t?.width=="number"&&Number.isFinite(t.width)?Math.max(0,t.width):Ae.width,r=typeof t?.shadowBlur=="number"&&Number.isFinite(t.shadowBlur)?Math.max(0,t.shadowBlur):Ae.shadowBlur,i=typeof t?.shadowOffsetX=="number"&&Number.isFinite(t.shadowOffsetX)?t.shadowOffsetX:Ae.shadowOffsetX,o=typeof t?.shadowOffsetY=="number"&&Number.isFinite(t.shadowOffsetY)?t.shadowOffsetY:Ae.shadowOffsetY;return{color:t?.color||Ae.color,width:n,lineDash:e.length?e:lt,lineJoin:t?.lineJoin||Ae.lineJoin,lineCap:t?.lineCap||Ae.lineCap,shadowColor:t?.shadowColor||Ae.shadowColor,shadowBlur:r,shadowOffsetX:i,shadowOffsetY:o}}function We(t,e){return e?qt({color:e.color??t.color,width:e.width??t.width,lineDash:e.lineDash??t.lineDash,lineJoin:e.lineJoin??t.lineJoin,lineCap:e.lineCap??t.lineCap,shadowColor:e.shadowColor??t.shadowColor,shadowBlur:e.shadowBlur??t.shadowBlur,shadowOffsetX:e.shadowOffsetX??t.shadowOffsetX,shadowOffsetY:e.shadowOffsetY??t.shadowOffsetY}):t}function Lt(t,e){return t==null||e===null||e===void 0?!1:String(t)===String(e)}function Ln(t){const e=typeof t?.paddingX=="number"&&Number.isFinite(t.paddingX)?Math.max(0,t.paddingX):we.paddingX,n=typeof t?.paddingY=="number"&&Number.isFinite(t.paddingY)?Math.max(0,t.paddingY):we.paddingY,r=typeof t?.fontSize=="number"&&Number.isFinite(t.fontSize)?Math.max(8,t.fontSize):we.fontSize,i=typeof t?.borderWidth=="number"&&Number.isFinite(t.borderWidth)?Math.max(0,t.borderWidth):we.borderWidth,o=typeof t?.offsetY=="number"&&Number.isFinite(t.offsetY)?t.offsetY:we.offsetY,s=typeof t?.borderRadius=="number"&&Number.isFinite(t.borderRadius)?Math.max(0,t.borderRadius):we.borderRadius;return{fontFamily:t?.fontFamily||we.fontFamily,fontSize:r,fontWeight:t?.fontWeight||we.fontWeight,textColor:t?.textColor||we.textColor,backgroundColor:t?.backgroundColor||we.backgroundColor,borderColor:t?.borderColor||we.borderColor,borderWidth:i,paddingX:e,paddingY:n,offsetY:o,borderRadius:s}}function Dn(t,e,n,r,i,o){const s=Math.max(0,Math.min(o,r*.5,i*.5));t.beginPath(),t.moveTo(e+s,n),t.lineTo(e+r-s,n),t.quadraticCurveTo(e+r,n,e+r,n+s),t.lineTo(e+r,n+i-s),t.quadraticCurveTo(e+r,n+i,e+r-s,n+i),t.lineTo(e+s,n+i),t.quadraticCurveTo(e,n+i,e,n+i-s),t.lineTo(e,n+s),t.quadraticCurveTo(e,n,e+s,n),t.closePath()}function kn(t){if(!t.length)return null;let e=1/0;for(const i of t)i[1]<e&&(e=i[1]);if(!Number.isFinite(e))return null;let n=1/0,r=-1/0;for(const i of t)Math.abs(i[1]-e)>.5||(i[0]<n&&(n=i[0]),i[0]>r&&(r=i[0]));return!Number.isFinite(n)||!Number.isFinite(r)?null:[(n+r)*.5,e]}function Nn(t,e,n,r,i,o){const s=e.trim();if(!s)return;t.save(),t.font=`${o.fontWeight} ${o.fontSize}px ${o.fontFamily}`,t.textAlign="center",t.textBaseline="middle";const u=t.measureText(s).width+o.paddingX*2,l=o.fontSize+o.paddingY*2,h=Qe(n[0],u*.5+1,r-u*.5-1),w=Qe(n[1]-o.offsetY,l*.5+1,i-l*.5-1),m=h-u*.5,g=w-l*.5;t.fillStyle=o.backgroundColor,t.strokeStyle=o.borderColor,t.lineWidth=o.borderWidth,Dn(t,m,g,u,l,o.borderRadius),t.fill(),o.borderWidth>0&&t.stroke(),t.fillStyle=o.textColor,t.fillText(s,h,w+.5),t.restore()}function ot(t,e,n){return[Qe(t[0],0,e),Qe(t[1],0,n)]}function st(t){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),n=Number(t[1]);return!Number.isFinite(e)||!Number.isFinite(n)?null:[e,n]}function $t({tool:t,imageWidth:e,imageHeight:n,imageMpp:r,imageZoom:i,stampOptions:o,projectorRef:s,onDrawComplete:a,onPatchComplete:u,enabled:l,viewStateSignal:h,persistedRegions:w,patchRegions:m,persistedPolygons:g,regionStrokeStyle:x,regionStrokeHoverStyle:y,regionStrokeActiveStyle:M,patchStrokeStyle:P,resolveRegionStrokeStyle:D,overlayShapes:A,hoveredRegionId:W=null,activeRegionId:be=null,regionLabelStyle:re,invalidateRef:Y,className:Ee,style:he}){const ae=f.useRef(null),Ce=f.useRef(!1),Z=f.useRef(t),ee=f.useRef({isDrawing:!1,pointerId:null,start:null,current:null,points:[],stampCenter:null}),X=l??t!=="cursor",ue=f.useMemo(()=>w&&w.length>0?w:!g||g.length===0?It:g.map((c,p)=>({id:p,coordinates:c})),[w,g]),Pe=f.useMemo(()=>m??It,[m]),ie=f.useMemo(()=>qt(x),[x]),T=f.useMemo(()=>We(ie,y),[ie,y]),v=f.useMemo(()=>We(ie,M),[ie,M]),S=f.useMemo(()=>We(_n,P),[P]),N=f.useMemo(()=>Ln(re),[re]),V=f.useMemo(()=>Bn(o),[o]),fe=f.useMemo(()=>({position:"absolute",inset:0,zIndex:2,width:"100%",height:"100%",display:"block",touchAction:"none",pointerEvents:X?"auto":"none",cursor:X?"crosshair":"default",...he}),[X,he]),K=f.useCallback(()=>{const c=ae.current;if(!c)return;const p=c.getBoundingClientRect(),E=Math.max(1,window.devicePixelRatio||1),B=Math.max(1,Math.round(p.width*E)),R=Math.max(1,Math.round(p.height*E));(c.width!==B||c.height!==R)&&(c.width=B,c.height=R)},[]),G=f.useCallback(c=>{const p=s.current;if(!p||c.length===0)return[];const E=new Array(c.length);for(let B=0;B<c.length;B+=1){const R=st(p.worldToScreen(c[B][0],c[B][1]));if(!R)return[];E[B]=R}return E},[s]),J=f.useCallback(c=>{if(!Number.isFinite(c)||c<=0)return 0;const p=typeof r=="number"&&Number.isFinite(r)&&r>0?r:1,E=typeof i=="number"&&Number.isFinite(i)?i:0,B=s.current?.getViewState?.().zoom,R=typeof B=="number"&&Number.isFinite(B)&&B>0?B:1,I=E+Math.log2(R),_=Math.max(1e-9,bt(p,E,I));return c/_/R},[r,i,s]),oe=f.useCallback((c,p)=>{if(!p)return[];let E=0;if(c==="stamp-rectangle-4096px"){const I=V.rectanglePixelSize*.5;return Bt(p,I).map(O=>ot(O,e,n))}if(c==="stamp-rectangle"||c==="stamp-rectangle-2mm2"?E=c==="stamp-rectangle-2mm2"?Ot:V.rectangleAreaMm2:(c==="stamp-circle"||c==="stamp-circle-2mm2"||c==="stamp-circle-hpf-0.2mm2")&&(E=c==="stamp-circle-hpf-0.2mm2"?In:c==="stamp-circle-2mm2"?Gt:V.circleAreaMm2),!Number.isFinite(E)||E<=0)return[];const B=Un(E);let R=[];if(c==="stamp-rectangle"||c==="stamp-rectangle-2mm2"){const I=J(Math.sqrt(B)*.5);R=Bt(p,I)}else if(c==="stamp-circle"||c==="stamp-circle-2mm2"||c==="stamp-circle-hpf-0.2mm2"){const I=J(Math.sqrt(B/Math.PI));R=Fn(p,I)}return R.length?R.map(I=>ot(I,e,n)):[]},[J,e,n,V]),se=f.useCallback(()=>{const c=ee.current;return qe(t)?oe(t,c.stampCenter):c.isDrawing?t==="freehand"?c.points:t==="rectangle"?ht(c.start,c.current):t==="circular"?ft(c.start,c.current):[]:[]},[t,oe]),F=f.useCallback(()=>{K();const c=ae.current;if(!c)return;const p=c.getContext("2d");if(!p)return;const E=Math.max(1,window.devicePixelRatio||1),B=c.width/E,R=c.height/E;if(p.setTransform(1,0,0,1,0,0),p.clearRect(0,0,c.width,c.height),p.setTransform(E,0,0,E,0,0),ue.length>0)for(let I=0;I<ue.length;I+=1){const _=ue[I],O=_?.coordinates;if(!O||O.length<3)continue;const te=ye(O),ne=G(te);if(ne.length>=4){const ve=_.id??I,Se=Lt(be,ve)?"active":Lt(W,ve)?"hover":"default";let ze=Se==="active"?v:Se==="hover"?T:ie;if(D){const et=D({region:_,regionId:ve,regionIndex:I,state:Se});ze=We(ze,et||void 0)}ke(p,ne,ze,!0,!1)}}if(Pe.length>0)for(let I=0;I<Pe.length;I+=1){const O=Pe[I]?.coordinates;if(!O||O.length<3)continue;const te=ye(O),ne=G(te);ne.length<4||ke(p,ne,S,!0,!1)}if(Array.isArray(A)&&A.length>0)for(let I=0;I<A.length;I+=1){const _=A[I];if(!_?.coordinates?.length)continue;const O=_.closed??!1,te=O?ye(_.coordinates):_.coordinates,ne=G(te);if(ne.length<2)continue;const ve=We(ie,_.strokeStyle);ke(p,ne,ve,O,_.fill??!1)}if(X){const I=se();if(I.length>0)if(t==="freehand"){const _=G(I);_.length>=2&&ke(p,_,ie,!1,!1),_.length>=3&&ke(p,G(ye(I)),ie,!0,!0)}else{const _=G(I);_.length>=4&&ke(p,_,ie,!0,!0)}}if(ue.length>0)for(const I of ue){if(!I.label)continue;const _=I?.coordinates;if(!_||_.length<3)continue;const O=ye(_),te=kn(O);if(!te)continue;const ne=st(s.current?.worldToScreen(te[0],te[1])??[]);ne&&Nn(p,I.label,ne,B,R,N)}},[X,t,se,K,G,s,ue,A,W,be,ie,T,v,Pe,S,D,N]),C=f.useCallback(()=>{Ce.current||(Ce.current=!0,requestAnimationFrame(()=>{Ce.current=!1,F()}))},[F]),z=f.useCallback(()=>{const c=ee.current,p=ae.current;if(p&&c.pointerId!==null&&p.hasPointerCapture(c.pointerId))try{p.releasePointerCapture(c.pointerId)}catch{}c.isDrawing=!1,c.pointerId=null,c.start=null,c.current=null,c.points=[],c.stampCenter=null},[]),q=f.useCallback(c=>{const p=s.current;if(!p||e<=0||n<=0)return null;const E=st(p.screenToWorld(c.clientX,c.clientY));return E?ot(E,e,n):null},[s,e,n]),j=f.useCallback(()=>{const c=ee.current;if(!c.isDrawing){z(),C();return}let p=[];t==="freehand"?c.points.length>=Pn&&(p=ye(c.points)):t==="rectangle"?p=ht(c.start,c.current):t==="circular"&&(p=ft(c.start,c.current)),(t==="freehand"||t==="rectangle"||t==="circular")&&Ft(p)&&a&&a({tool:t,intent:"roi",coordinates:p,bbox:Ut(p),areaPx:dt(p)}),z(),C()},[t,a,z,C]),ce=f.useCallback((c,p)=>{const E=oe(c,p);if(!Ft(E))return;const B=c==="stamp-rectangle-4096px"?"patch":"roi",R={tool:c,intent:B,coordinates:E,bbox:Ut(E),areaPx:dt(E)};a?.(R),B==="patch"&&u&&u(R)},[oe,a,u]),Be=f.useCallback(c=>{if(!X||t==="cursor"||c.button!==0)return;const p=q(c);if(!p)return;if(c.preventDefault(),c.stopPropagation(),qe(t)){const R=ee.current;R.stampCenter=p,ce(t,p),C();return}const E=ae.current;E&&E.setPointerCapture(c.pointerId);const B=ee.current;B.isDrawing=!0,B.pointerId=c.pointerId,B.start=p,B.current=p,B.points=t==="freehand"?[p]:[],C()},[X,t,q,ce,C]),xe=f.useCallback(c=>{if(!X||t==="cursor")return;const p=q(c);if(!p)return;if(qe(t)){const B=ee.current;B.stampCenter=p,c.preventDefault(),c.stopPropagation(),C();return}const E=ee.current;if(!(!E.isDrawing||E.pointerId!==c.pointerId)){if(c.preventDefault(),c.stopPropagation(),t==="freehand"){const B=s.current,R=Math.max(1e-6,B?.getViewState?.().zoom??1),I=Sn/R,_=I*I,O=E.points[E.points.length-1];if(!O)E.points.push(p);else{const te=p[0]-O[0],ne=p[1]-O[1];te*te+ne*ne>=_&&E.points.push(p)}}else E.current=p;C()}},[X,t,q,C,s]),Te=f.useCallback(c=>{const p=ee.current;if(!p.isDrawing||p.pointerId!==c.pointerId)return;c.preventDefault(),c.stopPropagation();const E=ae.current;if(E&&E.hasPointerCapture(c.pointerId))try{E.releasePointerCapture(c.pointerId)}catch{}j()},[j]),pe=f.useCallback(()=>{if(!qe(t))return;const c=ee.current;c.stampCenter&&(c.stampCenter=null,C())},[t,C]);return f.useEffect(()=>{K(),C();const c=ae.current;if(!c)return;const p=new ResizeObserver(()=>{K(),C()});return p.observe(c),()=>{p.disconnect()}},[K,C]),f.useEffect(()=>{X||z(),C()},[X,C,z]),f.useEffect(()=>{Z.current!==t&&(Z.current=t,z(),C())},[t,z,C]),f.useEffect(()=>{C()},[h,ue,A,C]),f.useEffect(()=>{if(Y)return Y.current=C,()=>{Y.current===C&&(Y.current=null)}},[Y,C]),f.useEffect(()=>{if(!X)return;const c=p=>{p.key==="Escape"&&(z(),C())};return window.addEventListener("keydown",c),()=>{window.removeEventListener("keydown",c)}},[X,z,C]),me.jsx("canvas",{ref:ae,className:Ee,style:fe,onPointerDown:Be,onPointerMove:xe,onPointerUp:Te,onPointerCancel:Te,onPointerLeave:pe,onContextMenu:c=>{X&&c.preventDefault()},onWheel:c=>{X&&c.preventDefault()}})}function Dt(t){return String(t??"").replace(/\/+$/,"")}function Ht(t){const e=String(t??"");return e.startsWith("/")?e:`/${e}`}function zn(t){const e=Dt(t);if(!e)return"";if(/\/TileGroup\d+$/i.test(e))return e;let n=null;try{n=new URL(e)}catch{n=null}if(n){const r=`${n.protocol}//${n.host}`,i=Dt(n.pathname||"");return/\/ims$/i.test(i)?`${r}${i}`:/\/tiles$/i.test(i)?`${r}${i}`:`${r}${i}/tiles`}return/\/ims$/i.test(e)?"/ims":/\/tiles$/i.test(e)?`${e}`:`${e}/tiles`}function Wn(t,e){const n=t?.imsInfo||{},r=!!t?.imsInfo,i=Number(n.width??t?.width??0),o=Number(n.height??t?.height??0),s=Number(n.tileSize??t?.tileSize??0),a=Number(n.zoom??t?.zoom??0),u=String(n.path??t?.path??""),l=Number(n.mpp??t?.mpp??0);if(!i||!o||!s||!u)throw new Error("이미지 메타데이터가 불완전합니다. width/height/tileSize/path 확인 필요");const h=Array.isArray(t?.terms)?t.terms.map(x=>({termId:String(x?.termId??""),termName:String(x?.termName??""),termColor:String(x?.termColor??"")})):[],w=Ht(u),m=zn(e),g=r?(x,y,M)=>`${m}${w}/${x}/${M}_${y}.webp`:void 0;return{id:t?._id||"unknown",name:t?.name||"unknown",width:i,height:o,mpp:Number.isFinite(l)&&l>0?l:void 0,tileSize:s,maxTierZoom:Number.isFinite(a)?Math.max(0,Math.floor(a)):0,tilePath:u,tileBaseUrl:e,terms:h,tileUrlBuilder:g}}function wt(t,e,n,r){if(t.tileUrlBuilder)return t.tileUrlBuilder(e,n,r);const i=Ht(t.tilePath);return`${t.tileBaseUrl}${i}/${e}/${r}_${n}.webp`}const de={width:220,height:140,margin:16,position:"bottom-right",borderRadius:10,borderWidth:1.5,backgroundColor:"rgba(4, 10, 18, 0.88)",borderColor:"rgba(230, 244, 255, 0.35)",viewportStrokeColor:"rgba(255, 106, 61, 0.95)",viewportFillColor:"rgba(255, 106, 61, 0.2)",interactive:!0,showThumbnail:!0,maxThumbnailTiles:16};function Ne(t,e,n=1){return typeof t!="number"||!Number.isFinite(t)?e:Math.max(n,t)}function $e(t){return Array.isArray(t)&&t.length===4&&Number.isFinite(t[0])&&Number.isFinite(t[1])&&Number.isFinite(t[2])&&Number.isFinite(t[3])}function Zt({source:t,projectorRef:e,authToken:n="",options:r,invalidateRef:i,className:o,style:s}){const a=f.useRef(null),u=f.useRef(null),l=f.useRef(null),h=f.useRef({active:!1,pointerId:null}),w=f.useRef(null),m=f.useRef(!1),g=Ne(r?.width,de.width,64),x=Ne(r?.height,de.height,48),y=Ne(r?.margin,de.margin,0),M=Ne(r?.borderRadius,de.borderRadius,0),P=Ne(r?.borderWidth,de.borderWidth,0),D=Math.max(1,Math.round(Ne(r?.maxThumbnailTiles,de.maxThumbnailTiles,1))),A=r?.backgroundColor||de.backgroundColor,W=r?.borderColor||de.borderColor,be=r?.viewportStrokeColor||de.viewportStrokeColor,re=r?.viewportFillColor||de.viewportFillColor,Y=r?.interactive??de.interactive,Ee=r?.showThumbnail??de.showThumbnail,he=r?.position||de.position,ae=f.useMemo(()=>{const T={};return he==="top-left"||he==="bottom-left"?T.left=y:T.right=y,he==="top-left"||he==="top-right"?T.top=y:T.bottom=y,{position:"absolute",...T,width:g,height:x,borderRadius:M,overflow:"hidden",zIndex:4,pointerEvents:Y?"auto":"none",touchAction:"none",boxShadow:"0 10px 22px rgba(0, 0, 0, 0.3)",...s}},[y,he,g,x,M,Y,s]),Ce=f.useCallback(()=>{const T=a.current;if(!T)return;const v=T.getContext("2d");if(!v)return;const S=g,N=x,V=Math.max(1,window.devicePixelRatio||1),fe=Math.max(1,Math.round(S*V)),K=Math.max(1,Math.round(N*V));(T.width!==fe||T.height!==K)&&(T.width=fe,T.height=K),v.setTransform(1,0,0,1,0,0),v.clearRect(0,0,T.width,T.height),v.setTransform(V,0,0,V,0,0),v.fillStyle=A,v.fillRect(0,0,S,N);const G=u.current;G&&v.drawImage(G,0,0,S,N),v.strokeStyle=W,v.lineWidth=P,v.strokeRect(P*.5,P*.5,S-P,N-P);const J=e.current,oe=J?.getViewBounds?.(),se=J?.getViewCorners?.(),F=$e(oe)?oe:$e(l.current)?l.current:null;if(!F)return;l.current=F;const C=S/Math.max(1,t.width),z=N/Math.max(1,t.height),q=Array.isArray(se)&&se.length>=4&&se.every(c=>Array.isArray(c)&&c.length>=2&&Number.isFinite(c[0])&&Number.isFinite(c[1]))?se:null;if(q){v.beginPath();for(let c=0;c<q.length;c+=1){const p=q[c],E=$(p[0]*C,0,S),B=$(p[1]*z,0,N);c===0?v.moveTo(E,B):v.lineTo(E,B)}v.closePath(),v.fillStyle=re,v.fill(),v.strokeStyle=be,v.lineWidth=1.5,v.stroke();return}const j=$(F[0]*C,0,S),ce=$(F[1]*z,0,N),Be=$(F[2]*C,0,S),xe=$(F[3]*z,0,N),Te=Math.max(1,Be-j),pe=Math.max(1,xe-ce);v.fillStyle=re,v.fillRect(j,ce,Te,pe),v.strokeStyle=be,v.lineWidth=1.5,v.strokeRect(j+.5,ce+.5,Math.max(1,Te-1),Math.max(1,pe-1))},[g,x,A,W,P,e,t.width,t.height,re,be]),Z=f.useCallback(()=>{m.current||(m.current=!0,w.current=requestAnimationFrame(()=>{m.current=!1,w.current=null,Ce()}))},[Ce]),ee=f.useCallback((T,v)=>{const S=a.current;if(!S)return null;const N=S.getBoundingClientRect();if(!N.width||!N.height)return null;const V=$((T-N.left)/N.width,0,1),fe=$((v-N.top)/N.height,0,1);return[V*t.width,fe*t.height]},[t.width,t.height]),X=f.useCallback((T,v)=>{const S=e.current;if(!S)return;if(S.setViewCenter){S.setViewCenter(T,v),Z();return}const N=S.getViewBounds?.(),V=$e(N)?N:$e(l.current)?l.current:null;if(!V)return;const fe=Math.max(1e-6,V[2]-V[0]),K=Math.max(1e-6,V[3]-V[1]);S.setViewState({offsetX:T-fe*.5,offsetY:v-K*.5}),Z()},[e,Z]),ue=f.useCallback(T=>{if(!Y||T.button!==0)return;const v=a.current;if(!v)return;const S=ee(T.clientX,T.clientY);S&&(T.preventDefault(),T.stopPropagation(),v.setPointerCapture(T.pointerId),h.current={active:!0,pointerId:T.pointerId},X(S[0],S[1]))},[Y,ee,X]),Pe=f.useCallback(T=>{const v=h.current;if(!v.active||v.pointerId!==T.pointerId)return;const S=ee(T.clientX,T.clientY);S&&(T.preventDefault(),T.stopPropagation(),X(S[0],S[1]))},[ee,X]),ie=f.useCallback(T=>{const v=h.current;if(!v.active||v.pointerId!==T.pointerId)return;const S=a.current;if(S&&S.hasPointerCapture(T.pointerId))try{S.releasePointerCapture(T.pointerId)}catch{}h.current={active:!1,pointerId:null},Z()},[Z]);return f.useEffect(()=>{let T=!1;u.current=null,Z();const v=0,S=2**(t.maxTierZoom-v),N=Math.ceil(t.width/S),V=Math.ceil(t.height/S),fe=Math.max(1,Math.ceil(N/t.tileSize)),K=Math.max(1,Math.ceil(V/t.tileSize)),G=fe*K;if(!Ee||G>D)return;const J=document.createElement("canvas");J.width=Math.max(1,Math.round(g)),J.height=Math.max(1,Math.round(x));const oe=J.getContext("2d");if(!oe)return;oe.fillStyle=A,oe.fillRect(0,0,J.width,J.height);const se=[];for(let F=0;F<K;F+=1)for(let C=0;C<fe;C+=1){const z=C*t.tileSize*S,q=F*t.tileSize*S,j=Math.min((C+1)*t.tileSize,N)*S,ce=Math.min((F+1)*t.tileSize,V)*S;se.push({url:wt(t,v,C,F),bounds:[z,q,j,ce]})}return Promise.allSettled(se.map(async F=>{const C=!!n,z=await fetch(F.url,{headers:C?{Authorization:n}:void 0});if(!z.ok)throw new Error(`HTTP ${z.status}`);const q=await createImageBitmap(await z.blob());return{tile:F,bitmap:q}})).then(F=>{if(T){for(const q of F)q.status==="fulfilled"&&q.value.bitmap.close();return}const C=J.width/Math.max(1,t.width),z=J.height/Math.max(1,t.height);for(const q of F){if(q.status!=="fulfilled")continue;const{tile:{bounds:j},bitmap:ce}=q.value,Be=j[0]*C,xe=j[1]*z,Te=Math.max(1,(j[2]-j[0])*C),pe=Math.max(1,(j[3]-j[1])*z);oe.drawImage(ce,Be,xe,Te,pe),ce.close()}u.current=J,Z()}),()=>{T=!0}},[t,n,g,x,A,Ee,D,Z]),f.useEffect(()=>{Z()},[Z]),f.useEffect(()=>{if(i)return i.current=Z,()=>{i.current===Z&&(i.current=null)}},[i,Z]),f.useEffect(()=>()=>{h.current={active:!1,pointerId:null},w.current!==null&&(cancelAnimationFrame(w.current),w.current=null),m.current=!1},[]),me.jsx("canvas",{ref:a,className:o,style:ae,onPointerDown:ue,onPointerMove:Pe,onPointerUp:ie,onPointerCancel:ie,onContextMenu:T=>{T.preventDefault()},onWheel:T=>{T.preventDefault(),T.stopPropagation()}})}function Yn({imageWidth:t,imageHeight:e,tiles:n,viewState:r,className:i,style:o}){const s=f.useRef(null),a=f.useRef(null),u=f.useMemo(()=>({width:"100%",height:"100%",display:"block",...o}),[o]);return f.useEffect(()=>{const l=s.current;if(!l)return;const h=new Yt({canvas:l,imageWidth:t,imageHeight:e,initialViewState:r});return a.current=h,h.setTiles(n),()=>{h.destroy(),a.current=null}},[t,e]),f.useEffect(()=>{const l=a.current;l&&l.setTiles(n)},[n]),f.useEffect(()=>{const l=a.current;!l||!r||l.setViewState(r)},[r]),me.jsx("canvas",{ref:s,className:i,style:u})}function jt(t){return Math.max(0,Math.min(Math.floor(t.count??0),Math.floor((t.positions?.length??0)/2),t.paletteIndices?.length??0))}function Xn(t){if(!Array.isArray(t)||t.length<3)return[];const e=t.map(([i,o])=>[i,o]),n=e[0],r=e[e.length-1];return!n||!r?[]:((n[0]!==r[0]||n[1]!==r[1])&&e.push([n[0],n[1]]),e)}function Kt(t){const e=[];for(const n of t??[]){const r=Xn(n);if(r.length<4)continue;let i=1/0,o=1/0,s=-1/0,a=-1/0;for(const[u,l]of r)u<i&&(i=u),u>s&&(s=u),l<o&&(o=l),l>a&&(a=l);!Number.isFinite(i)||!Number.isFinite(o)||e.push({ring:r,minX:i,minY:o,maxX:s,maxY:a})}return e}function Vn(t,e,n){let r=!1;for(let i=0,o=n.length-1;i<n.length;o=i,i+=1){const s=n[i][0],a=n[i][1],u=n[o][0],l=n[o][1];a>e!=l>e&&t<(u-s)*(e-a)/(l-a||Number.EPSILON)+s&&(r=!r)}return r}function Jt(t,e,n){for(const r of n)if(!(t<r.minX||t>r.maxX||e<r.minY||e>r.maxY)&&Vn(t,e,r.ring))return!0;return!1}function Xe(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return null;const n=Kt(e??[]);if(n.length===0)return{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)};const r=jt(t),i=t.positions,o=t.paletteIndices,s=new Float32Array(r*2),a=new Uint16Array(r);let u=0;for(let l=0;l<r;l+=1){const h=i[l*2],w=i[l*2+1];Jt(h,w,n)&&(s[u*2]=h,s[u*2+1]=w,a[u]=o[l],u+=1)}return{count:u,positions:s.subarray(0,u*2),paletteIndices:a.subarray(0,u)}}function Qt(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return new Uint32Array(0);const n=Kt(e??[]);if(n.length===0)return new Uint32Array(0);const r=jt(t);if(r===0)return new Uint32Array(0);const i=t.positions,o=new Uint32Array(r);let s=0;for(let a=0;a<r;a+=1){const u=i[a*2],l=i[a*2+1];Jt(u,l,n)&&(o[s]=a,s+=1)}return o.subarray(0,s)}let He=null;const On=`
32
+ `;class Ot{constructor(e){d(this,"canvas");d(this,"gl");d(this,"camera",new vn);d(this,"imageWidth");d(this,"imageHeight");d(this,"clearColor");d(this,"program");d(this,"vao");d(this,"quadBuffer");d(this,"uCameraLocation");d(this,"uBoundsLocation");d(this,"uTextureLocation");d(this,"resizeObserver");d(this,"tiles",[]);d(this,"frameId",null);d(this,"loadVersion",0);d(this,"destroyed",!1);d(this,"fitted",!1);d(this,"controlledViewState",!1);this.canvas=e.canvas,this.imageWidth=Math.max(1,e.imageWidth),this.imageHeight=Math.max(1,e.imageHeight),this.clearColor=e.clearColor??[.03,.05,.08,1],this.gl=Tn(this.canvas),this.program=xn(this.gl,Cn,Rn);const n=this.gl.createVertexArray(),r=this.gl.createBuffer();if(!n||!r)throw new Error("Failed to create WebGL buffers.");this.vao=n,this.quadBuffer=r,this.gl.bindVertexArray(this.vao),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.quadBuffer);const i=new Float32Array([0,0,0,0,1,0,1,0,0,1,0,1,1,1,1,1]);this.gl.bufferData(this.gl.ARRAY_BUFFER,i,this.gl.STATIC_DRAW);const o=this.gl.getAttribLocation(this.program,"aUnit"),s=this.gl.getAttribLocation(this.program,"aUv");if(o<0||s<0)throw new Error("Failed to get attribute locations.");const a=4*Float32Array.BYTES_PER_ELEMENT;this.gl.enableVertexAttribArray(o),this.gl.vertexAttribPointer(o,2,this.gl.FLOAT,!1,a,0),this.gl.enableVertexAttribArray(s),this.gl.vertexAttribPointer(s,2,this.gl.FLOAT,!1,a,2*Float32Array.BYTES_PER_ELEMENT),this.gl.bindVertexArray(null),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,null),this.uCameraLocation=rt(this.gl,this.program,"uCamera"),this.uBoundsLocation=rt(this.gl,this.program,"uBounds"),this.uTextureLocation=rt(this.gl,this.program,"uTexture"),e.initialViewState&&(this.controlledViewState=!0,this.camera.setViewState(e.initialViewState)),this.resizeObserver=new ResizeObserver(()=>{this.resize()}),this.resizeObserver.observe(this.canvas),this.resize()}async setTiles(e){if(this.destroyed)return;const n=++this.loadVersion,r=await Promise.all(e.map(async i=>await this.loadTile(i,n)));if(this.destroyed||n!==this.loadVersion){for(const i of r)i&&this.gl.deleteTexture(i.texture);return}this.disposeTiles(this.tiles),this.tiles=r.filter(i=>i!==null),this.requestRender()}setViewState(e){this.controlledViewState=!0,this.camera.setViewState(e),this.requestRender()}getViewState(){return this.camera.getViewState()}destroy(){this.destroyed||(this.destroyed=!0,this.loadVersion+=1,this.frameId!==null&&(cancelAnimationFrame(this.frameId),this.frameId=null),this.resizeObserver.disconnect(),this.disposeTiles(this.tiles),this.tiles=[],this.gl.deleteBuffer(this.quadBuffer),this.gl.deleteVertexArray(this.vao),this.gl.deleteProgram(this.program))}async loadTile(e,n){try{const r=await fetch(e.url);if(!r.ok)throw new Error(`Tile fetch failed: ${r.status} ${r.statusText}`);const i=await r.blob(),o=await createImageBitmap(i);if(this.destroyed||n!==this.loadVersion)return o.close(),null;const s=this.gl.createTexture();if(!s)throw o.close(),new Error("Failed to create tile texture.");return this.gl.bindTexture(this.gl.TEXTURE_2D,s),this.gl.pixelStorei(this.gl.UNPACK_FLIP_Y_WEBGL,1),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_S,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_WRAP_T,this.gl.CLAMP_TO_EDGE),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MIN_FILTER,this.gl.LINEAR),this.gl.texParameteri(this.gl.TEXTURE_2D,this.gl.TEXTURE_MAG_FILTER,this.gl.LINEAR),this.gl.texImage2D(this.gl.TEXTURE_2D,0,this.gl.RGBA,this.gl.RGBA,this.gl.UNSIGNED_BYTE,o),this.gl.bindTexture(this.gl.TEXTURE_2D,null),o.close(),{id:e.id,bounds:e.bounds,texture:s}}catch(r){return console.error(`[M1TileRenderer] tile load failed: ${e.id}`,r),null}}resize(){if(this.destroyed)return;const e=this.canvas.getBoundingClientRect(),n=Math.max(1,e.width||this.canvas.clientWidth||1),r=Math.max(1,e.height||this.canvas.clientHeight||1),i=Math.max(1,window.devicePixelRatio||1),o=Math.max(1,Math.round(n*i)),s=Math.max(1,Math.round(r*i));(this.canvas.width!==o||this.canvas.height!==s)&&(this.canvas.width=o,this.canvas.height=s),this.camera.setViewport(n,r),this.gl.viewport(0,0,this.canvas.width,this.canvas.height),!this.fitted&&!this.controlledViewState&&(this.fitToImage(),this.fitted=!0),this.requestRender()}fitToImage(){const e=this.camera.getViewportSize(),n=Math.min(e.width/this.imageWidth,e.height/this.imageHeight),r=Number.isFinite(n)&&n>0?n:1,i=e.width/r,o=e.height/r,s=(this.imageWidth-i)*.5,a=(this.imageHeight-o)*.5;this.camera.setViewState({zoom:r,offsetX:s,offsetY:a})}requestRender(){this.frameId!==null||this.destroyed||(this.frameId=requestAnimationFrame(()=>{this.frameId=null,this.render()}))}render(){if(!this.destroyed){this.gl.clearColor(this.clearColor[0],this.clearColor[1],this.clearColor[2],this.clearColor[3]),this.gl.clear(this.gl.COLOR_BUFFER_BIT),this.gl.useProgram(this.program),this.gl.bindVertexArray(this.vao),this.gl.uniformMatrix3fv(this.uCameraLocation,!1,this.camera.getMatrix()),this.gl.uniform1i(this.uTextureLocation,0);for(const e of this.tiles)this.gl.activeTexture(this.gl.TEXTURE0),this.gl.bindTexture(this.gl.TEXTURE_2D,e.texture),this.gl.uniform4f(this.uBoundsLocation,e.bounds[0],e.bounds[1],e.bounds[2],e.bounds[3]),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,4);this.gl.bindTexture(this.gl.TEXTURE_2D,null),this.gl.bindVertexArray(null)}}disposeTiles(e){for(const n of e)this.gl.deleteTexture(n.texture)}}const bt=[160,160,160,255];function H(t,e,n){return Math.max(e,Math.min(n,t))}function wt(t,e,n){const r=Number(t),i=Number(e),o=Number(n);return!Number.isFinite(r)||r<=0?1:!Number.isFinite(i)||!Number.isFinite(o)?r:Math.pow(2,i-o)*r}function Mn(t,e,n){let i=100*wt(t,e,n);if(Number(t)){let o="μm";return i>1e3&&(i/=1e3,o="mm"),`${i.toPrecision(3)} ${o}`}return`${Math.round(i*1e3)/1e3} pixels`}function En(t,e){return!t&&!e?!0:!t||!e?!1:Math.abs((t.zoom??0)-(e.zoom??0))<1e-6&&Math.abs((t.offsetX??0)-(e.offsetX??0))<1e-6&&Math.abs((t.offsetY??0)-(e.offsetY??0))<1e-6&&Math.abs((t.rotationDeg??0)-(e.rotationDeg??0))<1e-6}function Pn(t){const e=String(t??"").trim();if(!e)return"";if(/^bearer\s+/i.test(e)){const n=e.replace(/^bearer\s+/i,"").trim();return n?`Bearer ${n}`:""}return`Bearer ${e}`}function Gt(t){const n=String(t??"").trim().match(/^#?([0-9a-fA-F]{6})$/);if(!n)return[...bt];const r=Number.parseInt(n[1],16);return[r>>16&255,r>>8&255,r&255,255]}function Sn(t){const e=[[...bt]],n=new Map;for(const i of t??[]){const o=String(i?.termId??"");!o||n.has(o)||(n.set(o,e.length),e.push(Gt(i?.termColor)))}const r=new Uint8Array(e.length*4);for(let i=0;i<e.length;i+=1)r[i*4]=e[i][0],r[i*4+1]=e[i][1],r[i*4+2]=e[i][2],r[i*4+3]=e[i][3];return{colors:r,termToPaletteIndex:n}}function It(t,e,n){const r=t.createShader(t.VERTEX_SHADER),i=t.createShader(t.FRAGMENT_SHADER);if(!r||!i)throw new Error("Shader allocation failed");if(t.shaderSource(r,e),t.compileShader(r),!t.getShaderParameter(r,t.COMPILE_STATUS))throw new Error(t.getShaderInfoLog(r)||"vertex compile failed");if(t.shaderSource(i,n),t.compileShader(i),!t.getShaderParameter(i,t.COMPILE_STATUS))throw new Error(t.getShaderInfoLog(i)||"fragment compile failed");const o=t.createProgram();if(!o)throw new Error("Program allocation failed");if(t.attachShader(o,r),t.attachShader(o,i),t.linkProgram(o),t.deleteShader(r),t.deleteShader(i),!t.getProgramParameter(o,t.LINK_STATUS))throw new Error(t.getProgramInfoLog(o)||"program link failed");return o}const An="rgba(255, 77, 79, 0.16)",In=3,_n=2,qt=96,Bn=1,_t=[],lt=[],Bt=1e3,$t=2,Ht=2,Un=4096,Fn=.2,Ue={color:"#ff4d4f",width:2,lineJoin:"round",lineCap:"round",shadowColor:"rgba(0, 0, 0, 0)",shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0},Ln={color:"#4cc9f0",width:2,lineDash:[10,8],lineJoin:"round",lineCap:"round",shadowColor:"rgba(0, 0, 0, 0)",shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0},ve={fontFamily:"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",fontSize:12,fontWeight:500,textColor:"#ffffff",backgroundColor:"rgba(8, 14, 22, 0.88)",borderColor:"rgba(255, 77, 79, 0.85)",borderWidth:1,paddingX:6,paddingY:4,offsetY:10,borderRadius:3};function et(t,e,n){return Math.max(e,Math.min(n,t))}function $e(t){return t==="stamp-rectangle"||t==="stamp-circle"||t==="stamp-rectangle-4096px"||t==="stamp-rectangle-2mm2"||t==="stamp-circle-2mm2"||t==="stamp-circle-hpf-0.2mm2"}function it(t,e){return typeof t!="number"||!Number.isFinite(t)||t<=0?e:t}function Dn(t){return{rectangleAreaMm2:it(t?.rectangleAreaMm2,$t),circleAreaMm2:it(t?.circleAreaMm2,Ht),rectanglePixelSize:it(t?.rectanglePixelSize,Un)}}function kn(t){return t*Bt*Bt}function Ut(t,e){return!t||!Number.isFinite(e)||e<=0?[]:we([[t[0]-e,t[1]-e],[t[0]+e,t[1]-e],[t[0]+e,t[1]+e],[t[0]-e,t[1]+e]])}function Nn(t,e,n=qt){if(!t||!Number.isFinite(e)||e<=0)return[];const r=[];for(let i=0;i<=n;i+=1){const o=i/n*Math.PI*2;r.push([t[0]+Math.cos(o)*e,t[1]+Math.sin(o)*e])}return we(r)}function we(t){if(!Array.isArray(t)||t.length<3)return[];const e=t.map(([i,o])=>[i,o]),n=e[0],r=e[e.length-1];return!n||!r?[]:((n[0]!==r[0]||n[1]!==r[1])&&e.push([n[0],n[1]]),e)}function ht(t,e){return!t||!e?[]:we([[t[0],t[1]],[e[0],t[1]],[e[0],e[1]],[t[0],e[1]]])}function ft(t,e,n=qt){if(!t||!e)return[];const r=(t[0]+e[0])*.5,i=(t[1]+e[1])*.5,o=Math.hypot(e[0]-t[0],e[1]-t[1])*.5;if(o<1)return[];const s=[];for(let a=0;a<=n;a+=1){const u=a/n*Math.PI*2;s.push([r+Math.cos(u)*o,i+Math.sin(u)*o])}return we(s)}function dt(t){if(!Array.isArray(t)||t.length<4)return 0;let e=0;for(let n=0;n<t.length-1;n+=1){const r=t[n],i=t[n+1];e+=r[0]*i[1]-i[0]*r[1]}return Math.abs(e*.5)}function Ft(t){if(!Array.isArray(t)||t.length===0)return[0,0,0,0];let e=1/0,n=1/0,r=-1/0,i=-1/0;for(const[o,s]of t)o<e&&(e=o),o>r&&(r=o),s<n&&(n=s),s>i&&(i=s);return[e,n,r,i]}function Lt(t){return Array.isArray(t)&&t.length>=4&&dt(t)>Bn}function mt(t,e,n=!1){if(e.length!==0){t.moveTo(e[0][0],e[0][1]);for(let r=1;r<e.length;r+=1)t.lineTo(e[r][0],e[r][1]);n&&t.closePath()}}function Ye(t,e,n,r=!1,i=!1){e.length!==0&&(t.beginPath(),mt(t,e,r),i&&r&&(t.fillStyle=An,t.fill()),t.strokeStyle=n.color,t.lineWidth=n.width,t.lineJoin=n.lineJoin,t.lineCap=n.lineCap,t.shadowColor=n.shadowColor,t.shadowBlur=n.shadowBlur,t.shadowOffsetX=n.shadowOffsetX,t.shadowOffsetY=n.shadowOffsetY,t.setLineDash(n.lineDash),t.stroke(),t.setLineDash(lt),t.shadowColor="rgba(0, 0, 0, 0)",t.shadowBlur=0,t.shadowOffsetX=0,t.shadowOffsetY=0)}function Zt(t){const e=Array.isArray(t?.lineDash)?t.lineDash.filter(s=>Number.isFinite(s)&&s>=0):lt,n=typeof t?.width=="number"&&Number.isFinite(t.width)?Math.max(0,t.width):Ue.width,r=typeof t?.shadowBlur=="number"&&Number.isFinite(t.shadowBlur)?Math.max(0,t.shadowBlur):Ue.shadowBlur,i=typeof t?.shadowOffsetX=="number"&&Number.isFinite(t.shadowOffsetX)?t.shadowOffsetX:Ue.shadowOffsetX,o=typeof t?.shadowOffsetY=="number"&&Number.isFinite(t.shadowOffsetY)?t.shadowOffsetY:Ue.shadowOffsetY;return{color:t?.color||Ue.color,width:n,lineDash:e.length?e:lt,lineJoin:t?.lineJoin||Ue.lineJoin,lineCap:t?.lineCap||Ue.lineCap,shadowColor:t?.shadowColor||Ue.shadowColor,shadowBlur:r,shadowOffsetX:i,shadowOffsetY:o}}function Xe(t,e){return e?Zt({color:e.color??t.color,width:e.width??t.width,lineDash:e.lineDash??t.lineDash,lineJoin:e.lineJoin??t.lineJoin,lineCap:e.lineCap??t.lineCap,shadowColor:e.shadowColor??t.shadowColor,shadowBlur:e.shadowBlur??t.shadowBlur,shadowOffsetX:e.shadowOffsetX??t.shadowOffsetX,shadowOffsetY:e.shadowOffsetY??t.shadowOffsetY}):t}function Dt(t,e){return t==null||e===null||e===void 0?!1:String(t)===String(e)}function zn(t){const e=t[0];return Array.isArray(e)&&Array.isArray(e[0])}function kt(t){return typeof t=="number"&&Number.isFinite(t)}function Yn(t){return Array.isArray(t)&&t.length>=2&&kt(t[0])&&kt(t[1])}function Wn(t){return Array.isArray(t)&&t.length>=2&&t.every(e=>Yn(e))}function jt(t,e){if(!(!Array.isArray(t)||t.length===0)){if(Wn(t)){e.push(t.map(([n,r])=>[n,r]));return}for(const n of t)jt(n,e)}}function Nt(t,e){const n=[];jt(t,n);const r=[];for(const i of n){if(i.length<2)continue;const o=e?we(i):i;o.length>=(e?4:2)&&r.push(o)}return r}function Xn(t,e,n,r){if(!(e.length<4||n.length===0)){t.save(),t.beginPath(),mt(t,e,!0);for(const i of n)i.length<4||mt(t,i,!0);t.fillStyle=r,t.fill("evenodd"),t.restore()}}function Vn(t){const e=typeof t?.paddingX=="number"&&Number.isFinite(t.paddingX)?Math.max(0,t.paddingX):ve.paddingX,n=typeof t?.paddingY=="number"&&Number.isFinite(t.paddingY)?Math.max(0,t.paddingY):ve.paddingY,r=typeof t?.fontSize=="number"&&Number.isFinite(t.fontSize)?Math.max(8,t.fontSize):ve.fontSize,i=typeof t?.borderWidth=="number"&&Number.isFinite(t.borderWidth)?Math.max(0,t.borderWidth):ve.borderWidth,o=typeof t?.offsetY=="number"&&Number.isFinite(t.offsetY)?t.offsetY:ve.offsetY,s=typeof t?.borderRadius=="number"&&Number.isFinite(t.borderRadius)?Math.max(0,t.borderRadius):ve.borderRadius;return{fontFamily:t?.fontFamily||ve.fontFamily,fontSize:r,fontWeight:t?.fontWeight||ve.fontWeight,textColor:t?.textColor||ve.textColor,backgroundColor:t?.backgroundColor||ve.backgroundColor,borderColor:t?.borderColor||ve.borderColor,borderWidth:i,paddingX:e,paddingY:n,offsetY:o,borderRadius:s}}function On(t,e,n,r,i,o){const s=Math.max(0,Math.min(o,r*.5,i*.5));t.beginPath(),t.moveTo(e+s,n),t.lineTo(e+r-s,n),t.quadraticCurveTo(e+r,n,e+r,n+s),t.lineTo(e+r,n+i-s),t.quadraticCurveTo(e+r,n+i,e+r-s,n+i),t.lineTo(e+s,n+i),t.quadraticCurveTo(e,n+i,e,n+i-s),t.lineTo(e,n+s),t.quadraticCurveTo(e,n,e+s,n),t.closePath()}function Gn(t){if(!t.length)return null;let e=1/0;for(const i of t)i[1]<e&&(e=i[1]);if(!Number.isFinite(e))return null;let n=1/0,r=-1/0;for(const i of t)Math.abs(i[1]-e)>.5||(i[0]<n&&(n=i[0]),i[0]>r&&(r=i[0]));return!Number.isFinite(n)||!Number.isFinite(r)?null:[(n+r)*.5,e]}function qn(t,e,n,r,i,o){const s=e.trim();if(!s)return;t.save(),t.font=`${o.fontWeight} ${o.fontSize}px ${o.fontFamily}`,t.textAlign="center",t.textBaseline="middle";const u=t.measureText(s).width+o.paddingX*2,c=o.fontSize+o.paddingY*2,l=et(n[0],u*.5+1,r-u*.5-1),w=et(n[1]-o.offsetY,c*.5+1,i-c*.5-1),m=l-u*.5,g=w-c*.5;t.fillStyle=o.backgroundColor,t.strokeStyle=o.borderColor,t.lineWidth=o.borderWidth,On(t,m,g,u,c,o.borderRadius),t.fill(),o.borderWidth>0&&t.stroke(),t.fillStyle=o.textColor,t.fillText(s,l,w+.5),t.restore()}function ot(t,e,n){return[et(t[0],0,e),et(t[1],0,n)]}function st(t){if(!Array.isArray(t)||t.length<2)return null;const e=Number(t[0]),n=Number(t[1]);return!Number.isFinite(e)||!Number.isFinite(n)?null:[e,n]}function Kt({tool:t,imageWidth:e,imageHeight:n,imageMpp:r,imageZoom:i,stampOptions:o,projectorRef:s,onDrawComplete:a,onPatchComplete:u,enabled:c,viewStateSignal:l,persistedRegions:w,patchRegions:m,persistedPolygons:g,regionStrokeStyle:T,regionStrokeHoverStyle:x,regionStrokeActiveStyle:C,patchStrokeStyle:P,resolveRegionStrokeStyle:F,overlayShapes:I,hoveredRegionId:X=null,activeRegionId:ye=null,regionLabelStyle:se,invalidateRef:V,className:Ae,style:le}){const ae=f.useRef(null),Ie=f.useRef(!1),ee=f.useRef(new Map),Me=f.useRef(t),ie=f.useRef({isDrawing:!1,pointerId:null,start:null,current:null,points:[],stampCenter:null}),q=c??t!=="cursor",xe=f.useMemo(()=>w&&w.length>0?w:!g||g.length===0?_t:g.map((h,p)=>({id:p,coordinates:h})),[w,g]),Ee=f.useMemo(()=>m??_t,[m]),y=f.useMemo(()=>Zt(T),[T]),v=f.useMemo(()=>Xe(y,x),[y,x]),S=f.useMemo(()=>Xe(y,C),[y,C]),k=f.useMemo(()=>Xe(Ln,P),[P]),Z=f.useMemo(()=>Vn(se),[se]),oe=f.useMemo(()=>Dn(o),[o]),he=f.useMemo(()=>({position:"absolute",inset:0,zIndex:2,width:"100%",height:"100%",display:"block",touchAction:"none",pointerEvents:q?"auto":"none",cursor:q?"crosshair":"default",...le}),[q,le]),Q=f.useCallback(()=>{const h=ae.current;if(!h)return;const p=h.getBoundingClientRect(),M=Math.max(1,window.devicePixelRatio||1),R=Math.max(1,Math.round(p.width*M)),W=Math.max(1,Math.round(p.height*M));(h.width!==R||h.height!==W)&&(h.width=R,h.height=W)},[]),O=f.useCallback(h=>{const p=s.current;if(!p||h.length===0)return[];const M=new Array(h.length);for(let R=0;R<h.length;R+=1){const W=st(p.worldToScreen(h[R][0],h[R][1]));if(!W)return[];M[R]=W}return M},[s]),ue=f.useCallback(h=>{if(!Number.isFinite(h)||h<=0)return 0;const p=typeof r=="number"&&Number.isFinite(r)&&r>0?r:1,M=typeof i=="number"&&Number.isFinite(i)?i:0,R=s.current?.getViewState?.().zoom,W=typeof R=="number"&&Number.isFinite(R)&&R>0?R:1,E=M+Math.log2(W),N=Math.max(1e-9,wt(p,M,E));return h/N/W},[r,i,s]),ne=f.useCallback((h,p)=>{if(!p)return[];let M=0;if(h==="stamp-rectangle-4096px"){const E=oe.rectanglePixelSize*.5;return Ut(p,E).map(Y=>ot(Y,e,n))}if(h==="stamp-rectangle"||h==="stamp-rectangle-2mm2"?M=h==="stamp-rectangle-2mm2"?$t:oe.rectangleAreaMm2:(h==="stamp-circle"||h==="stamp-circle-2mm2"||h==="stamp-circle-hpf-0.2mm2")&&(M=h==="stamp-circle-hpf-0.2mm2"?Fn:h==="stamp-circle-2mm2"?Ht:oe.circleAreaMm2),!Number.isFinite(M)||M<=0)return[];const R=kn(M);let W=[];if(h==="stamp-rectangle"||h==="stamp-rectangle-2mm2"){const E=ue(Math.sqrt(R)*.5);W=Ut(p,E)}else if(h==="stamp-circle"||h==="stamp-circle-2mm2"||h==="stamp-circle-hpf-0.2mm2"){const E=ue(Math.sqrt(R/Math.PI));W=Nn(p,E)}return W.length?W.map(E=>ot(E,e,n)):[]},[ue,e,n,oe]),B=f.useCallback(()=>{const h=ie.current;return $e(t)?ne(t,h.stampCenter):h.isDrawing?t==="freehand"?h.points:t==="rectangle"?ht(h.start,h.current):t==="circular"?ft(h.start,h.current):[]:[]},[t,ne]),$=f.useCallback(()=>{Q();const h=ae.current;if(!h)return;const p=h.getContext("2d");if(!p)return;const M=Math.max(1,window.devicePixelRatio||1),R=h.width/M,W=h.height/M;if(p.setTransform(1,0,0,1,0,0),p.clearRect(0,0,h.width,h.height),p.setTransform(M,0,0,M,0,0),xe.length>0)for(let E=0;E<xe.length;E+=1){const N=xe[E],Y=N?.coordinates;if(!Y||Y.length<3)continue;const L=we(Y),te=O(L);if(te.length>=4){const Te=N.id??E,De=Dt(ye,Te)?"active":Dt(X,Te)?"hover":"default";let pe=De==="active"?S:De==="hover"?v:y;if(F){const G=F({region:N,regionId:Te,regionIndex:E,state:De});pe=Xe(pe,G||void 0)}Ye(p,te,pe,!0,!1)}}if(Ee.length>0)for(let E=0;E<Ee.length;E+=1){const Y=Ee[E]?.coordinates;if(!Y||Y.length<3)continue;const L=we(Y),te=O(L);te.length<4||Ye(p,te,k,!0,!1)}if(Array.isArray(I)&&I.length>0){const E=!!globalThis.__OPEN_PLANT_DEBUG_OVERLAY__,N=O(we([[0,0],[e,0],[e,n],[0,n]]));for(let Y=0;Y<I.length;Y+=1){const L=I[Y];if(!L?.coordinates?.length||L.visible===!1)continue;const te=L.closed??zn(L.coordinates),Te=Nt(L.coordinates,te);if(L.invertedFill?.fillColor){const pe=[],G=Nt(L.coordinates,!0);for(const be of G){const Re=O(be);Re.length>=4&&pe.push(Re)}if(E){const be=String(L.id??Y),Re=`${N.length}|${G.length}|${pe.length}|${L.invertedFill.fillColor}`;ee.current.get(be)!==Re&&(ee.current.set(be,Re),console.debug("[open-plant] invertedFill",{id:L.id??Y,outerRingPoints:N.length,sourceRingCount:G.length,holeRingCount:pe.length,fillColor:L.invertedFill.fillColor}))}Xn(p,N,pe,L.invertedFill.fillColor)}if(Te.length===0)continue;const De=Xe(y,L.stroke??L.strokeStyle);for(const pe of Te){const G=O(pe);G.length<2||Ye(p,G,De,te,L.fill??!1)}}}if(q){const E=B();if(E.length>0)if(t==="freehand"){const N=O(E);N.length>=2&&Ye(p,N,y,!1,!1),N.length>=3&&Ye(p,O(we(E)),y,!0,!0)}else{const N=O(E);N.length>=4&&Ye(p,N,y,!0,!0)}}if(xe.length>0)for(const E of xe){if(!E.label)continue;const N=E?.coordinates;if(!N||N.length<3)continue;const Y=we(N),L=Gn(Y);if(!L)continue;const te=st(s.current?.worldToScreen(L[0],L[1])??[]);te&&qn(p,E.label,te,R,W,Z)}},[q,t,B,Q,O,e,n,s,xe,I,X,ye,y,v,S,Ee,k,F,Z]),A=f.useCallback(()=>{Ie.current||(Ie.current=!0,requestAnimationFrame(()=>{Ie.current=!1,$()}))},[$]),z=f.useCallback(()=>{const h=ie.current,p=ae.current;if(p&&h.pointerId!==null&&p.hasPointerCapture(h.pointerId))try{p.releasePointerCapture(h.pointerId)}catch{}h.isDrawing=!1,h.pointerId=null,h.start=null,h.current=null,h.points=[],h.stampCenter=null},[]),j=f.useCallback(h=>{const p=s.current;if(!p||e<=0||n<=0)return null;const M=st(p.screenToWorld(h.clientX,h.clientY));return M?ot(M,e,n):null},[s,e,n]),ce=f.useCallback(()=>{const h=ie.current;if(!h.isDrawing){z(),A();return}let p=[];t==="freehand"?h.points.length>=In&&(p=we(h.points)):t==="rectangle"?p=ht(h.start,h.current):t==="circular"&&(p=ft(h.start,h.current)),(t==="freehand"||t==="rectangle"||t==="circular")&&Lt(p)&&a&&a({tool:t,intent:"roi",coordinates:p,bbox:Ft(p),areaPx:dt(p)}),z(),A()},[t,a,z,A]),_e=f.useCallback((h,p)=>{const M=ne(h,p);if(!Lt(M))return;const R=h==="stamp-rectangle-4096px"?"patch":"roi",W={tool:h,intent:R,coordinates:M,bbox:Ft(M),areaPx:dt(M)};a?.(W),R==="patch"&&u&&u(W)},[ne,a,u]),Ce=f.useCallback(h=>{if(!q||t==="cursor"||h.button!==0)return;const p=j(h);if(!p)return;if(h.preventDefault(),h.stopPropagation(),$e(t)){const W=ie.current;W.stampCenter=p,_e(t,p),A();return}const M=ae.current;M&&M.setPointerCapture(h.pointerId);const R=ie.current;R.isDrawing=!0,R.pointerId=h.pointerId,R.start=p,R.current=p,R.points=t==="freehand"?[p]:[],A()},[q,t,j,_e,A]),Pe=f.useCallback(h=>{if(!q||t==="cursor")return;const p=j(h);if(!p)return;if($e(t)){const R=ie.current;R.stampCenter=p,h.preventDefault(),h.stopPropagation(),A();return}const M=ie.current;if(!(!M.isDrawing||M.pointerId!==h.pointerId)){if(h.preventDefault(),h.stopPropagation(),t==="freehand"){const R=s.current,W=Math.max(1e-6,R?.getViewState?.().zoom??1),E=_n/W,N=E*E,Y=M.points[M.points.length-1];if(!Y)M.points.push(p);else{const L=p[0]-Y[0],te=p[1]-Y[1];L*L+te*te>=N&&M.points.push(p)}}else M.current=p;A()}},[q,t,j,A,s]),fe=f.useCallback(h=>{const p=ie.current;if(!p.isDrawing||p.pointerId!==h.pointerId)return;h.preventDefault(),h.stopPropagation();const M=ae.current;if(M&&M.hasPointerCapture(h.pointerId))try{M.releasePointerCapture(h.pointerId)}catch{}ce()},[ce]),K=f.useCallback(()=>{if(!$e(t))return;const h=ie.current;h.stampCenter&&(h.stampCenter=null,A())},[t,A]);return f.useEffect(()=>{Q(),A();const h=ae.current;if(!h)return;const p=new ResizeObserver(()=>{Q(),A()});return p.observe(h),()=>{p.disconnect()}},[Q,A]),f.useEffect(()=>{q||z(),A()},[q,A,z]),f.useEffect(()=>{Me.current!==t&&(Me.current=t,z(),A())},[t,z,A]),f.useEffect(()=>{A()},[l,xe,I,A]),f.useEffect(()=>{if(V)return V.current=A,()=>{V.current===A&&(V.current=null)}},[V,A]),f.useEffect(()=>{if(!q)return;const h=p=>{p.key==="Escape"&&(z(),A())};return window.addEventListener("keydown",h),()=>{window.removeEventListener("keydown",h)}},[q,z,A]),me.jsx("canvas",{ref:ae,className:Ae,style:he,onPointerDown:Ce,onPointerMove:Pe,onPointerUp:fe,onPointerCancel:fe,onPointerLeave:K,onContextMenu:h=>{q&&h.preventDefault()},onWheel:h=>{q&&h.preventDefault()}})}function zt(t){return String(t??"").replace(/\/+$/,"")}function Jt(t){const e=String(t??"");return e.startsWith("/")?e:`/${e}`}function $n(t){const e=zt(t);if(!e)return"";if(/\/TileGroup\d+$/i.test(e))return e;let n=null;try{n=new URL(e)}catch{n=null}if(n){const r=`${n.protocol}//${n.host}`,i=zt(n.pathname||"");return/\/ims$/i.test(i)?`${r}${i}`:/\/tiles$/i.test(i)?`${r}${i}`:`${r}${i}/tiles`}return/\/ims$/i.test(e)?"/ims":/\/tiles$/i.test(e)?`${e}`:`${e}/tiles`}function Hn(t,e){const n=t?.imsInfo||{},r=!!t?.imsInfo,i=Number(n.width??t?.width??0),o=Number(n.height??t?.height??0),s=Number(n.tileSize??t?.tileSize??0),a=Number(n.zoom??t?.zoom??0),u=String(n.path??t?.path??""),c=Number(n.mpp??t?.mpp??0);if(!i||!o||!s||!u)throw new Error("이미지 메타데이터가 불완전합니다. width/height/tileSize/path 확인 필요");const l=Array.isArray(t?.terms)?t.terms.map(T=>({termId:String(T?.termId??""),termName:String(T?.termName??""),termColor:String(T?.termColor??"")})):[],w=Jt(u),m=$n(e),g=r?(T,x,C)=>`${m}${w}/${T}/${C}_${x}.webp`:void 0;return{id:t?._id||"unknown",name:t?.name||"unknown",width:i,height:o,mpp:Number.isFinite(c)&&c>0?c:void 0,tileSize:s,maxTierZoom:Number.isFinite(a)?Math.max(0,Math.floor(a)):0,tilePath:u,tileBaseUrl:e,terms:l,tileUrlBuilder:g}}function yt(t,e,n,r){if(t.tileUrlBuilder)return t.tileUrlBuilder(e,n,r);const i=Jt(t.tilePath);return`${t.tileBaseUrl}${i}/${e}/${r}_${n}.webp`}const de={width:220,height:140,margin:16,position:"bottom-right",borderRadius:10,borderWidth:1.5,backgroundColor:"rgba(4, 10, 18, 0.88)",borderColor:"rgba(230, 244, 255, 0.35)",viewportStrokeColor:"rgba(255, 106, 61, 0.95)",viewportFillColor:"rgba(255, 106, 61, 0.2)",interactive:!0,showThumbnail:!0,maxThumbnailTiles:16};function We(t,e,n=1){return typeof t!="number"||!Number.isFinite(t)?e:Math.max(n,t)}function He(t){return Array.isArray(t)&&t.length===4&&Number.isFinite(t[0])&&Number.isFinite(t[1])&&Number.isFinite(t[2])&&Number.isFinite(t[3])}function Qt({source:t,projectorRef:e,authToken:n="",options:r,invalidateRef:i,className:o,style:s}){const a=f.useRef(null),u=f.useRef(null),c=f.useRef(null),l=f.useRef({active:!1,pointerId:null}),w=f.useRef(null),m=f.useRef(!1),g=We(r?.width,de.width,64),T=We(r?.height,de.height,48),x=We(r?.margin,de.margin,0),C=We(r?.borderRadius,de.borderRadius,0),P=We(r?.borderWidth,de.borderWidth,0),F=Math.max(1,Math.round(We(r?.maxThumbnailTiles,de.maxThumbnailTiles,1))),I=r?.backgroundColor||de.backgroundColor,X=r?.borderColor||de.borderColor,ye=r?.viewportStrokeColor||de.viewportStrokeColor,se=r?.viewportFillColor||de.viewportFillColor,V=r?.interactive??de.interactive,Ae=r?.showThumbnail??de.showThumbnail,le=r?.position||de.position,ae=f.useMemo(()=>{const y={};return le==="top-left"||le==="bottom-left"?y.left=x:y.right=x,le==="top-left"||le==="top-right"?y.top=x:y.bottom=x,{position:"absolute",...y,width:g,height:T,borderRadius:C,overflow:"hidden",zIndex:4,pointerEvents:V?"auto":"none",touchAction:"none",boxShadow:"0 10px 22px rgba(0, 0, 0, 0.3)",...s}},[x,le,g,T,C,V,s]),Ie=f.useCallback(()=>{const y=a.current;if(!y)return;const v=y.getContext("2d");if(!v)return;const S=g,k=T,Z=Math.max(1,window.devicePixelRatio||1),oe=Math.max(1,Math.round(S*Z)),he=Math.max(1,Math.round(k*Z));(y.width!==oe||y.height!==he)&&(y.width=oe,y.height=he),v.setTransform(1,0,0,1,0,0),v.clearRect(0,0,y.width,y.height),v.setTransform(Z,0,0,Z,0,0),v.fillStyle=I,v.fillRect(0,0,S,k);const Q=u.current;Q&&v.drawImage(Q,0,0,S,k),v.strokeStyle=X,v.lineWidth=P,v.strokeRect(P*.5,P*.5,S-P,k-P);const O=e.current,ue=O?.getViewBounds?.(),ne=O?.getViewCorners?.(),B=He(ue)?ue:He(c.current)?c.current:null;if(!B)return;c.current=B;const $=S/Math.max(1,t.width),A=k/Math.max(1,t.height),z=Array.isArray(ne)&&ne.length>=4&&ne.every(K=>Array.isArray(K)&&K.length>=2&&Number.isFinite(K[0])&&Number.isFinite(K[1]))?ne:null;if(z){v.beginPath();for(let K=0;K<z.length;K+=1){const h=z[K],p=H(h[0]*$,0,S),M=H(h[1]*A,0,k);K===0?v.moveTo(p,M):v.lineTo(p,M)}v.closePath(),v.fillStyle=se,v.fill(),v.strokeStyle=ye,v.lineWidth=1.5,v.stroke();return}const j=H(B[0]*$,0,S),ce=H(B[1]*A,0,k),_e=H(B[2]*$,0,S),Ce=H(B[3]*A,0,k),Pe=Math.max(1,_e-j),fe=Math.max(1,Ce-ce);v.fillStyle=se,v.fillRect(j,ce,Pe,fe),v.strokeStyle=ye,v.lineWidth=1.5,v.strokeRect(j+.5,ce+.5,Math.max(1,Pe-1),Math.max(1,fe-1))},[g,T,I,X,P,e,t.width,t.height,se,ye]),ee=f.useCallback(()=>{m.current||(m.current=!0,w.current=requestAnimationFrame(()=>{m.current=!1,w.current=null,Ie()}))},[Ie]),Me=f.useCallback((y,v)=>{const S=a.current;if(!S)return null;const k=S.getBoundingClientRect();if(!k.width||!k.height)return null;const Z=H((y-k.left)/k.width,0,1),oe=H((v-k.top)/k.height,0,1);return[Z*t.width,oe*t.height]},[t.width,t.height]),ie=f.useCallback((y,v)=>{const S=e.current;if(!S)return;if(S.setViewCenter){S.setViewCenter(y,v),ee();return}const k=S.getViewBounds?.(),Z=He(k)?k:He(c.current)?c.current:null;if(!Z)return;const oe=Math.max(1e-6,Z[2]-Z[0]),he=Math.max(1e-6,Z[3]-Z[1]);S.setViewState({offsetX:y-oe*.5,offsetY:v-he*.5}),ee()},[e,ee]),q=f.useCallback(y=>{if(!V||y.button!==0)return;const v=a.current;if(!v)return;const S=Me(y.clientX,y.clientY);S&&(y.preventDefault(),y.stopPropagation(),v.setPointerCapture(y.pointerId),l.current={active:!0,pointerId:y.pointerId},ie(S[0],S[1]))},[V,Me,ie]),xe=f.useCallback(y=>{const v=l.current;if(!v.active||v.pointerId!==y.pointerId)return;const S=Me(y.clientX,y.clientY);S&&(y.preventDefault(),y.stopPropagation(),ie(S[0],S[1]))},[Me,ie]),Ee=f.useCallback(y=>{const v=l.current;if(!v.active||v.pointerId!==y.pointerId)return;const S=a.current;if(S&&S.hasPointerCapture(y.pointerId))try{S.releasePointerCapture(y.pointerId)}catch{}l.current={active:!1,pointerId:null},ee()},[ee]);return f.useEffect(()=>{let y=!1;u.current=null,ee();const v=0,S=2**(t.maxTierZoom-v),k=Math.ceil(t.width/S),Z=Math.ceil(t.height/S),oe=Math.max(1,Math.ceil(k/t.tileSize)),he=Math.max(1,Math.ceil(Z/t.tileSize)),Q=oe*he;if(!Ae||Q>F)return;const O=document.createElement("canvas");O.width=Math.max(1,Math.round(g)),O.height=Math.max(1,Math.round(T));const ue=O.getContext("2d");if(!ue)return;ue.fillStyle=I,ue.fillRect(0,0,O.width,O.height);const ne=[];for(let B=0;B<he;B+=1)for(let $=0;$<oe;$+=1){const A=$*t.tileSize*S,z=B*t.tileSize*S,j=Math.min(($+1)*t.tileSize,k)*S,ce=Math.min((B+1)*t.tileSize,Z)*S;ne.push({url:yt(t,v,$,B),bounds:[A,z,j,ce]})}return Promise.allSettled(ne.map(async B=>{const $=!!n,A=await fetch(B.url,{headers:$?{Authorization:n}:void 0});if(!A.ok)throw new Error(`HTTP ${A.status}`);const z=await createImageBitmap(await A.blob());return{tile:B,bitmap:z}})).then(B=>{if(y){for(const z of B)z.status==="fulfilled"&&z.value.bitmap.close();return}const $=O.width/Math.max(1,t.width),A=O.height/Math.max(1,t.height);for(const z of B){if(z.status!=="fulfilled")continue;const{tile:{bounds:j},bitmap:ce}=z.value,_e=j[0]*$,Ce=j[1]*A,Pe=Math.max(1,(j[2]-j[0])*$),fe=Math.max(1,(j[3]-j[1])*A);ue.drawImage(ce,_e,Ce,Pe,fe),ce.close()}u.current=O,ee()}),()=>{y=!0}},[t,n,g,T,I,Ae,F,ee]),f.useEffect(()=>{ee()},[ee]),f.useEffect(()=>{if(i)return i.current=ee,()=>{i.current===ee&&(i.current=null)}},[i,ee]),f.useEffect(()=>()=>{l.current={active:!1,pointerId:null},w.current!==null&&(cancelAnimationFrame(w.current),w.current=null),m.current=!1},[]),me.jsx("canvas",{ref:a,className:o,style:ae,onPointerDown:q,onPointerMove:xe,onPointerUp:Ee,onPointerCancel:Ee,onContextMenu:y=>{y.preventDefault()},onWheel:y=>{y.preventDefault(),y.stopPropagation()}})}function Zn({imageWidth:t,imageHeight:e,tiles:n,viewState:r,className:i,style:o}){const s=f.useRef(null),a=f.useRef(null),u=f.useMemo(()=>({width:"100%",height:"100%",display:"block",...o}),[o]);return f.useEffect(()=>{const c=s.current;if(!c)return;const l=new Ot({canvas:c,imageWidth:t,imageHeight:e,initialViewState:r});return a.current=l,l.setTiles(n),()=>{l.destroy(),a.current=null}},[t,e]),f.useEffect(()=>{const c=a.current;c&&c.setTiles(n)},[n]),f.useEffect(()=>{const c=a.current;!c||!r||c.setViewState(r)},[r]),me.jsx("canvas",{ref:s,className:i,style:u})}function en(t){return Math.max(0,Math.min(Math.floor(t.count??0),Math.floor((t.positions?.length??0)/2),t.paletteIndices?.length??0))}function jn(t){if(!Array.isArray(t)||t.length<3)return[];const e=t.map(([i,o])=>[i,o]),n=e[0],r=e[e.length-1];return!n||!r?[]:((n[0]!==r[0]||n[1]!==r[1])&&e.push([n[0],n[1]]),e)}function tn(t){const e=[];for(const n of t??[]){const r=jn(n);if(r.length<4)continue;let i=1/0,o=1/0,s=-1/0,a=-1/0;for(const[u,c]of r)u<i&&(i=u),u>s&&(s=u),c<o&&(o=c),c>a&&(a=c);!Number.isFinite(i)||!Number.isFinite(o)||e.push({ring:r,minX:i,minY:o,maxX:s,maxY:a})}return e}function Kn(t,e,n){let r=!1;for(let i=0,o=n.length-1;i<n.length;o=i,i+=1){const s=n[i][0],a=n[i][1],u=n[o][0],c=n[o][1];a>e!=c>e&&t<(u-s)*(e-a)/(c-a||Number.EPSILON)+s&&(r=!r)}return r}function nn(t,e,n){for(const r of n)if(!(t<r.minX||t>r.maxX||e<r.minY||e>r.maxY)&&Kn(t,e,r.ring))return!0;return!1}function Oe(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return null;const n=tn(e??[]);if(n.length===0)return{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)};const r=en(t),i=t.positions,o=t.paletteIndices,s=new Float32Array(r*2),a=new Uint16Array(r);let u=0;for(let c=0;c<r;c+=1){const l=i[c*2],w=i[c*2+1];nn(l,w,n)&&(s[u*2]=l,s[u*2+1]=w,a[u]=o[c],u+=1)}return{count:u,positions:s.subarray(0,u*2),paletteIndices:a.subarray(0,u)}}function rn(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return new Uint32Array(0);const n=tn(e??[]);if(n.length===0)return new Uint32Array(0);const r=en(t);if(r===0)return new Uint32Array(0);const i=t.positions,o=new Uint32Array(r);let s=0;for(let a=0;a<r;a+=1){const u=i[a*2],c=i[a*2+1];nn(u,c,n)&&(o[s]=a,s+=1)}return o.subarray(0,s)}let Ze=null;const Jn=`
33
33
  struct Params {
34
34
  pointCount: u32,
35
35
  boundsCount: u32,
@@ -60,7 +60,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
60
60
  }
61
61
  outputMask[i] = inside;
62
62
  }
63
- `;function Gn(){if(typeof navigator>"u")return!1;const t=navigator;return typeof t.gpu=="object"&&t.gpu!==null}function en(){if(!Gn())return null;const e=navigator.gpu;if(!e||typeof e!="object")return null;const n=e;return typeof n.requestAdapter!="function"?null:n}const Ze=globalThis.GPUShaderStage?.COMPUTE??4,at=globalThis.GPUBufferUsage?.STORAGE??128,je=globalThis.GPUBufferUsage?.COPY_DST??8,qn=globalThis.GPUBufferUsage?.COPY_SRC??4,$n=globalThis.GPUBufferUsage?.UNIFORM??64,Hn=globalThis.GPUBufferUsage?.MAP_READ??1,Zn=globalThis.GPUMapMode?.READ??1;async function jn(){const t=en();if(!t)return{supported:!1,features:[]};const e=await t.requestAdapter();return e?{supported:!0,adapterName:e.info?.description??e.info?.vendor??"unknown",features:Array.from(e.features),limits:{maxStorageBufferBindingSize:Number(e.limits.maxStorageBufferBindingSize),maxComputeInvocationsPerWorkgroup:Number(e.limits.maxComputeInvocationsPerWorkgroup),maxComputeWorkgroupSizeX:Number(e.limits.maxComputeWorkgroupSizeX)}}:{supported:!1,features:[]}}async function Kn(){return He||(He=(async()=>{const t=en();if(!t)return null;const e=await t.requestAdapter();if(!e)return null;const n=await e.requestDevice(),r=n.createBindGroupLayout({entries:[{binding:0,visibility:Ze,buffer:{type:"read-only-storage"}},{binding:1,visibility:Ze,buffer:{type:"read-only-storage"}},{binding:2,visibility:Ze,buffer:{type:"storage"}},{binding:3,visibility:Ze,buffer:{type:"uniform"}}]}),i=n.createComputePipeline({layout:n.createPipelineLayout({bindGroupLayouts:[r]}),compute:{module:n.createShaderModule({code:On}),entryPoint:"main"}});return{device:n,pipeline:i,bindGroupLayout:r}})(),He)}function Ke(t,e){return Math.ceil(t/e)*e}async function tn(t,e,n){const r=await Kn();if(!r)return null;const i=Math.max(0,Math.floor(e)),o=Math.max(0,Math.floor(n.length/4));if(i===0||o===0)return new Uint32Array(0);const s=Math.min(i,Math.floor(t.length/2));if(s===0)return new Uint32Array(0);const a=s*2*Float32Array.BYTES_PER_ELEMENT,u=o*4*Float32Array.BYTES_PER_ELEMENT,l=s*Uint32Array.BYTES_PER_ELEMENT,h=Number(r.device.limits.maxStorageBufferBindingSize);if(a>h||u>h||l>h)return null;const w=r.device.createBuffer({size:Ke(a,4),usage:at|je}),m=r.device.createBuffer({size:Ke(u,4),usage:at|je}),g=r.device.createBuffer({size:Ke(l,4),usage:at|qn}),x=r.device.createBuffer({size:16,usage:$n|je}),y=r.device.createBuffer({size:Ke(l,4),usage:je|Hn});r.device.queue.writeBuffer(w,0,t.buffer,t.byteOffset,a),r.device.queue.writeBuffer(m,0,n.buffer,n.byteOffset,u),r.device.queue.writeBuffer(x,0,new Uint32Array([s,o,0,0]));const M=r.device.createBindGroup({layout:r.bindGroupLayout,entries:[{binding:0,resource:{buffer:w}},{binding:1,resource:{buffer:m}},{binding:2,resource:{buffer:g}},{binding:3,resource:{buffer:x}}]}),P=r.device.createCommandEncoder(),D=P.beginComputePass();D.setPipeline(r.pipeline),D.setBindGroup(0,M),D.dispatchWorkgroups(Math.ceil(s/256)),D.end(),P.copyBufferToBuffer(g,0,y,0,l),r.device.queue.submit([P.finish()]),await y.mapAsync(Zn);const A=y.getMappedRange(),W=new Uint32Array(A.slice(0));return y.unmap(),w.destroy(),m.destroy(),g.destroy(),x.destroy(),y.destroy(),W}function Me(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function Jn(t){if(!Array.isArray(t)||t.length<3)return[];const e=t.map(([i,o])=>[i,o]),n=e[0],r=e[e.length-1];return!n||!r?[]:((n[0]!==r[0]||n[1]!==r[1])&&e.push([n[0],n[1]]),e)}function Qn(t){const e=[];for(const n of t??[]){const r=Jn(n);if(r.length<4)continue;let i=1/0,o=1/0,s=-1/0,a=-1/0;for(const[u,l]of r)u<i&&(i=u),u>s&&(s=u),l<o&&(o=l),l>a&&(a=l);!Number.isFinite(i)||!Number.isFinite(o)||e.push({ring:r,minX:i,minY:o,maxX:s,maxY:a})}return e}function er(t,e,n){let r=!1;for(let i=0,o=n.length-1;i<n.length;o=i,i+=1){const s=n[i][0],a=n[i][1],u=n[o][0],l=n[o][1];a>e!=l>e&&t<(u-s)*(e-a)/(l-a||Number.EPSILON)+s&&(r=!r)}return r}function kt(t,e,n){for(const r of n)if(!(t<r.minX||t>r.maxX||e<r.minY||e>r.maxY)&&er(t,e,r.ring))return!0;return!1}async function nn(t,e,n={}){const r=Me(),i=n.bridgeToDraw===!0;if(!t||!t.count||!t.positions||!t.paletteIndices)return{data:null,meta:{mode:"hybrid-webgpu",durationMs:Me()-r,usedWebGpu:!1,candidateCount:0,bridgedToDraw:!1}};const o=Qn(e??[]);if(o.length===0)return{data:{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)},meta:{mode:"hybrid-webgpu",durationMs:Me()-r,usedWebGpu:!1,candidateCount:0,bridgedToDraw:!1}};const s=Math.max(0,Math.min(t.count,Math.floor(t.positions.length/2),t.paletteIndices.length));if(s===0)return{data:{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)},meta:{mode:"hybrid-webgpu",durationMs:Me()-r,usedWebGpu:!1,candidateCount:0,bridgedToDraw:!1}};const a=new Float32Array(o.length*4);for(let y=0;y<o.length;y+=1){const M=y*4,P=o[y];a[M]=P.minX,a[M+1]=P.minY,a[M+2]=P.maxX,a[M+3]=P.maxY}let u=null,l=!1;try{u=await tn(t.positions,s,a),l=!!u}catch{u=null,l=!1}if(!u)return{data:Xe(t,e),meta:{mode:"hybrid-webgpu",durationMs:Me()-r,usedWebGpu:!1,candidateCount:s,bridgedToDraw:!1}};let h=0;for(let y=0;y<s;y+=1)u[y]===1&&(h+=1);const w=new Uint32Array(h);if(h>0){let y=0;for(let M=0;M<s;M+=1)u[M]===1&&(w[y]=M,y+=1)}if(h===0)return i?{data:{count:s,positions:t.positions.subarray(0,s*2),paletteIndices:t.paletteIndices.subarray(0,s),drawIndices:new Uint32Array(0)},meta:{mode:"hybrid-webgpu",durationMs:Me()-r,usedWebGpu:!0,candidateCount:0,bridgedToDraw:!0}}:{data:{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)},meta:{mode:"hybrid-webgpu",durationMs:Me()-r,usedWebGpu:!0,candidateCount:0,bridgedToDraw:!1}};if(i){const y=new Uint32Array(h);let M=0;for(let P=0;P<h;P+=1){const D=w[P]??0,A=t.positions[D*2],W=t.positions[D*2+1];kt(A,W,o)&&(y[M]=D,M+=1)}return{data:{count:s,positions:t.positions.subarray(0,s*2),paletteIndices:t.paletteIndices.subarray(0,s),drawIndices:y.subarray(0,M)},meta:{mode:"hybrid-webgpu",durationMs:Me()-r,usedWebGpu:!0,candidateCount:h,bridgedToDraw:!0}}}const m=new Float32Array(h*2),g=new Uint16Array(h);let x=0;for(let y=0;y<h;y+=1){const M=w[y]??0,P=t.positions[M*2],D=t.positions[M*2+1];kt(P,D,o)&&(m[x*2]=P,m[x*2+1]=D,g[x]=t.paletteIndices[M],x+=1)}return{data:{count:x,positions:m.subarray(0,x*2),paletteIndices:g.subarray(0,x)},meta:{mode:"hybrid-webgpu",durationMs:Me()-r,usedWebGpu:!0,candidateCount:h,bridgedToDraw:!1}}}let ge=null,mt=!0,rn=1;const _e=new Map;function Ie(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function on(){if(!mt)return null;if(ge)return ge;try{const t=new Worker(new URL(""+(typeof document>"u"?require("url").pathToFileURL(__dirname+"/assets/roi-clip-worker-DdVYCepx.js").href:new URL("assets/roi-clip-worker-DdVYCepx.js",document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"&&document.currentScript.src||document.baseURI).href),typeof document>"u"?require("url").pathToFileURL(__filename).href:nt&&nt.tagName.toUpperCase()==="SCRIPT"&&nt.src||new URL("index.cjs",document.baseURI).href),{type:"module"});return t.addEventListener("message",yt),t.addEventListener("error",xt),ge=t,t}catch{return mt=!1,null}}function yt(t){const e=t.data;if(!e)return;const n=_e.get(e.id);if(!n)return;if(_e.delete(e.id),e.type==="roi-clip-failure"){n.reject(new Error(e.error||"worker clip failed"));return}if(e.type==="roi-clip-index-success"){if(n.kind!=="index"){n.reject(new Error("worker response mismatch: expected point data result"));return}const a=Math.max(0,Math.floor(e.count)),u=new Uint32Array(e.indices).subarray(0,a);n.resolve({indices:u,meta:{mode:"worker",durationMs:Number.isFinite(e.durationMs)?e.durationMs:Ie()-n.startMs}});return}if(n.kind!=="data"){n.reject(new Error("worker response mismatch: expected index result"));return}const r=Math.max(0,Math.floor(e.count)),i=new Float32Array(e.positions),o=new Uint16Array(e.paletteIndices),s={count:r,positions:i.subarray(0,r*2),paletteIndices:o.subarray(0,r)};n.resolve({data:s,meta:{mode:"worker",durationMs:Number.isFinite(e.durationMs)?e.durationMs:Ie()-n.startMs}})}function xt(){mt=!1,ge&&(ge.removeEventListener("message",yt),ge.removeEventListener("error",xt),ge.terminate(),ge=null);for(const[,t]of _e)t.reject(new Error("worker crashed"));_e.clear()}function tr(){if(ge){ge.removeEventListener("message",yt),ge.removeEventListener("error",xt),ge.terminate(),ge=null;for(const[,t]of _e)t.reject(new Error("worker terminated"));_e.clear()}}async function sn(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return{data:null,meta:{mode:"worker",durationMs:0}};const n=on();if(!n){const u=Ie();return{data:Xe(t,e),meta:{mode:"sync",durationMs:Ie()-u}}}const r=Math.max(0,Math.min(t.count,Math.floor(t.positions.length/2),t.paletteIndices.length)),i=t.positions.slice(0,r*2),o=t.paletteIndices.slice(0,r),s=rn++,a=Ie();return new Promise((u,l)=>{_e.set(s,{kind:"data",resolve:u,reject:l,startMs:a});const h={type:"roi-clip-request",id:s,count:r,positions:i.buffer,paletteIndices:o.buffer,polygons:e??[]};n.postMessage(h,[i.buffer,o.buffer])})}async function nr(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return{indices:new Uint32Array(0),meta:{mode:"worker",durationMs:0}};const n=on();if(!n){const a=Ie();return{indices:Qt(t,e),meta:{mode:"sync",durationMs:Ie()-a}}}const r=Math.max(0,Math.min(t.count,Math.floor(t.positions.length/2),t.paletteIndices.length)),i=t.positions.slice(0,r*2),o=rn++,s=Ie();return new Promise((a,u)=>{_e.set(o,{kind:"index",resolve:a,reject:u,startMs:s});const l={type:"roi-clip-index-request",id:o,count:r,positions:i.buffer,polygons:e??[]};n.postMessage(l,[i.buffer])})}function rr(t){if(!Array.isArray(t)||t.length<3)return[];const e=t.map(i=>[Number(i[0]),Number(i[1])]),n=e[0],r=e[e.length-1];return!n||!r?[]:((n[0]!==r[0]||n[1]!==r[1])&&e.push([n[0],n[1]]),e)}function ir(t){let e=0;for(let n=0;n<t.length-1;n+=1){const[r,i]=t[n],[o,s]=t[n+1];e+=r*s-o*i}return Math.abs(e*.5)}function or(t){const e=[];for(let n=0;n<t.length;n+=1){const r=t[n];if(!r?.coordinates?.length)continue;const i=rr(r.coordinates);if(i.length<4)continue;let o=1/0,s=1/0,a=-1/0,u=-1/0;for(const[l,h]of i)l<o&&(o=l),l>a&&(a=l),h<s&&(s=h),h>u&&(u=h);!Number.isFinite(o)||!Number.isFinite(s)||!Number.isFinite(a)||!Number.isFinite(u)||e.push({regionId:r.id??n,regionIndex:n,ring:i,minX:o,minY:s,maxX:a,maxY:u,area:Math.max(1e-6,ir(i))})}return e}function sr(t,e,n){let r=!1;for(let i=0,o=n.length-1;i<n.length;o=i,i+=1){const s=n[i][0],a=n[i][1],u=n[o][0],l=n[o][1];a>e!=l>e&&t<(u-s)*(e-a)/(l-a||Number.EPSILON)+s&&(r=!r)}return r}function ar(t,e){if(Array.isArray(e)){const n=e[t];if(typeof n=="string"&&n.length>0)return n}if(e instanceof Map){const n=e.get(t);if(typeof n=="string"&&n.length>0)return n}return String(t)}function an(t,e,n={}){const r=Math.max(0,Math.min(Math.floor(t?.count??0),Math.floor((t?.positions?.length??0)/2),t?.paletteIndices?.length??0));let i=null;if(t?.drawIndices instanceof Uint32Array){const m=t.drawIndices;let g=m.length;for(let x=0;x<m.length;x+=1)m[x]<r||(g-=1);if(g===m.length)i=m;else if(g>0){const x=new Uint32Array(g);let y=0;for(let M=0;M<m.length;M+=1){const P=m[M];P>=r||(x[y]=P,y+=1)}i=x}else i=new Uint32Array(0)}const o=i?i.length:r,s=or(e??[]);if(!t||o===0||s.length===0)return{groups:[],inputPointCount:o,pointsInsideAnyRegion:0,unmatchedPointCount:o};const a=new Map,u=new Map;let l=0;for(let m=0;m<o;m+=1){const g=i?i[m]:m,x=t.positions[g*2],y=t.positions[g*2+1];let M=null;for(const A of s)x<A.minX||x>A.maxX||y<A.minY||y>A.maxY||sr(x,y,A.ring)&&(!M||A.area<M.area)&&(M=A);if(!M)continue;l+=1;const P=t.paletteIndices[g]??0,D=a.get(M.regionIndex)??new Map;D.set(P,(D.get(P)??0)+1),a.set(M.regionIndex,D),u.set(M.regionIndex,(u.get(M.regionIndex)??0)+1)}const h=n.includeEmptyRegions??!1,w=[];for(const m of s){const g=u.get(m.regionIndex)??0;if(!h&&g<=0)continue;const x=a.get(m.regionIndex)??new Map,y=Array.from(x.entries()).map(([M,P])=>({termId:ar(M,n.paletteIndexToTermId),paletteIndex:M,count:P})).sort((M,P)=>P.count-M.count||M.paletteIndex-P.paletteIndex);w.push({regionId:m.regionId,regionIndex:m.regionIndex,totalCount:g,termCounts:y})}return{groups:w,inputPointCount:o,pointsInsideAnyRegion:l,unmatchedPointCount:Math.max(0,o-l)}}function Je(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function ur(t,e){if(!e)return!1;try{const r=new URL(t,typeof window<"u"?window.location.href:void 0).hostname.toLowerCase();if(r.includes("amazonaws.com")||r.startsWith("s3.")||r.includes(".s3."))return!1}catch{}return!0}class un{constructor(e){d(this,"maxConcurrency");d(this,"maxRetries");d(this,"retryBaseDelayMs");d(this,"retryMaxDelayMs");d(this,"onTileLoad");d(this,"onTileError");d(this,"onStateChange");d(this,"authToken");d(this,"destroyed",!1);d(this,"queue",[]);d(this,"queuedByKey",new Map);d(this,"inflight",new Map);d(this,"visibleKeys",new Set);d(this,"timerId",null);d(this,"abortedCount",0);d(this,"retryCount",0);d(this,"failedCount",0);this.maxConcurrency=Math.max(1,Math.floor(e.maxConcurrency??12)),this.maxRetries=Math.max(0,Math.floor(e.maxRetries??2)),this.retryBaseDelayMs=Math.max(10,Math.floor(e.retryBaseDelayMs??120)),this.retryMaxDelayMs=Math.max(this.retryBaseDelayMs,Math.floor(e.retryMaxDelayMs??1200)),this.authToken=e.authToken??"",this.onTileLoad=e.onTileLoad,this.onTileError=e.onTileError,this.onStateChange=e.onStateChange}setAuthToken(e){this.authToken=String(e??"")}schedule(e){if(this.destroyed)return;const n=new Set;for(const r of e)n.add(r.key);this.visibleKeys=n,this.dropInvisibleQueued(n),this.abortInvisibleInflight(n);for(const r of e){if(this.inflight.has(r.key)){const s=this.inflight.get(r.key);s&&(s.tile=r);continue}const i=this.queuedByKey.get(r.key);if(i){i.tile=r;continue}const o={tile:r,attempt:0,readyAt:Je()};this.queue.push(o),this.queuedByKey.set(r.key,o)}this.sortQueue(),this.pump(),this.emitStateChange()}clear(){this.clearPumpTimer(),this.visibleKeys.clear(),this.queue=[],this.queuedByKey.clear();for(const[,e]of this.inflight)e.controller.abort();this.inflight.clear(),this.emitStateChange()}destroy(){this.destroyed||(this.destroyed=!0,this.clear())}getInflightCount(){return this.inflight.size}getSnapshot(){return{inflight:this.inflight.size,queued:this.queue.length,aborted:this.abortedCount,retries:this.retryCount,failed:this.failedCount}}dropInvisibleQueued(e){if(this.queue.length===0)return;const n=[];for(const r of this.queue){if(!e.has(r.tile.key)){this.queuedByKey.delete(r.tile.key);continue}n.push(r)}this.queue=n}abortInvisibleInflight(e){for(const[n,r]of this.inflight)e.has(n)||(this.inflight.delete(n),this.abortedCount+=1,r.controller.abort())}sortQueue(){this.queue.sort((e,n)=>e.readyAt!==n.readyAt?e.readyAt-n.readyAt:e.tile.distance2!==n.tile.distance2?e.tile.distance2-n.tile.distance2:e.tile.tier!==n.tile.tier?n.tile.tier-e.tile.tier:e.tile.key.localeCompare(n.tile.key))}pump(){if(this.destroyed)return;for(this.clearPumpTimer();this.inflight.size<this.maxConcurrency;){const r=this.takeNextReadyQueueItem();if(!r)break;this.startFetch(r)}if(this.inflight.size>=this.maxConcurrency||this.queue.length===0)return;const e=this.queue[0]?.readyAt;if(typeof e!="number")return;const n=Math.max(0,e-Je());this.timerId=window.setTimeout(()=>{this.timerId=null,this.pump()},n)}takeNextReadyQueueItem(){if(this.queue.length===0)return null;const e=Je(),n=this.queue[0];return!n||n.readyAt>e?null:(this.queue.shift(),this.queuedByKey.delete(n.tile.key),n)}startFetch(e){const n=new AbortController,r={tile:e.tile,attempt:e.attempt,controller:n};this.inflight.set(e.tile.key,r),this.emitStateChange();const i=ur(e.tile.url,this.authToken);fetch(e.tile.url,{signal:n.signal,headers:i?{Authorization:this.authToken}:void 0}).then(o=>{if(!o.ok)throw new Error(`HTTP ${o.status}`);return o.blob()}).then(o=>createImageBitmap(o)).then(o=>{if(this.destroyed||n.signal.aborted){o.close();return}if(!this.visibleKeys.has(e.tile.key)){o.close();return}this.onTileLoad(e.tile,o)}).catch(o=>{if(n.signal.aborted||this.destroyed)return;if(e.attempt<this.maxRetries&&this.visibleKeys.has(e.tile.key)){this.retryCount+=1;const a=e.attempt+1,u=this.getRetryDelay(a),l={tile:e.tile,attempt:a,readyAt:Je()+u},h=this.queuedByKey.get(e.tile.key);h?(h.tile=l.tile,h.readyAt=Math.min(h.readyAt,l.readyAt),h.attempt=Math.max(h.attempt,l.attempt)):(this.queue.push(l),this.queuedByKey.set(l.tile.key,l)),this.sortQueue();return}this.failedCount+=1,this.onTileError?.(e.tile,o,e.attempt+1)}).finally(()=>{this.inflight.delete(e.tile.key),this.pump(),this.emitStateChange()})}getRetryDelay(e){const n=Math.max(0,e-1),r=Math.min(this.retryMaxDelayMs,this.retryBaseDelayMs*2**n),i=.85+Math.random()*.3;return Math.round(r*i)}clearPumpTimer(){this.timerId!==null&&(window.clearTimeout(this.timerId),this.timerId=null)}emitStateChange(){this.onStateChange?.(this.getSnapshot())}}const Nt=.35;class cr{constructor(){d(this,"viewportWidth",1);d(this,"viewportHeight",1);d(this,"viewState",{zoom:1,offsetX:0,offsetY:0,rotationDeg:0})}setViewport(e,n){this.viewportWidth=Math.max(1,e),this.viewportHeight=Math.max(1,n)}getViewport(){return{width:this.viewportWidth,height:this.viewportHeight}}setViewState(e){typeof e.zoom=="number"&&(this.viewState.zoom=Math.max(1e-4,e.zoom)),typeof e.offsetX=="number"&&(this.viewState.offsetX=e.offsetX),typeof e.offsetY=="number"&&(this.viewState.offsetY=e.offsetY),typeof e.rotationDeg=="number"&&Number.isFinite(e.rotationDeg)&&(this.viewState.rotationDeg=e.rotationDeg)}getViewState(){return{...this.viewState}}getCenter(){const e=Math.max(1e-6,this.viewState.zoom);return[this.viewState.offsetX+this.viewportWidth/(2*e),this.viewState.offsetY+this.viewportHeight/(2*e)]}setCenter(e,n){const r=Math.max(1e-6,this.viewState.zoom);this.viewState.offsetX=e-this.viewportWidth/(2*r),this.viewState.offsetY=n-this.viewportHeight/(2*r)}screenToWorld(e,n){const r=this.viewState,i=Math.max(1e-6,r.zoom),[o,s]=this.getCenter(),a=(e-this.viewportWidth*.5)/i,u=(n-this.viewportHeight*.5)/i,l=Ye(r.rotationDeg),h=Math.cos(l),w=Math.sin(l);return[o+a*h-u*w,s+a*w+u*h]}worldToScreen(e,n){const r=this.viewState,i=Math.max(1e-6,r.zoom),[o,s]=this.getCenter(),a=e-o,u=n-s,l=Ye(r.rotationDeg),h=Math.cos(l),w=Math.sin(l),m=a*h+u*w,g=-a*w+u*h;return[this.viewportWidth*.5+m*i,this.viewportHeight*.5+g*i]}getViewCorners(){const e=this.viewportWidth,n=this.viewportHeight;return[this.screenToWorld(0,0),this.screenToWorld(e,0),this.screenToWorld(e,n),this.screenToWorld(0,n)]}getMatrix(){const e=Math.max(1e-6,this.viewState.zoom),[n,r]=this.getCenter(),i=Ye(this.viewState.rotationDeg),o=Math.cos(i),s=Math.sin(i),a=2*e*o/this.viewportWidth,u=2*e*s/this.viewportWidth,l=2*e*s/this.viewportHeight,h=-2*e*o/this.viewportHeight,w=-(a*n+u*r),m=-(l*n+h*r);return new Float32Array([a,l,0,u,h,0,w,m,1])}}function Ye(t){return t*Math.PI/180}function zt(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function Fe(t,e,n){const r=t.getUniformLocation(e,n);if(!r)throw new Error(`uniform location lookup failed: ${n}`);return r}function ut(t,e){return!t||!e?t===e:t.buffer===e.buffer&&t.byteOffset===e.byteOffset&&t.byteLength===e.byteLength}class cn{constructor(e,n,r={}){d(this,"canvas");d(this,"source");d(this,"gl");d(this,"camera",new cr);d(this,"onViewStateChange");d(this,"onStats");d(this,"onTileError");d(this,"onContextLost");d(this,"onContextRestored");d(this,"resizeObserver");d(this,"tileProgram");d(this,"pointProgram");d(this,"tileScheduler");d(this,"authToken");d(this,"destroyed",!1);d(this,"contextLost",!1);d(this,"frame",null);d(this,"frameSerial",0);d(this,"dragging",!1);d(this,"interactionMode","none");d(this,"rotateLastAngleRad",null);d(this,"pointerId",null);d(this,"lastPointerX",0);d(this,"lastPointerY",0);d(this,"interactionLocked",!1);d(this,"ctrlDragRotate",!0);d(this,"rotationDragSensitivityDegPerPixel",.35);d(this,"maxCacheTiles");d(this,"fitZoom",1);d(this,"minZoom",1e-6);d(this,"maxZoom",1);d(this,"currentTier",0);d(this,"pointCount",0);d(this,"usePointIndices",!1);d(this,"pointBuffersDirty",!0);d(this,"pointPaletteSize",1);d(this,"lastPointData",null);d(this,"lastPointPalette",null);d(this,"cache",new Map);d(this,"boundPointerDown");d(this,"boundPointerMove");d(this,"boundPointerUp");d(this,"boundWheel");d(this,"boundDoubleClick");d(this,"boundContextMenu");d(this,"boundContextLost");d(this,"boundContextRestored");this.canvas=e,this.source=n,this.onViewStateChange=r.onViewStateChange,this.onStats=r.onStats,this.onTileError=r.onTileError,this.onContextLost=r.onContextLost,this.onContextRestored=r.onContextRestored,this.authToken=r.authToken??"",this.maxCacheTiles=Math.max(32,Math.floor(r.maxCacheTiles??320)),this.ctrlDragRotate=r.ctrlDragRotate??!0,this.rotationDragSensitivityDegPerPixel=typeof r.rotationDragSensitivityDegPerPixel=="number"&&Number.isFinite(r.rotationDragSensitivityDegPerPixel)?Math.max(0,r.rotationDragSensitivityDegPerPixel):Nt;const i=e.getContext("webgl2",{alpha:!1,antialias:!1,depth:!1,stencil:!1,powerPreference:"high-performance"});if(!i)throw new Error("WebGL2 not supported");this.gl=i,this.tileProgram=this.initTileProgram(),this.pointProgram=this.initPointProgram(),this.tileScheduler=new un({authToken:this.authToken,maxConcurrency:r.tileScheduler?.maxConcurrency??12,maxRetries:r.tileScheduler?.maxRetries??2,retryBaseDelayMs:r.tileScheduler?.retryBaseDelayMs??120,retryMaxDelayMs:r.tileScheduler?.retryMaxDelayMs??1200,onTileLoad:(o,s)=>this.handleTileLoaded(o,s),onTileError:(o,s,a)=>{this.onTileError?.({tile:o,error:s,attemptCount:a}),console.warn("tile load failed",o.url,s)}}),this.resizeObserver=new ResizeObserver(()=>this.resize()),this.resizeObserver.observe(e),this.boundPointerDown=o=>this.onPointerDown(o),this.boundPointerMove=o=>this.onPointerMove(o),this.boundPointerUp=o=>this.onPointerUp(o),this.boundWheel=o=>this.onWheel(o),this.boundDoubleClick=o=>this.onDoubleClick(o),this.boundContextMenu=o=>this.onContextMenu(o),this.boundContextLost=o=>this.onWebGlContextLost(o),this.boundContextRestored=o=>this.onWebGlContextRestored(o),e.addEventListener("pointerdown",this.boundPointerDown),e.addEventListener("pointermove",this.boundPointerMove),e.addEventListener("pointerup",this.boundPointerUp),e.addEventListener("pointercancel",this.boundPointerUp),e.addEventListener("wheel",this.boundWheel,{passive:!1}),e.addEventListener("dblclick",this.boundDoubleClick),e.addEventListener("contextmenu",this.boundContextMenu),e.addEventListener("webglcontextlost",this.boundContextLost),e.addEventListener("webglcontextrestored",this.boundContextRestored),this.fitToImage(),this.resize()}setAuthToken(e){this.authToken=String(e??""),this.tileScheduler.setAuthToken(this.authToken)}setViewState(e){const n={...e};typeof n.zoom=="number"&&(n.zoom=$(n.zoom,this.minZoom,this.maxZoom)),this.camera.setViewState(n),this.clampViewState(),this.emitViewState(),this.requestRender()}getViewState(){return this.camera.getViewState()}setPointPalette(e){if(!e||e.length===0){this.lastPointPalette=null;return}if(this.lastPointPalette=new Uint8Array(e),this.contextLost||this.gl.isContextLost())return;const n=this.gl,r=Math.max(1,Math.floor(this.lastPointPalette.length/4));this.pointPaletteSize=r,n.bindTexture(n.TEXTURE_2D,this.pointProgram.paletteTexture),n.texImage2D(n.TEXTURE_2D,0,n.RGBA,r,1,0,n.RGBA,n.UNSIGNED_BYTE,this.lastPointPalette),n.bindTexture(n.TEXTURE_2D,null),this.requestRender()}setPointData(e){if(!e||!e.count||!e.positions||!e.paletteIndices){this.lastPointData=null,this.pointCount=0,this.usePointIndices=!1,this.requestRender();return}const n=Math.max(0,Math.min(e.count,Math.floor(e.positions.length/2),e.paletteIndices.length)),r=e.positions.subarray(0,n*2),i=e.paletteIndices.subarray(0,n),o=e.drawIndices instanceof Uint32Array,s=o?this.sanitizeDrawIndices(e.drawIndices,n):null,a=this.lastPointData;let u=this.pointBuffersDirty||!a||a.count!==n||!ut(a.positions,r)||!ut(a.paletteIndices,i),l=this.pointBuffersDirty||o&&(!a?.drawIndices||!ut(a.drawIndices,s))||!o&&!!a?.drawIndices;if(this.lastPointData={count:n,positions:r,paletteIndices:i,drawIndices:o?s??void 0:void 0},this.contextLost||this.gl.isContextLost())return;const h=this.gl;u&&(h.bindBuffer(h.ARRAY_BUFFER,this.pointProgram.posBuffer),h.bufferData(h.ARRAY_BUFFER,this.lastPointData.positions,h.STATIC_DRAW),h.bindBuffer(h.ARRAY_BUFFER,this.pointProgram.termBuffer),h.bufferData(h.ARRAY_BUFFER,this.lastPointData.paletteIndices,h.STATIC_DRAW),h.bindBuffer(h.ARRAY_BUFFER,null)),o&&l&&(h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,this.pointProgram.indexBuffer),h.bufferData(h.ELEMENT_ARRAY_BUFFER,s??new Uint32Array(0),h.DYNAMIC_DRAW),h.bindBuffer(h.ELEMENT_ARRAY_BUFFER,null)),this.usePointIndices=o,this.pointCount=o?s?.length??0:this.lastPointData.count,(u||l)&&(this.pointBuffersDirty=!1),this.requestRender()}sanitizeDrawIndices(e,n){if(n<=0||e.length===0)return new Uint32Array(0);let r=e.length;for(let s=0;s<e.length;s+=1)e[s]<n||(r-=1);if(r===e.length)return e;if(r<=0)return new Uint32Array(0);const i=new Uint32Array(r);let o=0;for(let s=0;s<e.length;s+=1){const a=e[s];a>=n||(i[o]=a,o+=1)}return i}setInteractionLock(e){const n=!!e;this.interactionLocked!==n&&(this.interactionLocked=n,n&&this.cancelDrag())}cancelDrag(){if(this.pointerId!==null&&this.canvas.hasPointerCapture(this.pointerId))try{this.canvas.releasePointerCapture(this.pointerId)}catch{}this.dragging=!1,this.interactionMode="none",this.rotateLastAngleRad=null,this.pointerId=null,this.canvas.classList.remove("dragging")}getPointerAngleRad(e,n){const r=this.canvas.getBoundingClientRect(),i=e-r.left-r.width*.5,o=n-r.top-r.height*.5;return Math.atan2(o,i)}screenToWorld(e,n){const r=this.canvas.getBoundingClientRect(),i=e-r.left,o=n-r.top;return this.camera.screenToWorld(i,o)}worldToScreen(e,n){return this.camera.worldToScreen(e,n)}setViewCenter(e,n){!Number.isFinite(e)||!Number.isFinite(n)||(this.camera.setCenter(e,n),this.clampViewState(),this.emitViewState(),this.requestRender())}getViewCorners(){return this.camera.getViewCorners()}resetRotation(){const e=this.camera.getViewState();Math.abs(e.rotationDeg)<1e-6||(this.camera.setViewState({rotationDeg:0}),this.clampViewState(),this.emitViewState(),this.requestRender())}getPointSizeByZoom(){const e=Math.max(1e-6,this.camera.getViewState().zoom),n=this.source.maxTierZoom+Math.log2(e),r=[[1,2.6],[2,3.1],[3,3.8],[4,4.8],[5,6.1],[6,7.4],[7,8.4],[8,9],[9,11.5],[10,14.5],[11,18],[12,22]];let i=r[0][1];for(let s=1;s<r.length;s+=1){const[a,u]=r[s-1],[l,h]=r[s];if(n<=a)break;const w=$((n-a)/Math.max(1e-6,l-a),0,1);i=u+(h-u)*w}const o=r[r.length-1];return n>o[0]&&(i+=(n-o[0])*4),$(i,2.2,36)}fitToImage(){const e=this.canvas.getBoundingClientRect(),n=Math.max(1,e.width||1),r=Math.max(1,e.height||1),i=Math.min(n/this.source.width,r/this.source.height),o=Number.isFinite(i)&&i>0?i:1;this.fitZoom=o,this.minZoom=Math.max(this.fitZoom*.5,1e-6),this.maxZoom=Math.max(1,this.fitZoom*8),this.minZoom>this.maxZoom&&(this.minZoom=this.maxZoom);const s=n/o,a=r/o;this.camera.setViewState({zoom:$(o,this.minZoom,this.maxZoom),offsetX:(this.source.width-s)*.5,offsetY:(this.source.height-a)*.5,rotationDeg:0}),this.clampViewState(),this.emitViewState(),this.requestRender()}zoomBy(e,n,r){const i=this.camera.getViewState(),o=$(i.zoom*e,this.minZoom,this.maxZoom);if(o===i.zoom)return;const[s,a]=this.camera.screenToWorld(n,r);this.camera.setViewState({zoom:o});const u=this.camera.getViewport(),l=n-u.width*.5,h=r-u.height*.5,w=Ye(this.camera.getViewState().rotationDeg),m=Math.cos(w),g=Math.sin(w),x=l/o*m-h/o*g,y=l/o*g+h/o*m;this.camera.setCenter(s-x,a-y),this.clampViewState(),this.emitViewState(),this.requestRender()}clampViewState(){const e=this.getViewBounds(),n=Math.max(1e-6,e[2]-e[0]),r=Math.max(1e-6,e[3]-e[1]),i=n*.2,o=r*.2,[s,a]=this.camera.getCenter(),u=n*.5,l=r*.5,h=u-i,w=this.source.width-u+i,m=l-o,g=this.source.height-l+o,x=h<=w?$(s,h,w):this.source.width*.5,y=m<=g?$(a,m,g):this.source.height*.5;this.camera.setCenter(x,y)}emitViewState(){this.onViewStateChange?.(this.camera.getViewState())}selectTier(){const e=Math.max(1e-6,this.camera.getViewState().zoom),n=this.source.maxTierZoom+Math.log2(e);return $(Math.floor(n),0,this.source.maxTierZoom)}getViewBounds(){const e=this.camera.getViewCorners();let n=1/0,r=1/0,i=-1/0,o=-1/0;for(const[s,a]of e)s<n&&(n=s),s>i&&(i=s),a<r&&(r=a),a>o&&(o=a);return[n,r,i,o]}intersectsBounds(e,n){return!(e[2]<=n[0]||e[0]>=n[2]||e[3]<=n[1]||e[1]>=n[3])}getVisibleTiles(){const e=this.selectTier();this.currentTier=e;const n=this.getViewBounds(),r=Math.pow(2,this.source.maxTierZoom-e),i=Math.ceil(this.source.width/r),o=Math.ceil(this.source.height/r),s=Math.max(1,Math.ceil(i/this.source.tileSize)),a=Math.max(1,Math.ceil(o/this.source.tileSize)),u=n[0],l=n[1],h=n[2],w=n[3],m=$(Math.floor(u/r/this.source.tileSize),0,s-1),g=$(Math.floor((h-1)/r/this.source.tileSize),0,s-1),x=$(Math.floor(l/r/this.source.tileSize),0,a-1),y=$(Math.floor((w-1)/r/this.source.tileSize),0,a-1);if(m>g||x>y)return[];const M=(u+h)*.5/r/this.source.tileSize,P=(l+w)*.5/r/this.source.tileSize,D=[];for(let A=x;A<=y;A+=1)for(let W=m;W<=g;W+=1){const be=W*this.source.tileSize*r,re=A*this.source.tileSize*r,Y=Math.min((W+1)*this.source.tileSize,i)*r,Ee=Math.min((A+1)*this.source.tileSize,o)*r,he=W-M,ae=A-P;D.push({key:`${e}/${W}/${A}`,tier:e,x:W,y:A,bounds:[be,re,Y,Ee],distance2:he*he+ae*ae,url:wt(this.source,e,W,A)})}return D.sort((A,W)=>A.distance2-W.distance2),D}trimCache(){if(this.cache.size<=this.maxCacheTiles)return;const e=Array.from(this.cache.entries());e.sort((r,i)=>r[1].lastUsed-i[1].lastUsed);const n=this.cache.size-this.maxCacheTiles;for(let r=0;r<n;r+=1){const[i,o]=e[r];this.gl.deleteTexture(o.texture),this.cache.delete(i)}}render(){if(this.destroyed||this.contextLost||this.gl.isContextLost())return;const e=zt();this.frameSerial+=1;const n=this.gl,r=this.tileProgram,i=this.pointProgram;n.clearColor(.03,.06,.1,1),n.clear(n.COLOR_BUFFER_BIT);const o=this.getVisibleTiles(),s=this.getViewBounds(),a=new Set(o.map(m=>m.key));n.useProgram(r.program),n.bindVertexArray(r.vao),n.uniformMatrix3fv(r.uCamera,!1,this.camera.getMatrix()),n.uniform1i(r.uTexture,0);const u=[];for(const[,m]of this.cache)a.has(m.key)||this.intersectsBounds(m.bounds,s)&&u.push(m);u.sort((m,g)=>m.tier-g.tier);for(const m of u)m.lastUsed=this.frameSerial,n.activeTexture(n.TEXTURE0),n.bindTexture(n.TEXTURE_2D,m.texture),n.uniform4f(r.uBounds,m.bounds[0],m.bounds[1],m.bounds[2],m.bounds[3]),n.drawArrays(n.TRIANGLE_STRIP,0,4);let l=0;const h=[];for(const m of o){const g=this.cache.get(m.key);if(!g){h.push(m);continue}g.lastUsed=this.frameSerial,n.activeTexture(n.TEXTURE0),n.bindTexture(n.TEXTURE_2D,g.texture),n.uniform4f(r.uBounds,g.bounds[0],g.bounds[1],g.bounds[2],g.bounds[3]),n.drawArrays(n.TRIANGLE_STRIP,0,4),l+=1}this.tileScheduler.schedule(h),n.bindTexture(n.TEXTURE_2D,null),n.bindVertexArray(null);let w=0;if(this.pointCount>0&&(n.enable(n.BLEND),n.blendFunc(n.ONE,n.ONE_MINUS_SRC_ALPHA),n.useProgram(i.program),n.bindVertexArray(i.vao),n.uniformMatrix3fv(i.uCamera,!1,this.camera.getMatrix()),n.uniform1f(i.uPointSize,this.getPointSizeByZoom()),n.uniform1f(i.uPaletteSize,this.pointPaletteSize),n.uniform1i(i.uPalette,1),n.activeTexture(n.TEXTURE1),n.bindTexture(n.TEXTURE_2D,i.paletteTexture),this.usePointIndices?n.drawElements(n.POINTS,this.pointCount,n.UNSIGNED_INT,0):n.drawArrays(n.POINTS,0,this.pointCount),n.bindTexture(n.TEXTURE_2D,null),n.bindVertexArray(null),w=this.pointCount),this.onStats){const m=this.tileScheduler.getSnapshot(),g=l,x=h.length,y=u.length+l+(w>0?1:0);this.onStats({tier:this.currentTier,visible:o.length,rendered:l,points:w,fallback:u.length,cache:this.cache.size,inflight:m.inflight,queued:m.queued,retries:m.retries,failed:m.failed,aborted:m.aborted,cacheHits:g,cacheMisses:x,drawCalls:y,frameMs:zt()-e})}}requestRender(){this.frame!==null||this.destroyed||this.contextLost||this.gl.isContextLost()||(this.frame=requestAnimationFrame(()=>{this.frame=null,this.render()}))}resize(){const e=this.canvas.getBoundingClientRect(),n=Math.max(1,e.width||this.canvas.clientWidth||1),r=Math.max(1,e.height||this.canvas.clientHeight||1),i=Math.max(1,window.devicePixelRatio||1),o=Math.max(1,Math.round(n*i)),s=Math.max(1,Math.round(r*i));(this.canvas.width!==o||this.canvas.height!==s)&&(this.canvas.width=o,this.canvas.height=s),this.camera.setViewport(n,r),this.gl.viewport(0,0,o,s),this.requestRender()}onPointerDown(e){if(this.interactionLocked)return;const n=this.ctrlDragRotate&&(e.ctrlKey||e.metaKey);(e.button===0||n&&e.button===2)&&(n&&e.preventDefault(),this.dragging=!0,this.interactionMode=n?"rotate":"pan",this.pointerId=e.pointerId,this.lastPointerX=e.clientX,this.lastPointerY=e.clientY,this.rotateLastAngleRad=this.interactionMode==="rotate"?this.getPointerAngleRad(e.clientX,e.clientY):null,this.canvas.classList.add("dragging"),this.canvas.setPointerCapture(e.pointerId))}onPointerMove(e){if(this.interactionLocked||!this.dragging||e.pointerId!==this.pointerId)return;const n=e.clientX-this.lastPointerX,r=e.clientY-this.lastPointerY;if(this.lastPointerX=e.clientX,this.lastPointerY=e.clientY,this.interactionMode==="rotate"){const i=this.getPointerAngleRad(e.clientX,e.clientY),o=this.rotateLastAngleRad;if(this.rotateLastAngleRad=i,o!==null){const s=i-o,a=Math.atan2(Math.sin(s),Math.cos(s)),u=this.rotationDragSensitivityDegPerPixel/Nt,l=this.camera.getViewState();this.camera.setViewState({rotationDeg:l.rotationDeg-a*180/Math.PI*u})}}else{const i=this.camera.getViewState(),o=Math.max(1e-6,i.zoom),s=Ye(i.rotationDeg),a=Math.cos(s),u=Math.sin(s),l=(n*a-r*u)/o,h=(n*u+r*a)/o;this.camera.setViewState({offsetX:i.offsetX-l,offsetY:i.offsetY-h})}this.clampViewState(),this.emitViewState(),this.requestRender()}onPointerUp(e){this.interactionLocked||e.pointerId===this.pointerId&&this.cancelDrag()}onWheel(e){if(this.interactionLocked){e.preventDefault();return}e.preventDefault();const n=this.canvas.getBoundingClientRect(),r=e.clientX-n.left,i=e.clientY-n.top,o=e.deltaY<0?1.12:.89;this.zoomBy(o,r,i)}onDoubleClick(e){if(this.interactionLocked)return;const n=this.canvas.getBoundingClientRect(),r=e.clientX-n.left,i=e.clientY-n.top;this.zoomBy(e.shiftKey?.8:1.25,r,i)}onContextMenu(e){(this.dragging||e.ctrlKey||e.metaKey)&&e.preventDefault()}onWebGlContextLost(e){e.preventDefault(),!(this.destroyed||this.contextLost)&&(this.contextLost=!0,this.pointBuffersDirty=!0,this.frame!==null&&(cancelAnimationFrame(this.frame),this.frame=null),this.cancelDrag(),this.tileScheduler.clear(),this.cache.clear(),this.onContextLost?.())}onWebGlContextRestored(e){this.destroyed||(this.contextLost=!1,this.cache.clear(),this.tileProgram=this.initTileProgram(),this.pointProgram=this.initPointProgram(),this.pointBuffersDirty=!0,this.lastPointPalette&&this.lastPointPalette.length>0&&this.setPointPalette(this.lastPointPalette),this.lastPointData?this.setPointData(this.lastPointData):this.pointCount=0,this.resize(),this.requestRender(),this.onContextRestored?.())}destroy(){if(!this.destroyed){if(this.destroyed=!0,this.frame!==null&&(cancelAnimationFrame(this.frame),this.frame=null),this.resizeObserver.disconnect(),this.canvas.removeEventListener("pointerdown",this.boundPointerDown),this.canvas.removeEventListener("pointermove",this.boundPointerMove),this.canvas.removeEventListener("pointerup",this.boundPointerUp),this.canvas.removeEventListener("pointercancel",this.boundPointerUp),this.canvas.removeEventListener("wheel",this.boundWheel),this.canvas.removeEventListener("dblclick",this.boundDoubleClick),this.canvas.removeEventListener("contextmenu",this.boundContextMenu),this.canvas.removeEventListener("webglcontextlost",this.boundContextLost),this.canvas.removeEventListener("webglcontextrestored",this.boundContextRestored),this.cancelDrag(),this.tileScheduler.destroy(),!this.contextLost&&!this.gl.isContextLost()){for(const[,e]of this.cache)this.gl.deleteTexture(e.texture);this.gl.deleteBuffer(this.tileProgram.vbo),this.gl.deleteVertexArray(this.tileProgram.vao),this.gl.deleteProgram(this.tileProgram.program),this.gl.deleteBuffer(this.pointProgram.posBuffer),this.gl.deleteBuffer(this.pointProgram.termBuffer),this.gl.deleteBuffer(this.pointProgram.indexBuffer),this.gl.deleteTexture(this.pointProgram.paletteTexture),this.gl.deleteVertexArray(this.pointProgram.vao),this.gl.deleteProgram(this.pointProgram.program)}this.cache.clear()}}initTileProgram(){const e=this.gl,i=At(e,`#version 300 es
63
+ `;function Qn(){if(typeof navigator>"u")return!1;const t=navigator;return typeof t.gpu=="object"&&t.gpu!==null}function on(){if(!Qn())return null;const e=navigator.gpu;if(!e||typeof e!="object")return null;const n=e;return typeof n.requestAdapter!="function"?null:n}const je=globalThis.GPUShaderStage?.COMPUTE??4,at=globalThis.GPUBufferUsage?.STORAGE??128,Ke=globalThis.GPUBufferUsage?.COPY_DST??8,er=globalThis.GPUBufferUsage?.COPY_SRC??4,tr=globalThis.GPUBufferUsage?.UNIFORM??64,nr=globalThis.GPUBufferUsage?.MAP_READ??1,rr=globalThis.GPUMapMode?.READ??1;async function ir(){const t=on();if(!t)return{supported:!1,features:[]};const e=await t.requestAdapter();return e?{supported:!0,adapterName:e.info?.description??e.info?.vendor??"unknown",features:Array.from(e.features),limits:{maxStorageBufferBindingSize:Number(e.limits.maxStorageBufferBindingSize),maxComputeInvocationsPerWorkgroup:Number(e.limits.maxComputeInvocationsPerWorkgroup),maxComputeWorkgroupSizeX:Number(e.limits.maxComputeWorkgroupSizeX)}}:{supported:!1,features:[]}}async function or(){return Ze||(Ze=(async()=>{const t=on();if(!t)return null;const e=await t.requestAdapter();if(!e)return null;const n=await e.requestDevice(),r=n.createBindGroupLayout({entries:[{binding:0,visibility:je,buffer:{type:"read-only-storage"}},{binding:1,visibility:je,buffer:{type:"read-only-storage"}},{binding:2,visibility:je,buffer:{type:"storage"}},{binding:3,visibility:je,buffer:{type:"uniform"}}]}),i=n.createComputePipeline({layout:n.createPipelineLayout({bindGroupLayouts:[r]}),compute:{module:n.createShaderModule({code:Jn}),entryPoint:"main"}});return{device:n,pipeline:i,bindGroupLayout:r}})(),Ze)}function Je(t,e){return Math.ceil(t/e)*e}async function sn(t,e,n){const r=await or();if(!r)return null;const i=Math.max(0,Math.floor(e)),o=Math.max(0,Math.floor(n.length/4));if(i===0||o===0)return new Uint32Array(0);const s=Math.min(i,Math.floor(t.length/2));if(s===0)return new Uint32Array(0);const a=s*2*Float32Array.BYTES_PER_ELEMENT,u=o*4*Float32Array.BYTES_PER_ELEMENT,c=s*Uint32Array.BYTES_PER_ELEMENT,l=Number(r.device.limits.maxStorageBufferBindingSize);if(a>l||u>l||c>l)return null;const w=r.device.createBuffer({size:Je(a,4),usage:at|Ke}),m=r.device.createBuffer({size:Je(u,4),usage:at|Ke}),g=r.device.createBuffer({size:Je(c,4),usage:at|er}),T=r.device.createBuffer({size:16,usage:tr|Ke}),x=r.device.createBuffer({size:Je(c,4),usage:Ke|nr});r.device.queue.writeBuffer(w,0,t.buffer,t.byteOffset,a),r.device.queue.writeBuffer(m,0,n.buffer,n.byteOffset,u),r.device.queue.writeBuffer(T,0,new Uint32Array([s,o,0,0]));const C=r.device.createBindGroup({layout:r.bindGroupLayout,entries:[{binding:0,resource:{buffer:w}},{binding:1,resource:{buffer:m}},{binding:2,resource:{buffer:g}},{binding:3,resource:{buffer:T}}]}),P=r.device.createCommandEncoder(),F=P.beginComputePass();F.setPipeline(r.pipeline),F.setBindGroup(0,C),F.dispatchWorkgroups(Math.ceil(s/256)),F.end(),P.copyBufferToBuffer(g,0,x,0,c),r.device.queue.submit([P.finish()]),await x.mapAsync(rr);const I=x.getMappedRange(),X=new Uint32Array(I.slice(0));return x.unmap(),w.destroy(),m.destroy(),g.destroy(),T.destroy(),x.destroy(),X}function Se(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function sr(t){if(!Array.isArray(t)||t.length<3)return[];const e=t.map(([i,o])=>[i,o]),n=e[0],r=e[e.length-1];return!n||!r?[]:((n[0]!==r[0]||n[1]!==r[1])&&e.push([n[0],n[1]]),e)}function ar(t){const e=[];for(const n of t??[]){const r=sr(n);if(r.length<4)continue;let i=1/0,o=1/0,s=-1/0,a=-1/0;for(const[u,c]of r)u<i&&(i=u),u>s&&(s=u),c<o&&(o=c),c>a&&(a=c);!Number.isFinite(i)||!Number.isFinite(o)||e.push({ring:r,minX:i,minY:o,maxX:s,maxY:a})}return e}function ur(t,e,n){let r=!1;for(let i=0,o=n.length-1;i<n.length;o=i,i+=1){const s=n[i][0],a=n[i][1],u=n[o][0],c=n[o][1];a>e!=c>e&&t<(u-s)*(e-a)/(c-a||Number.EPSILON)+s&&(r=!r)}return r}function Yt(t,e,n){for(const r of n)if(!(t<r.minX||t>r.maxX||e<r.minY||e>r.maxY)&&ur(t,e,r.ring))return!0;return!1}async function an(t,e,n={}){const r=Se(),i=n.bridgeToDraw===!0;if(!t||!t.count||!t.positions||!t.paletteIndices)return{data:null,meta:{mode:"hybrid-webgpu",durationMs:Se()-r,usedWebGpu:!1,candidateCount:0,bridgedToDraw:!1}};const o=ar(e??[]);if(o.length===0)return{data:{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)},meta:{mode:"hybrid-webgpu",durationMs:Se()-r,usedWebGpu:!1,candidateCount:0,bridgedToDraw:!1}};const s=Math.max(0,Math.min(t.count,Math.floor(t.positions.length/2),t.paletteIndices.length));if(s===0)return{data:{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)},meta:{mode:"hybrid-webgpu",durationMs:Se()-r,usedWebGpu:!1,candidateCount:0,bridgedToDraw:!1}};const a=new Float32Array(o.length*4);for(let x=0;x<o.length;x+=1){const C=x*4,P=o[x];a[C]=P.minX,a[C+1]=P.minY,a[C+2]=P.maxX,a[C+3]=P.maxY}let u=null,c=!1;try{u=await sn(t.positions,s,a),c=!!u}catch{u=null,c=!1}if(!u)return{data:Oe(t,e),meta:{mode:"hybrid-webgpu",durationMs:Se()-r,usedWebGpu:!1,candidateCount:s,bridgedToDraw:!1}};let l=0;for(let x=0;x<s;x+=1)u[x]===1&&(l+=1);const w=new Uint32Array(l);if(l>0){let x=0;for(let C=0;C<s;C+=1)u[C]===1&&(w[x]=C,x+=1)}if(l===0)return i?{data:{count:s,positions:t.positions.subarray(0,s*2),paletteIndices:t.paletteIndices.subarray(0,s),drawIndices:new Uint32Array(0)},meta:{mode:"hybrid-webgpu",durationMs:Se()-r,usedWebGpu:!0,candidateCount:0,bridgedToDraw:!0}}:{data:{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)},meta:{mode:"hybrid-webgpu",durationMs:Se()-r,usedWebGpu:!0,candidateCount:0,bridgedToDraw:!1}};if(i){const x=new Uint32Array(l);let C=0;for(let P=0;P<l;P+=1){const F=w[P]??0,I=t.positions[F*2],X=t.positions[F*2+1];Yt(I,X,o)&&(x[C]=F,C+=1)}return{data:{count:s,positions:t.positions.subarray(0,s*2),paletteIndices:t.paletteIndices.subarray(0,s),drawIndices:x.subarray(0,C)},meta:{mode:"hybrid-webgpu",durationMs:Se()-r,usedWebGpu:!0,candidateCount:l,bridgedToDraw:!0}}}const m=new Float32Array(l*2),g=new Uint16Array(l);let T=0;for(let x=0;x<l;x+=1){const C=w[x]??0,P=t.positions[C*2],F=t.positions[C*2+1];Yt(P,F,o)&&(m[T*2]=P,m[T*2+1]=F,g[T]=t.paletteIndices[C],T+=1)}return{data:{count:T,positions:m.subarray(0,T*2),paletteIndices:g.subarray(0,T)},meta:{mode:"hybrid-webgpu",durationMs:Se()-r,usedWebGpu:!0,candidateCount:l,bridgedToDraw:!1}}}let ge=null,gt=!0,un=1;const Le=new Map;function Fe(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function cn(){if(!gt)return null;if(ge)return ge;try{const t=new Worker(new URL(""+(typeof document>"u"?require("url").pathToFileURL(__dirname+"/assets/roi-clip-worker-DdVYCepx.js").href:new URL("assets/roi-clip-worker-DdVYCepx.js",document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"&&document.currentScript.src||document.baseURI).href),typeof document>"u"?require("url").pathToFileURL(__filename).href:nt&&nt.tagName.toUpperCase()==="SCRIPT"&&nt.src||new URL("index.cjs",document.baseURI).href),{type:"module"});return t.addEventListener("message",xt),t.addEventListener("error",Tt),ge=t,t}catch{return gt=!1,null}}function xt(t){const e=t.data;if(!e)return;const n=Le.get(e.id);if(!n)return;if(Le.delete(e.id),e.type==="roi-clip-failure"){n.reject(new Error(e.error||"worker clip failed"));return}if(e.type==="roi-clip-index-success"){if(n.kind!=="index"){n.reject(new Error("worker response mismatch: expected point data result"));return}const a=Math.max(0,Math.floor(e.count)),u=new Uint32Array(e.indices).subarray(0,a);n.resolve({indices:u,meta:{mode:"worker",durationMs:Number.isFinite(e.durationMs)?e.durationMs:Fe()-n.startMs}});return}if(n.kind!=="data"){n.reject(new Error("worker response mismatch: expected index result"));return}const r=Math.max(0,Math.floor(e.count)),i=new Float32Array(e.positions),o=new Uint16Array(e.paletteIndices),s={count:r,positions:i.subarray(0,r*2),paletteIndices:o.subarray(0,r)};n.resolve({data:s,meta:{mode:"worker",durationMs:Number.isFinite(e.durationMs)?e.durationMs:Fe()-n.startMs}})}function Tt(){gt=!1,ge&&(ge.removeEventListener("message",xt),ge.removeEventListener("error",Tt),ge.terminate(),ge=null);for(const[,t]of Le)t.reject(new Error("worker crashed"));Le.clear()}function cr(){if(ge){ge.removeEventListener("message",xt),ge.removeEventListener("error",Tt),ge.terminate(),ge=null;for(const[,t]of Le)t.reject(new Error("worker terminated"));Le.clear()}}async function ln(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return{data:null,meta:{mode:"worker",durationMs:0}};const n=cn();if(!n){const u=Fe();return{data:Oe(t,e),meta:{mode:"sync",durationMs:Fe()-u}}}const r=Math.max(0,Math.min(t.count,Math.floor(t.positions.length/2),t.paletteIndices.length)),i=t.positions.slice(0,r*2),o=t.paletteIndices.slice(0,r),s=un++,a=Fe();return new Promise((u,c)=>{Le.set(s,{kind:"data",resolve:u,reject:c,startMs:a});const l={type:"roi-clip-request",id:s,count:r,positions:i.buffer,paletteIndices:o.buffer,polygons:e??[]};n.postMessage(l,[i.buffer,o.buffer])})}async function lr(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return{indices:new Uint32Array(0),meta:{mode:"worker",durationMs:0}};const n=cn();if(!n){const a=Fe();return{indices:rn(t,e),meta:{mode:"sync",durationMs:Fe()-a}}}const r=Math.max(0,Math.min(t.count,Math.floor(t.positions.length/2),t.paletteIndices.length)),i=t.positions.slice(0,r*2),o=un++,s=Fe();return new Promise((a,u)=>{Le.set(o,{kind:"index",resolve:a,reject:u,startMs:s});const c={type:"roi-clip-index-request",id:o,count:r,positions:i.buffer,polygons:e??[]};n.postMessage(c,[i.buffer])})}function hr(t){if(!Array.isArray(t)||t.length<3)return[];const e=t.map(i=>[Number(i[0]),Number(i[1])]),n=e[0],r=e[e.length-1];return!n||!r?[]:((n[0]!==r[0]||n[1]!==r[1])&&e.push([n[0],n[1]]),e)}function fr(t){let e=0;for(let n=0;n<t.length-1;n+=1){const[r,i]=t[n],[o,s]=t[n+1];e+=r*s-o*i}return Math.abs(e*.5)}function dr(t){const e=[];for(let n=0;n<t.length;n+=1){const r=t[n];if(!r?.coordinates?.length)continue;const i=hr(r.coordinates);if(i.length<4)continue;let o=1/0,s=1/0,a=-1/0,u=-1/0;for(const[c,l]of i)c<o&&(o=c),c>a&&(a=c),l<s&&(s=l),l>u&&(u=l);!Number.isFinite(o)||!Number.isFinite(s)||!Number.isFinite(a)||!Number.isFinite(u)||e.push({regionId:r.id??n,regionIndex:n,ring:i,minX:o,minY:s,maxX:a,maxY:u,area:Math.max(1e-6,fr(i))})}return e}function mr(t,e,n){let r=!1;for(let i=0,o=n.length-1;i<n.length;o=i,i+=1){const s=n[i][0],a=n[i][1],u=n[o][0],c=n[o][1];a>e!=c>e&&t<(u-s)*(e-a)/(c-a||Number.EPSILON)+s&&(r=!r)}return r}function gr(t,e){if(Array.isArray(e)){const n=e[t];if(typeof n=="string"&&n.length>0)return n}if(e instanceof Map){const n=e.get(t);if(typeof n=="string"&&n.length>0)return n}return String(t)}function hn(t,e,n={}){const r=Math.max(0,Math.min(Math.floor(t?.count??0),Math.floor((t?.positions?.length??0)/2),t?.paletteIndices?.length??0));let i=null;if(t?.drawIndices instanceof Uint32Array){const m=t.drawIndices;let g=m.length;for(let T=0;T<m.length;T+=1)m[T]<r||(g-=1);if(g===m.length)i=m;else if(g>0){const T=new Uint32Array(g);let x=0;for(let C=0;C<m.length;C+=1){const P=m[C];P>=r||(T[x]=P,x+=1)}i=T}else i=new Uint32Array(0)}const o=i?i.length:r,s=dr(e??[]);if(!t||o===0||s.length===0)return{groups:[],inputPointCount:o,pointsInsideAnyRegion:0,unmatchedPointCount:o};const a=new Map,u=new Map;let c=0;for(let m=0;m<o;m+=1){const g=i?i[m]:m,T=t.positions[g*2],x=t.positions[g*2+1];let C=null;for(const I of s)T<I.minX||T>I.maxX||x<I.minY||x>I.maxY||mr(T,x,I.ring)&&(!C||I.area<C.area)&&(C=I);if(!C)continue;c+=1;const P=t.paletteIndices[g]??0,F=a.get(C.regionIndex)??new Map;F.set(P,(F.get(P)??0)+1),a.set(C.regionIndex,F),u.set(C.regionIndex,(u.get(C.regionIndex)??0)+1)}const l=n.includeEmptyRegions??!1,w=[];for(const m of s){const g=u.get(m.regionIndex)??0;if(!l&&g<=0)continue;const T=a.get(m.regionIndex)??new Map,x=Array.from(T.entries()).map(([C,P])=>({termId:gr(C,n.paletteIndexToTermId),paletteIndex:C,count:P})).sort((C,P)=>P.count-C.count||C.paletteIndex-P.paletteIndex);w.push({regionId:m.regionId,regionIndex:m.regionIndex,totalCount:g,termCounts:x})}return{groups:w,inputPointCount:o,pointsInsideAnyRegion:c,unmatchedPointCount:Math.max(0,o-c)}}function Qe(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function pr(t,e){if(!e)return!1;try{const r=new URL(t,typeof window<"u"?window.location.href:void 0).hostname.toLowerCase();if(r.includes("amazonaws.com")||r.startsWith("s3.")||r.includes(".s3."))return!1}catch{}return!0}class fn{constructor(e){d(this,"maxConcurrency");d(this,"maxRetries");d(this,"retryBaseDelayMs");d(this,"retryMaxDelayMs");d(this,"onTileLoad");d(this,"onTileError");d(this,"onStateChange");d(this,"authToken");d(this,"destroyed",!1);d(this,"queue",[]);d(this,"queuedByKey",new Map);d(this,"inflight",new Map);d(this,"visibleKeys",new Set);d(this,"timerId",null);d(this,"abortedCount",0);d(this,"retryCount",0);d(this,"failedCount",0);this.maxConcurrency=Math.max(1,Math.floor(e.maxConcurrency??12)),this.maxRetries=Math.max(0,Math.floor(e.maxRetries??2)),this.retryBaseDelayMs=Math.max(10,Math.floor(e.retryBaseDelayMs??120)),this.retryMaxDelayMs=Math.max(this.retryBaseDelayMs,Math.floor(e.retryMaxDelayMs??1200)),this.authToken=e.authToken??"",this.onTileLoad=e.onTileLoad,this.onTileError=e.onTileError,this.onStateChange=e.onStateChange}setAuthToken(e){this.authToken=String(e??"")}schedule(e){if(this.destroyed)return;const n=new Set;for(const r of e)n.add(r.key);this.visibleKeys=n,this.dropInvisibleQueued(n),this.abortInvisibleInflight(n);for(const r of e){if(this.inflight.has(r.key)){const s=this.inflight.get(r.key);s&&(s.tile=r);continue}const i=this.queuedByKey.get(r.key);if(i){i.tile=r;continue}const o={tile:r,attempt:0,readyAt:Qe()};this.queue.push(o),this.queuedByKey.set(r.key,o)}this.sortQueue(),this.pump(),this.emitStateChange()}clear(){this.clearPumpTimer(),this.visibleKeys.clear(),this.queue=[],this.queuedByKey.clear();for(const[,e]of this.inflight)e.controller.abort();this.inflight.clear(),this.emitStateChange()}destroy(){this.destroyed||(this.destroyed=!0,this.clear())}getInflightCount(){return this.inflight.size}getSnapshot(){return{inflight:this.inflight.size,queued:this.queue.length,aborted:this.abortedCount,retries:this.retryCount,failed:this.failedCount}}dropInvisibleQueued(e){if(this.queue.length===0)return;const n=[];for(const r of this.queue){if(!e.has(r.tile.key)){this.queuedByKey.delete(r.tile.key);continue}n.push(r)}this.queue=n}abortInvisibleInflight(e){for(const[n,r]of this.inflight)e.has(n)||(this.inflight.delete(n),this.abortedCount+=1,r.controller.abort())}sortQueue(){this.queue.sort((e,n)=>e.readyAt!==n.readyAt?e.readyAt-n.readyAt:e.tile.distance2!==n.tile.distance2?e.tile.distance2-n.tile.distance2:e.tile.tier!==n.tile.tier?n.tile.tier-e.tile.tier:e.tile.key.localeCompare(n.tile.key))}pump(){if(this.destroyed)return;for(this.clearPumpTimer();this.inflight.size<this.maxConcurrency;){const r=this.takeNextReadyQueueItem();if(!r)break;this.startFetch(r)}if(this.inflight.size>=this.maxConcurrency||this.queue.length===0)return;const e=this.queue[0]?.readyAt;if(typeof e!="number")return;const n=Math.max(0,e-Qe());this.timerId=window.setTimeout(()=>{this.timerId=null,this.pump()},n)}takeNextReadyQueueItem(){if(this.queue.length===0)return null;const e=Qe(),n=this.queue[0];return!n||n.readyAt>e?null:(this.queue.shift(),this.queuedByKey.delete(n.tile.key),n)}startFetch(e){const n=new AbortController,r={tile:e.tile,attempt:e.attempt,controller:n};this.inflight.set(e.tile.key,r),this.emitStateChange();const i=pr(e.tile.url,this.authToken);fetch(e.tile.url,{signal:n.signal,headers:i?{Authorization:this.authToken}:void 0}).then(o=>{if(!o.ok)throw new Error(`HTTP ${o.status}`);return o.blob()}).then(o=>createImageBitmap(o)).then(o=>{if(this.destroyed||n.signal.aborted){o.close();return}if(!this.visibleKeys.has(e.tile.key)){o.close();return}this.onTileLoad(e.tile,o)}).catch(o=>{if(n.signal.aborted||this.destroyed)return;if(e.attempt<this.maxRetries&&this.visibleKeys.has(e.tile.key)){this.retryCount+=1;const a=e.attempt+1,u=this.getRetryDelay(a),c={tile:e.tile,attempt:a,readyAt:Qe()+u},l=this.queuedByKey.get(e.tile.key);l?(l.tile=c.tile,l.readyAt=Math.min(l.readyAt,c.readyAt),l.attempt=Math.max(l.attempt,c.attempt)):(this.queue.push(c),this.queuedByKey.set(c.tile.key,c)),this.sortQueue();return}this.failedCount+=1,this.onTileError?.(e.tile,o,e.attempt+1)}).finally(()=>{this.inflight.delete(e.tile.key),this.pump(),this.emitStateChange()})}getRetryDelay(e){const n=Math.max(0,e-1),r=Math.min(this.retryMaxDelayMs,this.retryBaseDelayMs*2**n),i=.85+Math.random()*.3;return Math.round(r*i)}clearPumpTimer(){this.timerId!==null&&(window.clearTimeout(this.timerId),this.timerId=null)}emitStateChange(){this.onStateChange?.(this.getSnapshot())}}const Wt=.35;class br{constructor(){d(this,"viewportWidth",1);d(this,"viewportHeight",1);d(this,"viewState",{zoom:1,offsetX:0,offsetY:0,rotationDeg:0})}setViewport(e,n){this.viewportWidth=Math.max(1,e),this.viewportHeight=Math.max(1,n)}getViewport(){return{width:this.viewportWidth,height:this.viewportHeight}}setViewState(e){typeof e.zoom=="number"&&(this.viewState.zoom=Math.max(1e-4,e.zoom)),typeof e.offsetX=="number"&&(this.viewState.offsetX=e.offsetX),typeof e.offsetY=="number"&&(this.viewState.offsetY=e.offsetY),typeof e.rotationDeg=="number"&&Number.isFinite(e.rotationDeg)&&(this.viewState.rotationDeg=e.rotationDeg)}getViewState(){return{...this.viewState}}getCenter(){const e=Math.max(1e-6,this.viewState.zoom);return[this.viewState.offsetX+this.viewportWidth/(2*e),this.viewState.offsetY+this.viewportHeight/(2*e)]}setCenter(e,n){const r=Math.max(1e-6,this.viewState.zoom);this.viewState.offsetX=e-this.viewportWidth/(2*r),this.viewState.offsetY=n-this.viewportHeight/(2*r)}screenToWorld(e,n){const r=this.viewState,i=Math.max(1e-6,r.zoom),[o,s]=this.getCenter(),a=(e-this.viewportWidth*.5)/i,u=(n-this.viewportHeight*.5)/i,c=Ve(r.rotationDeg),l=Math.cos(c),w=Math.sin(c);return[o+a*l-u*w,s+a*w+u*l]}worldToScreen(e,n){const r=this.viewState,i=Math.max(1e-6,r.zoom),[o,s]=this.getCenter(),a=e-o,u=n-s,c=Ve(r.rotationDeg),l=Math.cos(c),w=Math.sin(c),m=a*l+u*w,g=-a*w+u*l;return[this.viewportWidth*.5+m*i,this.viewportHeight*.5+g*i]}getViewCorners(){const e=this.viewportWidth,n=this.viewportHeight;return[this.screenToWorld(0,0),this.screenToWorld(e,0),this.screenToWorld(e,n),this.screenToWorld(0,n)]}getMatrix(){const e=Math.max(1e-6,this.viewState.zoom),[n,r]=this.getCenter(),i=Ve(this.viewState.rotationDeg),o=Math.cos(i),s=Math.sin(i),a=2*e*o/this.viewportWidth,u=2*e*s/this.viewportWidth,c=2*e*s/this.viewportHeight,l=-2*e*o/this.viewportHeight,w=-(a*n+u*r),m=-(c*n+l*r);return new Float32Array([a,c,0,u,l,0,w,m,1])}}function Ve(t){return t*Math.PI/180}function Xt(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function ke(t,e,n){const r=t.getUniformLocation(e,n);if(!r)throw new Error(`uniform location lookup failed: ${n}`);return r}function ut(t,e){return!t||!e?t===e:t.buffer===e.buffer&&t.byteOffset===e.byteOffset&&t.byteLength===e.byteLength}class dn{constructor(e,n,r={}){d(this,"canvas");d(this,"source");d(this,"gl");d(this,"camera",new br);d(this,"onViewStateChange");d(this,"onStats");d(this,"onTileError");d(this,"onContextLost");d(this,"onContextRestored");d(this,"resizeObserver");d(this,"tileProgram");d(this,"pointProgram");d(this,"tileScheduler");d(this,"authToken");d(this,"destroyed",!1);d(this,"contextLost",!1);d(this,"frame",null);d(this,"frameSerial",0);d(this,"dragging",!1);d(this,"interactionMode","none");d(this,"rotateLastAngleRad",null);d(this,"pointerId",null);d(this,"lastPointerX",0);d(this,"lastPointerY",0);d(this,"interactionLocked",!1);d(this,"ctrlDragRotate",!0);d(this,"rotationDragSensitivityDegPerPixel",.35);d(this,"maxCacheTiles");d(this,"fitZoom",1);d(this,"minZoom",1e-6);d(this,"maxZoom",1);d(this,"currentTier",0);d(this,"pointCount",0);d(this,"usePointIndices",!1);d(this,"pointBuffersDirty",!0);d(this,"pointPaletteSize",1);d(this,"lastPointData",null);d(this,"lastPointPalette",null);d(this,"cache",new Map);d(this,"boundPointerDown");d(this,"boundPointerMove");d(this,"boundPointerUp");d(this,"boundWheel");d(this,"boundDoubleClick");d(this,"boundContextMenu");d(this,"boundContextLost");d(this,"boundContextRestored");this.canvas=e,this.source=n,this.onViewStateChange=r.onViewStateChange,this.onStats=r.onStats,this.onTileError=r.onTileError,this.onContextLost=r.onContextLost,this.onContextRestored=r.onContextRestored,this.authToken=r.authToken??"",this.maxCacheTiles=Math.max(32,Math.floor(r.maxCacheTiles??320)),this.ctrlDragRotate=r.ctrlDragRotate??!0,this.rotationDragSensitivityDegPerPixel=typeof r.rotationDragSensitivityDegPerPixel=="number"&&Number.isFinite(r.rotationDragSensitivityDegPerPixel)?Math.max(0,r.rotationDragSensitivityDegPerPixel):Wt;const i=e.getContext("webgl2",{alpha:!1,antialias:!1,depth:!1,stencil:!1,powerPreference:"high-performance"});if(!i)throw new Error("WebGL2 not supported");this.gl=i,this.tileProgram=this.initTileProgram(),this.pointProgram=this.initPointProgram(),this.tileScheduler=new fn({authToken:this.authToken,maxConcurrency:r.tileScheduler?.maxConcurrency??12,maxRetries:r.tileScheduler?.maxRetries??2,retryBaseDelayMs:r.tileScheduler?.retryBaseDelayMs??120,retryMaxDelayMs:r.tileScheduler?.retryMaxDelayMs??1200,onTileLoad:(o,s)=>this.handleTileLoaded(o,s),onTileError:(o,s,a)=>{this.onTileError?.({tile:o,error:s,attemptCount:a}),console.warn("tile load failed",o.url,s)}}),this.resizeObserver=new ResizeObserver(()=>this.resize()),this.resizeObserver.observe(e),this.boundPointerDown=o=>this.onPointerDown(o),this.boundPointerMove=o=>this.onPointerMove(o),this.boundPointerUp=o=>this.onPointerUp(o),this.boundWheel=o=>this.onWheel(o),this.boundDoubleClick=o=>this.onDoubleClick(o),this.boundContextMenu=o=>this.onContextMenu(o),this.boundContextLost=o=>this.onWebGlContextLost(o),this.boundContextRestored=o=>this.onWebGlContextRestored(o),e.addEventListener("pointerdown",this.boundPointerDown),e.addEventListener("pointermove",this.boundPointerMove),e.addEventListener("pointerup",this.boundPointerUp),e.addEventListener("pointercancel",this.boundPointerUp),e.addEventListener("wheel",this.boundWheel,{passive:!1}),e.addEventListener("dblclick",this.boundDoubleClick),e.addEventListener("contextmenu",this.boundContextMenu),e.addEventListener("webglcontextlost",this.boundContextLost),e.addEventListener("webglcontextrestored",this.boundContextRestored),this.fitToImage(),this.resize()}setAuthToken(e){this.authToken=String(e??""),this.tileScheduler.setAuthToken(this.authToken)}setViewState(e){const n={...e};typeof n.zoom=="number"&&(n.zoom=H(n.zoom,this.minZoom,this.maxZoom)),this.camera.setViewState(n),this.clampViewState(),this.emitViewState(),this.requestRender()}getViewState(){return this.camera.getViewState()}setPointPalette(e){if(!e||e.length===0){this.lastPointPalette=null;return}if(this.lastPointPalette=new Uint8Array(e),this.contextLost||this.gl.isContextLost())return;const n=this.gl,r=Math.max(1,Math.floor(this.lastPointPalette.length/4));this.pointPaletteSize=r,n.bindTexture(n.TEXTURE_2D,this.pointProgram.paletteTexture),n.texImage2D(n.TEXTURE_2D,0,n.RGBA,r,1,0,n.RGBA,n.UNSIGNED_BYTE,this.lastPointPalette),n.bindTexture(n.TEXTURE_2D,null),this.requestRender()}setPointData(e){if(!e||!e.count||!e.positions||!e.paletteIndices){this.lastPointData=null,this.pointCount=0,this.usePointIndices=!1,this.requestRender();return}const n=Math.max(0,Math.min(e.count,Math.floor(e.positions.length/2),e.paletteIndices.length)),r=e.positions.subarray(0,n*2),i=e.paletteIndices.subarray(0,n),o=e.drawIndices instanceof Uint32Array,s=o?this.sanitizeDrawIndices(e.drawIndices,n):null,a=this.lastPointData;let u=this.pointBuffersDirty||!a||a.count!==n||!ut(a.positions,r)||!ut(a.paletteIndices,i),c=this.pointBuffersDirty||o&&(!a?.drawIndices||!ut(a.drawIndices,s))||!o&&!!a?.drawIndices;if(this.lastPointData={count:n,positions:r,paletteIndices:i,drawIndices:o?s??void 0:void 0},this.contextLost||this.gl.isContextLost())return;const l=this.gl;u&&(l.bindBuffer(l.ARRAY_BUFFER,this.pointProgram.posBuffer),l.bufferData(l.ARRAY_BUFFER,this.lastPointData.positions,l.STATIC_DRAW),l.bindBuffer(l.ARRAY_BUFFER,this.pointProgram.termBuffer),l.bufferData(l.ARRAY_BUFFER,this.lastPointData.paletteIndices,l.STATIC_DRAW),l.bindBuffer(l.ARRAY_BUFFER,null)),o&&c&&(l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,this.pointProgram.indexBuffer),l.bufferData(l.ELEMENT_ARRAY_BUFFER,s??new Uint32Array(0),l.DYNAMIC_DRAW),l.bindBuffer(l.ELEMENT_ARRAY_BUFFER,null)),this.usePointIndices=o,this.pointCount=o?s?.length??0:this.lastPointData.count,(u||c)&&(this.pointBuffersDirty=!1),this.requestRender()}sanitizeDrawIndices(e,n){if(n<=0||e.length===0)return new Uint32Array(0);let r=e.length;for(let s=0;s<e.length;s+=1)e[s]<n||(r-=1);if(r===e.length)return e;if(r<=0)return new Uint32Array(0);const i=new Uint32Array(r);let o=0;for(let s=0;s<e.length;s+=1){const a=e[s];a>=n||(i[o]=a,o+=1)}return i}setInteractionLock(e){const n=!!e;this.interactionLocked!==n&&(this.interactionLocked=n,n&&this.cancelDrag())}cancelDrag(){if(this.pointerId!==null&&this.canvas.hasPointerCapture(this.pointerId))try{this.canvas.releasePointerCapture(this.pointerId)}catch{}this.dragging=!1,this.interactionMode="none",this.rotateLastAngleRad=null,this.pointerId=null,this.canvas.classList.remove("dragging")}getPointerAngleRad(e,n){const r=this.canvas.getBoundingClientRect(),i=e-r.left-r.width*.5,o=n-r.top-r.height*.5;return Math.atan2(o,i)}screenToWorld(e,n){const r=this.canvas.getBoundingClientRect(),i=e-r.left,o=n-r.top;return this.camera.screenToWorld(i,o)}worldToScreen(e,n){return this.camera.worldToScreen(e,n)}setViewCenter(e,n){!Number.isFinite(e)||!Number.isFinite(n)||(this.camera.setCenter(e,n),this.clampViewState(),this.emitViewState(),this.requestRender())}getViewCorners(){return this.camera.getViewCorners()}resetRotation(){const e=this.camera.getViewState();Math.abs(e.rotationDeg)<1e-6||(this.camera.setViewState({rotationDeg:0}),this.clampViewState(),this.emitViewState(),this.requestRender())}getPointSizeByZoom(){const e=Math.max(1e-6,this.camera.getViewState().zoom),n=this.source.maxTierZoom+Math.log2(e),r=[[1,2.6],[2,3.1],[3,3.8],[4,4.8],[5,6.1],[6,7.4],[7,8.4],[8,9],[9,11.5],[10,14.5],[11,18],[12,22]];let i=r[0][1];for(let s=1;s<r.length;s+=1){const[a,u]=r[s-1],[c,l]=r[s];if(n<=a)break;const w=H((n-a)/Math.max(1e-6,c-a),0,1);i=u+(l-u)*w}const o=r[r.length-1];return n>o[0]&&(i+=(n-o[0])*4),H(i,2.2,36)}fitToImage(){const e=this.canvas.getBoundingClientRect(),n=Math.max(1,e.width||1),r=Math.max(1,e.height||1),i=Math.min(n/this.source.width,r/this.source.height),o=Number.isFinite(i)&&i>0?i:1;this.fitZoom=o,this.minZoom=Math.max(this.fitZoom*.5,1e-6),this.maxZoom=Math.max(1,this.fitZoom*8),this.minZoom>this.maxZoom&&(this.minZoom=this.maxZoom);const s=n/o,a=r/o;this.camera.setViewState({zoom:H(o,this.minZoom,this.maxZoom),offsetX:(this.source.width-s)*.5,offsetY:(this.source.height-a)*.5,rotationDeg:0}),this.clampViewState(),this.emitViewState(),this.requestRender()}zoomBy(e,n,r){const i=this.camera.getViewState(),o=H(i.zoom*e,this.minZoom,this.maxZoom);if(o===i.zoom)return;const[s,a]=this.camera.screenToWorld(n,r);this.camera.setViewState({zoom:o});const u=this.camera.getViewport(),c=n-u.width*.5,l=r-u.height*.5,w=Ve(this.camera.getViewState().rotationDeg),m=Math.cos(w),g=Math.sin(w),T=c/o*m-l/o*g,x=c/o*g+l/o*m;this.camera.setCenter(s-T,a-x),this.clampViewState(),this.emitViewState(),this.requestRender()}clampViewState(){const e=this.getViewBounds(),n=Math.max(1e-6,e[2]-e[0]),r=Math.max(1e-6,e[3]-e[1]),i=n*.2,o=r*.2,[s,a]=this.camera.getCenter(),u=n*.5,c=r*.5,l=u-i,w=this.source.width-u+i,m=c-o,g=this.source.height-c+o,T=l<=w?H(s,l,w):this.source.width*.5,x=m<=g?H(a,m,g):this.source.height*.5;this.camera.setCenter(T,x)}emitViewState(){this.onViewStateChange?.(this.camera.getViewState())}selectTier(){const e=Math.max(1e-6,this.camera.getViewState().zoom),n=this.source.maxTierZoom+Math.log2(e);return H(Math.floor(n),0,this.source.maxTierZoom)}getViewBounds(){const e=this.camera.getViewCorners();let n=1/0,r=1/0,i=-1/0,o=-1/0;for(const[s,a]of e)s<n&&(n=s),s>i&&(i=s),a<r&&(r=a),a>o&&(o=a);return[n,r,i,o]}intersectsBounds(e,n){return!(e[2]<=n[0]||e[0]>=n[2]||e[3]<=n[1]||e[1]>=n[3])}getVisibleTiles(){const e=this.selectTier();this.currentTier=e;const n=this.getViewBounds(),r=Math.pow(2,this.source.maxTierZoom-e),i=Math.ceil(this.source.width/r),o=Math.ceil(this.source.height/r),s=Math.max(1,Math.ceil(i/this.source.tileSize)),a=Math.max(1,Math.ceil(o/this.source.tileSize)),u=n[0],c=n[1],l=n[2],w=n[3],m=H(Math.floor(u/r/this.source.tileSize),0,s-1),g=H(Math.floor((l-1)/r/this.source.tileSize),0,s-1),T=H(Math.floor(c/r/this.source.tileSize),0,a-1),x=H(Math.floor((w-1)/r/this.source.tileSize),0,a-1);if(m>g||T>x)return[];const C=(u+l)*.5/r/this.source.tileSize,P=(c+w)*.5/r/this.source.tileSize,F=[];for(let I=T;I<=x;I+=1)for(let X=m;X<=g;X+=1){const ye=X*this.source.tileSize*r,se=I*this.source.tileSize*r,V=Math.min((X+1)*this.source.tileSize,i)*r,Ae=Math.min((I+1)*this.source.tileSize,o)*r,le=X-C,ae=I-P;F.push({key:`${e}/${X}/${I}`,tier:e,x:X,y:I,bounds:[ye,se,V,Ae],distance2:le*le+ae*ae,url:yt(this.source,e,X,I)})}return F.sort((I,X)=>I.distance2-X.distance2),F}trimCache(){if(this.cache.size<=this.maxCacheTiles)return;const e=Array.from(this.cache.entries());e.sort((r,i)=>r[1].lastUsed-i[1].lastUsed);const n=this.cache.size-this.maxCacheTiles;for(let r=0;r<n;r+=1){const[i,o]=e[r];this.gl.deleteTexture(o.texture),this.cache.delete(i)}}render(){if(this.destroyed||this.contextLost||this.gl.isContextLost())return;const e=Xt();this.frameSerial+=1;const n=this.gl,r=this.tileProgram,i=this.pointProgram;n.clearColor(.03,.06,.1,1),n.clear(n.COLOR_BUFFER_BIT);const o=this.getVisibleTiles(),s=this.getViewBounds(),a=new Set(o.map(m=>m.key));n.useProgram(r.program),n.bindVertexArray(r.vao),n.uniformMatrix3fv(r.uCamera,!1,this.camera.getMatrix()),n.uniform1i(r.uTexture,0);const u=[];for(const[,m]of this.cache)a.has(m.key)||this.intersectsBounds(m.bounds,s)&&u.push(m);u.sort((m,g)=>m.tier-g.tier);for(const m of u)m.lastUsed=this.frameSerial,n.activeTexture(n.TEXTURE0),n.bindTexture(n.TEXTURE_2D,m.texture),n.uniform4f(r.uBounds,m.bounds[0],m.bounds[1],m.bounds[2],m.bounds[3]),n.drawArrays(n.TRIANGLE_STRIP,0,4);let c=0;const l=[];for(const m of o){const g=this.cache.get(m.key);if(!g){l.push(m);continue}g.lastUsed=this.frameSerial,n.activeTexture(n.TEXTURE0),n.bindTexture(n.TEXTURE_2D,g.texture),n.uniform4f(r.uBounds,g.bounds[0],g.bounds[1],g.bounds[2],g.bounds[3]),n.drawArrays(n.TRIANGLE_STRIP,0,4),c+=1}this.tileScheduler.schedule(l),n.bindTexture(n.TEXTURE_2D,null),n.bindVertexArray(null);let w=0;if(this.pointCount>0&&(n.enable(n.BLEND),n.blendFunc(n.ONE,n.ONE_MINUS_SRC_ALPHA),n.useProgram(i.program),n.bindVertexArray(i.vao),n.uniformMatrix3fv(i.uCamera,!1,this.camera.getMatrix()),n.uniform1f(i.uPointSize,this.getPointSizeByZoom()),n.uniform1f(i.uPaletteSize,this.pointPaletteSize),n.uniform1i(i.uPalette,1),n.activeTexture(n.TEXTURE1),n.bindTexture(n.TEXTURE_2D,i.paletteTexture),this.usePointIndices?n.drawElements(n.POINTS,this.pointCount,n.UNSIGNED_INT,0):n.drawArrays(n.POINTS,0,this.pointCount),n.bindTexture(n.TEXTURE_2D,null),n.bindVertexArray(null),w=this.pointCount),this.onStats){const m=this.tileScheduler.getSnapshot(),g=c,T=l.length,x=u.length+c+(w>0?1:0);this.onStats({tier:this.currentTier,visible:o.length,rendered:c,points:w,fallback:u.length,cache:this.cache.size,inflight:m.inflight,queued:m.queued,retries:m.retries,failed:m.failed,aborted:m.aborted,cacheHits:g,cacheMisses:T,drawCalls:x,frameMs:Xt()-e})}}requestRender(){this.frame!==null||this.destroyed||this.contextLost||this.gl.isContextLost()||(this.frame=requestAnimationFrame(()=>{this.frame=null,this.render()}))}resize(){const e=this.canvas.getBoundingClientRect(),n=Math.max(1,e.width||this.canvas.clientWidth||1),r=Math.max(1,e.height||this.canvas.clientHeight||1),i=Math.max(1,window.devicePixelRatio||1),o=Math.max(1,Math.round(n*i)),s=Math.max(1,Math.round(r*i));(this.canvas.width!==o||this.canvas.height!==s)&&(this.canvas.width=o,this.canvas.height=s),this.camera.setViewport(n,r),this.gl.viewport(0,0,o,s),this.requestRender()}onPointerDown(e){if(this.interactionLocked)return;const n=this.ctrlDragRotate&&(e.ctrlKey||e.metaKey);(e.button===0||n&&e.button===2)&&(n&&e.preventDefault(),this.dragging=!0,this.interactionMode=n?"rotate":"pan",this.pointerId=e.pointerId,this.lastPointerX=e.clientX,this.lastPointerY=e.clientY,this.rotateLastAngleRad=this.interactionMode==="rotate"?this.getPointerAngleRad(e.clientX,e.clientY):null,this.canvas.classList.add("dragging"),this.canvas.setPointerCapture(e.pointerId))}onPointerMove(e){if(this.interactionLocked||!this.dragging||e.pointerId!==this.pointerId)return;const n=e.clientX-this.lastPointerX,r=e.clientY-this.lastPointerY;if(this.lastPointerX=e.clientX,this.lastPointerY=e.clientY,this.interactionMode==="rotate"){const i=this.getPointerAngleRad(e.clientX,e.clientY),o=this.rotateLastAngleRad;if(this.rotateLastAngleRad=i,o!==null){const s=i-o,a=Math.atan2(Math.sin(s),Math.cos(s)),u=this.rotationDragSensitivityDegPerPixel/Wt,c=this.camera.getViewState();this.camera.setViewState({rotationDeg:c.rotationDeg-a*180/Math.PI*u})}}else{const i=this.camera.getViewState(),o=Math.max(1e-6,i.zoom),s=Ve(i.rotationDeg),a=Math.cos(s),u=Math.sin(s),c=(n*a-r*u)/o,l=(n*u+r*a)/o;this.camera.setViewState({offsetX:i.offsetX-c,offsetY:i.offsetY-l})}this.clampViewState(),this.emitViewState(),this.requestRender()}onPointerUp(e){this.interactionLocked||e.pointerId===this.pointerId&&this.cancelDrag()}onWheel(e){if(this.interactionLocked){e.preventDefault();return}e.preventDefault();const n=this.canvas.getBoundingClientRect(),r=e.clientX-n.left,i=e.clientY-n.top,o=e.deltaY<0?1.12:.89;this.zoomBy(o,r,i)}onDoubleClick(e){if(this.interactionLocked)return;const n=this.canvas.getBoundingClientRect(),r=e.clientX-n.left,i=e.clientY-n.top;this.zoomBy(e.shiftKey?.8:1.25,r,i)}onContextMenu(e){(this.dragging||e.ctrlKey||e.metaKey)&&e.preventDefault()}onWebGlContextLost(e){e.preventDefault(),!(this.destroyed||this.contextLost)&&(this.contextLost=!0,this.pointBuffersDirty=!0,this.frame!==null&&(cancelAnimationFrame(this.frame),this.frame=null),this.cancelDrag(),this.tileScheduler.clear(),this.cache.clear(),this.onContextLost?.())}onWebGlContextRestored(e){this.destroyed||(this.contextLost=!1,this.cache.clear(),this.tileProgram=this.initTileProgram(),this.pointProgram=this.initPointProgram(),this.pointBuffersDirty=!0,this.lastPointPalette&&this.lastPointPalette.length>0&&this.setPointPalette(this.lastPointPalette),this.lastPointData?this.setPointData(this.lastPointData):this.pointCount=0,this.resize(),this.requestRender(),this.onContextRestored?.())}destroy(){if(!this.destroyed){if(this.destroyed=!0,this.frame!==null&&(cancelAnimationFrame(this.frame),this.frame=null),this.resizeObserver.disconnect(),this.canvas.removeEventListener("pointerdown",this.boundPointerDown),this.canvas.removeEventListener("pointermove",this.boundPointerMove),this.canvas.removeEventListener("pointerup",this.boundPointerUp),this.canvas.removeEventListener("pointercancel",this.boundPointerUp),this.canvas.removeEventListener("wheel",this.boundWheel),this.canvas.removeEventListener("dblclick",this.boundDoubleClick),this.canvas.removeEventListener("contextmenu",this.boundContextMenu),this.canvas.removeEventListener("webglcontextlost",this.boundContextLost),this.canvas.removeEventListener("webglcontextrestored",this.boundContextRestored),this.cancelDrag(),this.tileScheduler.destroy(),!this.contextLost&&!this.gl.isContextLost()){for(const[,e]of this.cache)this.gl.deleteTexture(e.texture);this.gl.deleteBuffer(this.tileProgram.vbo),this.gl.deleteVertexArray(this.tileProgram.vao),this.gl.deleteProgram(this.tileProgram.program),this.gl.deleteBuffer(this.pointProgram.posBuffer),this.gl.deleteBuffer(this.pointProgram.termBuffer),this.gl.deleteBuffer(this.pointProgram.indexBuffer),this.gl.deleteTexture(this.pointProgram.paletteTexture),this.gl.deleteVertexArray(this.pointProgram.vao),this.gl.deleteProgram(this.pointProgram.program)}this.cache.clear()}}initTileProgram(){const e=this.gl,i=It(e,`#version 300 es
64
64
  precision highp float;
65
65
  in vec2 aUnit;
66
66
  in vec2 aUv;
@@ -82,7 +82,7 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
82
82
  out vec4 outColor;
83
83
  void main() {
84
84
  outColor = texture(uTexture, vUv);
85
- }`),o=Fe(e,i,"uCamera"),s=Fe(e,i,"uBounds"),a=Fe(e,i,"uTexture"),u=e.createVertexArray(),l=e.createBuffer();if(!u||!l)throw new Error("buffer allocation failed");e.bindVertexArray(u),e.bindBuffer(e.ARRAY_BUFFER,l),e.bufferData(e.ARRAY_BUFFER,new Float32Array([0,0,0,0,1,0,1,0,0,1,0,1,1,1,1,1]),e.STATIC_DRAW);const h=e.getAttribLocation(i,"aUnit"),w=e.getAttribLocation(i,"aUv");if(h<0||w<0)throw new Error("tile attribute lookup failed");return e.enableVertexAttribArray(h),e.enableVertexAttribArray(w),e.vertexAttribPointer(h,2,e.FLOAT,!1,16,0),e.vertexAttribPointer(w,2,e.FLOAT,!1,16,8),e.bindVertexArray(null),e.bindBuffer(e.ARRAY_BUFFER,null),{program:i,vao:u,vbo:l,uCamera:o,uBounds:s,uTexture:a}}initPointProgram(){const e=this.gl,i=At(e,`#version 300 es
85
+ }`),o=ke(e,i,"uCamera"),s=ke(e,i,"uBounds"),a=ke(e,i,"uTexture"),u=e.createVertexArray(),c=e.createBuffer();if(!u||!c)throw new Error("buffer allocation failed");e.bindVertexArray(u),e.bindBuffer(e.ARRAY_BUFFER,c),e.bufferData(e.ARRAY_BUFFER,new Float32Array([0,0,0,0,1,0,1,0,0,1,0,1,1,1,1,1]),e.STATIC_DRAW);const l=e.getAttribLocation(i,"aUnit"),w=e.getAttribLocation(i,"aUv");if(l<0||w<0)throw new Error("tile attribute lookup failed");return e.enableVertexAttribArray(l),e.enableVertexAttribArray(w),e.vertexAttribPointer(l,2,e.FLOAT,!1,16,0),e.vertexAttribPointer(w,2,e.FLOAT,!1,16,8),e.bindVertexArray(null),e.bindBuffer(e.ARRAY_BUFFER,null),{program:i,vao:u,vbo:c,uCamera:o,uBounds:s,uTexture:a}}initPointProgram(){const e=this.gl,i=It(e,`#version 300 es
86
86
  precision highp float;
87
87
  in vec2 aPosition;
88
88
  in uint aTerm;
@@ -121,6 +121,6 @@ fn main(@builtin(global_invocation_id) gid: vec3<u32>) {
121
121
  if (alpha <= 0.001) discard;
122
122
 
123
123
  outColor = vec4(color.rgb * alpha, alpha);
124
- }`),o=Fe(e,i,"uCamera"),s=Fe(e,i,"uPointSize"),a=Fe(e,i,"uPalette"),u=Fe(e,i,"uPaletteSize"),l=e.createVertexArray(),h=e.createBuffer(),w=e.createBuffer(),m=e.createBuffer(),g=e.createTexture();if(!l||!h||!w||!m||!g)throw new Error("point buffer allocation failed");e.bindVertexArray(l),e.bindBuffer(e.ARRAY_BUFFER,h),e.bufferData(e.ARRAY_BUFFER,0,e.DYNAMIC_DRAW);const x=e.getAttribLocation(i,"aPosition");if(x<0)throw new Error("point position attribute not found");e.enableVertexAttribArray(x),e.vertexAttribPointer(x,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,w),e.bufferData(e.ARRAY_BUFFER,0,e.DYNAMIC_DRAW);const y=e.getAttribLocation(i,"aTerm");if(y<0)throw new Error("point term attribute not found");return e.enableVertexAttribArray(y),e.vertexAttribIPointer(y,1,e.UNSIGNED_SHORT,0,0),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,m),e.bufferData(e.ELEMENT_ARRAY_BUFFER,0,e.DYNAMIC_DRAW),e.bindVertexArray(null),e.bindBuffer(e.ARRAY_BUFFER,null),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,null),e.bindTexture(e.TEXTURE_2D,g),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,1,1,0,e.RGBA,e.UNSIGNED_BYTE,new Uint8Array([160,160,160,255])),e.bindTexture(e.TEXTURE_2D,null),{program:i,vao:l,posBuffer:h,termBuffer:w,indexBuffer:m,paletteTexture:g,uCamera:o,uPointSize:s,uPalette:a,uPaletteSize:u}}handleTileLoaded(e,n){if(this.destroyed||this.contextLost||this.gl.isContextLost()){n.close();return}if(this.cache.has(e.key)){n.close();return}const r=this.createTextureFromBitmap(n);n.close(),r&&(this.cache.set(e.key,{key:e.key,texture:r,bounds:e.bounds,tier:e.tier,lastUsed:this.frameSerial}),this.trimCache(),this.requestRender())}createTextureFromBitmap(e){if(this.contextLost||this.gl.isContextLost())return null;const n=this.gl,r=n.createTexture();return r?(n.bindTexture(n.TEXTURE_2D,r),n.pixelStorei(n.UNPACK_FLIP_Y_WEBGL,1),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texImage2D(n.TEXTURE_2D,0,n.RGBA,n.RGBA,n.UNSIGNED_BYTE,e),n.bindTexture(n.TEXTURE_2D,null),r):null}}const ct=[],lr=[],hr={count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)};function gt(t,e){return t.id??e}function fr(t,e){if(!Array.isArray(e)||e.length<3)return!1;const[n,r]=t;let i=!1;for(let o=0,s=e.length-1;o<e.length;s=o++){const[a,u]=e[o],[l,h]=e[s];u>r!=h>r&&n<(l-a)*(r-u)/Math.max(1e-12,h-u)+a&&(i=!i)}return i}function Wt(t,e){for(let n=e.length-1;n>=0;n-=1){const r=e[n];if(r?.coordinates?.length&&fr(t,r.coordinates))return{region:r,regionIndex:n,regionId:gt(r,n)}}return null}function dr({source:t,viewState:e,onViewStateChange:n,onStats:r,onTileError:i,onContextLost:o,onContextRestored:s,debugOverlay:a=!1,debugOverlayStyle:u,fitNonce:l=0,rotationResetNonce:h=0,authToken:w="",ctrlDragRotate:m=!0,pointData:g=null,pointPalette:x=null,roiRegions:y,roiPolygons:M,clipPointsToRois:P=!1,clipMode:D="worker",onClipStats:A,onRoiPointGroups:W,roiPaletteIndexToTermId:be,interactionLock:re=!1,drawTool:Y="cursor",stampOptions:Ee,regionStrokeStyle:he,regionStrokeHoverStyle:ae,regionStrokeActiveStyle:Ce,patchStrokeStyle:Z,resolveRegionStrokeStyle:ee,overlayShapes:X,customLayers:ue,patchRegions:Pe,regionLabelStyle:ie,onPointerWorldMove:T,onRegionHover:v,onRegionClick:S,onActiveRegionChange:N,onDrawComplete:V,onPatchComplete:fe,showOverviewMap:K=!1,overviewMapOptions:G,className:J,style:oe}){const se=f.useRef(null),F=f.useRef(null),C=f.useRef(null),z=f.useRef(null),q=f.useRef(n),j=f.useRef(r),ce=f.useRef(a),[Be,xe]=f.useState(!0),[Te,pe]=f.useState(null),[c,p]=f.useState(null),[E,B]=f.useState(null),[R,I]=f.useState(null),_=f.useRef(null),O=f.useRef(0),te=y??ct,ne=Pe??ct,ve=M??lr,Se=(ue?.length??0)>0,ze=f.useMemo(()=>({position:"relative",width:"100%",height:"100%",...oe}),[oe]),et=f.useMemo(()=>({position:"absolute",top:8,left:8,zIndex:7,margin:0,padding:"8px 10px",maxWidth:"min(420px, 80%)",pointerEvents:"none",whiteSpace:"pre-wrap",lineHeight:1.35,fontFamily:"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",fontSize:11,color:"#cde6ff",background:"rgba(6, 12, 20, 0.82)",border:"1px solid rgba(173, 216, 255, 0.28)",borderRadius:8,boxShadow:"0 8px 22px rgba(0,0,0,0.35)",...u}),[u]),le=f.useMemo(()=>te.length>0?te:ve.length===0?ct:ve.map((b,U)=>({id:U,coordinates:b})),[te,ve]),Ue=f.useMemo(()=>le.map(b=>b.coordinates),[le]),[Ve,Oe]=f.useState(g);f.useEffect(()=>{const b=++O.current;let U=!1;if(!P)return Oe(g),()=>{U=!0};if(!g||!g.count||!g.positions||!g.paletteIndices)return Oe(null),()=>{U=!0};if(Ue.length===0)return Oe(hr),A?.({mode:D,durationMs:0,inputCount:g.count,outputCount:0,polygonCount:0}),()=>{U=!0};const k=(L,Q)=>{if(U||b!==O.current)return;const tt=L?.drawIndices?L.drawIndices.length:L?.count??0;Oe(L),A?.({mode:Q.mode,durationMs:Q.durationMs,inputCount:g.count,outputCount:tt,polygonCount:Ue.length,usedWebGpu:Q.usedWebGpu,candidateCount:Q.candidateCount,bridgedToDraw:Q.bridgedToDraw})};return(async()=>{if(D==="sync"){const L=performance.now(),Q=Xe(g,Ue);k(Q,{mode:"sync",durationMs:performance.now()-L});return}if(D==="hybrid-webgpu"){const L=await nn(g,Ue,{bridgeToDraw:!0});k(L.data,{mode:L.meta.mode,durationMs:L.meta.durationMs,usedWebGpu:L.meta.usedWebGpu,candidateCount:L.meta.candidateCount,bridgedToDraw:L.meta.bridgedToDraw});return}try{const L=await sn(g,Ue);k(L.data,{mode:L.meta.mode,durationMs:L.meta.durationMs})}catch{const L=performance.now(),Q=Xe(g,Ue);k(Q,{mode:"sync",durationMs:performance.now()-L})}})(),()=>{U=!0}},[P,D,g,Ue,A]),f.useMemo(()=>{const b=Number(G?.width??220);return Number.isFinite(b)?Math.max(64,b):220},[G?.width]);const Tt=f.useMemo(()=>{const b=Number(G?.height??140);return Number.isFinite(b)?Math.max(48,b):140},[G?.height]),Re=f.useMemo(()=>{const b=Number(G?.margin??16);return Number.isFinite(b)?Math.max(0,b):16},[G?.margin]),Ge=G?.position||"bottom-right",Le=f.useCallback(b=>{p(U=>String(U)===String(b)?U:(N?.(b),b))},[N]);f.useEffect(()=>{q.current=n},[n]),f.useEffect(()=>{j.current=r},[r]),f.useEffect(()=>{ce.current=a,a||I(null)},[a]);const vt=f.useCallback(b=>{j.current?.(b),ce.current&&I(b)},[]),ln=f.useMemo(()=>R?[`tier ${R.tier} | frame ${R.frameMs?.toFixed(2)??"-"} ms | drawCalls ${R.drawCalls??"-"}`,`tiles visible ${R.visible} | rendered ${R.rendered} | fallback ${R.fallback}`,`cache size ${R.cache} | hit ${R.cacheHits??"-"} | miss ${R.cacheMisses??"-"}`,`queue inflight ${R.inflight} | queued ${R.queued??"-"} | retries ${R.retries??"-"} | failed ${R.failed??"-"} | aborted ${R.aborted??"-"}`,`points ${R.points}`].join(`
125
- `):"stats: waiting for first frame...",[R]);f.useEffect(()=>{!(c===null?!0:le.some((H,L)=>String(gt(H,L))===String(c)))&&c!==null&&Le(null);const U=_.current;!(U===null?!0:le.some((H,L)=>String(gt(H,L))===String(U)))&&U!==null&&(_.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[le,c,v,Le]);const Mt=f.useCallback(b=>{Se&&B(b);const U=q.current;U&&U(b),C.current?.(),z.current?.()},[Se]);f.useEffect(()=>{if(!K){xe(!1);return}xe(!0)},[K,t?.id]),f.useEffect(()=>{Y!=="cursor"&&_.current!==null&&(_.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[Y,v]);const De=f.useCallback((b,U)=>{const k=F.current;if(!k)return null;const H=k.screenToWorld(b,U);if(!Array.isArray(H)||H.length<2)return null;const L=Number(H[0]),Q=Number(H[1]);return!Number.isFinite(L)||!Number.isFinite(Q)?null:[L,Q]},[]),Et=f.useCallback((b,U)=>{const k=F.current;if(!k)return null;const H=k.worldToScreen(b,U);if(!Array.isArray(H)||H.length<2)return null;const L=Number(H[0]),Q=Number(H[1]);return!Number.isFinite(L)||!Number.isFinite(Q)?null:[L,Q]},[]),Ct=f.useCallback(()=>{F.current?.requestRender(),C.current?.(),z.current?.()},[]),Pt=f.useMemo(()=>E??F.current?.getViewState()??null,[E]),St=f.useMemo(()=>{if(!t)return null;const b=Pt;return b?{source:t,viewState:b,drawTool:Y,interactionLock:re,worldToScreen:Et,screenToWorld:De,requestRedraw:Ct}:null},[t,Pt,Y,re,Et,De,Ct]),hn=f.useCallback(b=>{const U=b.target===se.current,k=De(b.clientX,b.clientY);if(T){const tt=!!k&&k[0]>=0&&k[1]>=0&&!!t&&k[0]<=t.width&&k[1]<=t.height;T({coordinate:k,clientX:b.clientX,clientY:b.clientY,insideImage:tt})}if(Y!=="cursor")return;if(!U){_.current!==null&&(_.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}));return}if(!k||!le.length)return;const H=Wt(k,le),L=H?.regionId??null,Q=_.current;String(Q)!==String(L)&&(_.current=L,pe(L),v?.({region:H?.region??null,regionId:L,regionIndex:H?.regionIndex??-1,coordinate:k}))},[Y,le,De,v,T,t]),fn=f.useCallback(()=>{T?.({coordinate:null,clientX:-1,clientY:-1,insideImage:!1}),_.current!==null&&(_.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[v,T]),dn=f.useCallback(b=>{if(Y!=="cursor"||b.target!==se.current)return;if(!le.length){Le(null);return}const U=De(b.clientX,b.clientY);if(!U)return;const k=Wt(U,le);if(!k){Le(null);return}const H=c!==null&&String(c)===String(k.regionId)?null:k.regionId;Le(H),S?.({region:k.region,regionId:k.regionId,regionIndex:k.regionIndex,coordinate:U})},[Y,le,De,S,c,Le]);return f.useEffect(()=>{const b=se.current;if(!b||!t)return;const U=new cn(b,t,{onViewStateChange:Mt,onStats:vt,onTileError:i,onContextLost:o,onContextRestored:s,authToken:w,ctrlDragRotate:m});return F.current=U,e&&U.setViewState(e),U.setInteractionLock(re),Se&&B(U.getViewState()),()=>{U.destroy(),F.current=null}},[t,vt,i,o,s,w,m,Mt,Se]),f.useEffect(()=>{const b=F.current;!b||!e||b.setViewState(e)},[e]),f.useEffect(()=>{const b=F.current;b&&b.fitToImage()},[l]),f.useEffect(()=>{const b=F.current;b&&b.resetRotation()},[h]),f.useEffect(()=>{const b=F.current;!b||!x||b.setPointPalette(x)},[x]),f.useEffect(()=>{const b=F.current;b&&b.setPointData(Ve)},[Ve]),f.useEffect(()=>{if(!W)return;const U=an(P?Ve:g,le,{paletteIndexToTermId:be,includeEmptyRegions:!0});W(U)},[W,P,g,Ve,le,be]),f.useEffect(()=>{const b=F.current;b&&b.setInteractionLock(re)},[re]),me.jsxs("div",{className:J,style:ze,onPointerMove:hn,onPointerLeave:fn,onClick:dn,children:[me.jsx("canvas",{ref:se,className:"wsi-render-canvas",style:{position:"absolute",inset:0,zIndex:1,width:"100%",height:"100%",display:"block",touchAction:"none",cursor:Y==="cursor"&&Te!==null?"pointer":re?"crosshair":"grab"}}),t&&St&&Array.isArray(ue)&&ue.length>0?ue.map((b,U)=>me.jsx("div",{className:b.className,style:{position:"absolute",inset:0,zIndex:b.zIndex??3,pointerEvents:b.pointerEvents??"none",...b.style},children:b.render(St)},b.id??U)):null,t?me.jsx($t,{tool:Y,enabled:Y!=="cursor",imageWidth:t.width,imageHeight:t.height,imageMpp:t.mpp,imageZoom:t.maxTierZoom,stampOptions:Ee,projectorRef:F,viewStateSignal:e,persistedRegions:le,patchRegions:ne,regionStrokeStyle:he,regionStrokeHoverStyle:ae,regionStrokeActiveStyle:Ce,patchStrokeStyle:Z,resolveRegionStrokeStyle:ee,overlayShapes:X,hoveredRegionId:Te,activeRegionId:c,regionLabelStyle:ie,invalidateRef:C,onDrawComplete:V,onPatchComplete:fe}):null,a?me.jsx("pre",{"data-open-plant-debug-overlay":!0,style:et,children:ln}):null,t&&K?Be?me.jsxs(me.Fragment,{children:[me.jsx(Zt,{source:t,projectorRef:F,authToken:w,options:G,invalidateRef:z}),me.jsx("button",{type:"button","aria-label":"Hide overview map",onClick:()=>xe(!1),style:{position:"absolute",zIndex:6,...Ge.includes("left")?{left:Re}:{right:Re},...Ge.includes("top")?{top:Re+Tt+8}:{bottom:Re+Tt+8},width:20,height:20,borderRadius:999,border:"1px solid rgba(255,255,255,0.4)",background:"rgba(8, 14, 22, 0.9)",color:"#fff",fontSize:13,lineHeight:1,cursor:"pointer",padding:0},children:"×"})]}):me.jsx("button",{type:"button","aria-label":"Show overview map",onClick:()=>xe(!0),style:{position:"absolute",zIndex:6,...Ge.includes("left")?{left:Re}:{right:Re},...Ge.includes("top")?{top:Re}:{bottom:Re},height:24,minWidth:40,borderRadius:999,border:"1px solid rgba(255,255,255,0.45)",background:"rgba(8, 14, 22, 0.9)",color:"#dff8ff",fontSize:11,fontWeight:700,cursor:"pointer",padding:"0 8px"},children:"Map"}):null]})}exports.DEFAULT_POINT_COLOR=pt;exports.DrawLayer=$t;exports.M1TileRenderer=Yt;exports.OverviewMap=Zt;exports.TileScheduler=un;exports.TileViewerCanvas=Yn;exports.WsiTileRenderer=cn;exports.WsiViewerCanvas=dr;exports.buildTermPalette=En;exports.calcScaleLength=Tn;exports.calcScaleResolution=bt;exports.clamp=$;exports.closeRing=ye;exports.computeRoiPointGroups=an;exports.createCircle=ft;exports.createRectangle=ht;exports.filterPointDataByPolygons=Xe;exports.filterPointDataByPolygonsHybrid=nn;exports.filterPointDataByPolygonsInWorker=sn;exports.filterPointIndicesByPolygons=Qt;exports.filterPointIndicesByPolygonsInWorker=nr;exports.getWebGpuCapabilities=jn;exports.hexToRgba=Xt;exports.isSameViewState=vn;exports.normalizeImageInfo=Wn;exports.prefilterPointsByBoundsWebGpu=tn;exports.terminateRoiClipWorker=tr;exports.toBearerToken=Mn;exports.toTileUrl=wt;
124
+ }`),o=ke(e,i,"uCamera"),s=ke(e,i,"uPointSize"),a=ke(e,i,"uPalette"),u=ke(e,i,"uPaletteSize"),c=e.createVertexArray(),l=e.createBuffer(),w=e.createBuffer(),m=e.createBuffer(),g=e.createTexture();if(!c||!l||!w||!m||!g)throw new Error("point buffer allocation failed");e.bindVertexArray(c),e.bindBuffer(e.ARRAY_BUFFER,l),e.bufferData(e.ARRAY_BUFFER,0,e.DYNAMIC_DRAW);const T=e.getAttribLocation(i,"aPosition");if(T<0)throw new Error("point position attribute not found");e.enableVertexAttribArray(T),e.vertexAttribPointer(T,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,w),e.bufferData(e.ARRAY_BUFFER,0,e.DYNAMIC_DRAW);const x=e.getAttribLocation(i,"aTerm");if(x<0)throw new Error("point term attribute not found");return e.enableVertexAttribArray(x),e.vertexAttribIPointer(x,1,e.UNSIGNED_SHORT,0,0),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,m),e.bufferData(e.ELEMENT_ARRAY_BUFFER,0,e.DYNAMIC_DRAW),e.bindVertexArray(null),e.bindBuffer(e.ARRAY_BUFFER,null),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,null),e.bindTexture(e.TEXTURE_2D,g),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,1,1,0,e.RGBA,e.UNSIGNED_BYTE,new Uint8Array([160,160,160,255])),e.bindTexture(e.TEXTURE_2D,null),{program:i,vao:c,posBuffer:l,termBuffer:w,indexBuffer:m,paletteTexture:g,uCamera:o,uPointSize:s,uPalette:a,uPaletteSize:u}}handleTileLoaded(e,n){if(this.destroyed||this.contextLost||this.gl.isContextLost()){n.close();return}if(this.cache.has(e.key)){n.close();return}const r=this.createTextureFromBitmap(n);n.close(),r&&(this.cache.set(e.key,{key:e.key,texture:r,bounds:e.bounds,tier:e.tier,lastUsed:this.frameSerial}),this.trimCache(),this.requestRender())}createTextureFromBitmap(e){if(this.contextLost||this.gl.isContextLost())return null;const n=this.gl,r=n.createTexture();return r?(n.bindTexture(n.TEXTURE_2D,r),n.pixelStorei(n.UNPACK_FLIP_Y_WEBGL,1),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texImage2D(n.TEXTURE_2D,0,n.RGBA,n.RGBA,n.UNSIGNED_BYTE,e),n.bindTexture(n.TEXTURE_2D,null),r):null}}const ct=[],wr=[],yr={count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)};function pt(t,e){return t.id??e}function xr(t,e){if(!Array.isArray(e)||e.length<3)return!1;const[n,r]=t;let i=!1;for(let o=0,s=e.length-1;o<e.length;s=o++){const[a,u]=e[o],[c,l]=e[s];u>r!=l>r&&n<(c-a)*(r-u)/Math.max(1e-12,l-u)+a&&(i=!i)}return i}function Vt(t,e){for(let n=e.length-1;n>=0;n-=1){const r=e[n];if(r?.coordinates?.length&&xr(t,r.coordinates))return{region:r,regionIndex:n,regionId:pt(r,n)}}return null}function Tr({source:t,viewState:e,onViewStateChange:n,onStats:r,onTileError:i,onContextLost:o,onContextRestored:s,debugOverlay:a=!1,debugOverlayStyle:u,fitNonce:c=0,rotationResetNonce:l=0,authToken:w="",ctrlDragRotate:m=!0,pointData:g=null,pointPalette:T=null,roiRegions:x,roiPolygons:C,clipPointsToRois:P=!1,clipMode:F="worker",onClipStats:I,onRoiPointGroups:X,roiPaletteIndexToTermId:ye,interactionLock:se=!1,drawTool:V="cursor",stampOptions:Ae,regionStrokeStyle:le,regionStrokeHoverStyle:ae,regionStrokeActiveStyle:Ie,patchStrokeStyle:ee,resolveRegionStrokeStyle:Me,overlayShapes:ie,customLayers:q,patchRegions:xe,regionLabelStyle:Ee,onPointerWorldMove:y,onRegionHover:v,onRegionClick:S,onActiveRegionChange:k,onDrawComplete:Z,onPatchComplete:oe,showOverviewMap:he=!1,overviewMapOptions:Q,className:O,style:ue}){const ne=f.useRef(null),B=f.useRef(null),$=f.useRef(null),A=f.useRef(null),z=f.useRef(n),j=f.useRef(r),ce=f.useRef(a),[_e,Ce]=f.useState(!0),[Pe,fe]=f.useState(null),[K,h]=f.useState(null),[p,M]=f.useState(null),[R,W]=f.useState(null),E=f.useRef(null),N=f.useRef(0),Y=x??ct,L=xe??ct,te=C??wr,Te=(q?.length??0)>0,De=f.useMemo(()=>({position:"relative",width:"100%",height:"100%",...ue}),[ue]),pe=f.useMemo(()=>({position:"absolute",top:8,left:8,zIndex:7,margin:0,padding:"8px 10px",maxWidth:"min(420px, 80%)",pointerEvents:"none",whiteSpace:"pre-wrap",lineHeight:1.35,fontFamily:"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",fontSize:11,color:"#cde6ff",background:"rgba(6, 12, 20, 0.82)",border:"1px solid rgba(173, 216, 255, 0.28)",borderRadius:8,boxShadow:"0 8px 22px rgba(0,0,0,0.35)",...u}),[u]),G=f.useMemo(()=>Y.length>0?Y:te.length===0?ct:te.map((b,_)=>({id:_,coordinates:b})),[Y,te]),be=f.useMemo(()=>G.map(b=>b.coordinates),[G]),[Re,Ge]=f.useState(g);f.useEffect(()=>{const b=++N.current;let _=!1;if(!P)return Ge(g),()=>{_=!0};if(!g||!g.count||!g.positions||!g.paletteIndices)return Ge(null),()=>{_=!0};if(be.length===0)return Ge(yr),I?.({mode:F,durationMs:0,inputCount:g.count,outputCount:0,polygonCount:0}),()=>{_=!0};const D=(U,re)=>{if(_||b!==N.current)return;const tt=U?.drawIndices?U.drawIndices.length:U?.count??0;Ge(U),I?.({mode:re.mode,durationMs:re.durationMs,inputCount:g.count,outputCount:tt,polygonCount:be.length,usedWebGpu:re.usedWebGpu,candidateCount:re.candidateCount,bridgedToDraw:re.bridgedToDraw})};return(async()=>{if(F==="sync"){const U=performance.now(),re=Oe(g,be);D(re,{mode:"sync",durationMs:performance.now()-U});return}if(F==="hybrid-webgpu"){const U=await an(g,be,{bridgeToDraw:!0});D(U.data,{mode:U.meta.mode,durationMs:U.meta.durationMs,usedWebGpu:U.meta.usedWebGpu,candidateCount:U.meta.candidateCount,bridgedToDraw:U.meta.bridgedToDraw});return}try{const U=await ln(g,be);D(U.data,{mode:U.meta.mode,durationMs:U.meta.durationMs})}catch{const U=performance.now(),re=Oe(g,be);D(re,{mode:"sync",durationMs:performance.now()-U})}})(),()=>{_=!0}},[P,F,g,be,I]),f.useMemo(()=>{const b=Number(Q?.width??220);return Number.isFinite(b)?Math.max(64,b):220},[Q?.width]);const vt=f.useMemo(()=>{const b=Number(Q?.height??140);return Number.isFinite(b)?Math.max(48,b):140},[Q?.height]),Be=f.useMemo(()=>{const b=Number(Q?.margin??16);return Number.isFinite(b)?Math.max(0,b):16},[Q?.margin]),qe=Q?.position||"bottom-right",Ne=f.useCallback(b=>{h(_=>String(_)===String(b)?_:(k?.(b),b))},[k]);f.useEffect(()=>{z.current=n},[n]),f.useEffect(()=>{j.current=r},[r]),f.useEffect(()=>{ce.current=a,a||W(null)},[a]);const Ct=f.useCallback(b=>{j.current?.(b),ce.current&&W(b)},[]),mn=f.useMemo(()=>R?[`tier ${R.tier} | frame ${R.frameMs?.toFixed(2)??"-"} ms | drawCalls ${R.drawCalls??"-"}`,`tiles visible ${R.visible} | rendered ${R.rendered} | fallback ${R.fallback}`,`cache size ${R.cache} | hit ${R.cacheHits??"-"} | miss ${R.cacheMisses??"-"}`,`queue inflight ${R.inflight} | queued ${R.queued??"-"} | retries ${R.retries??"-"} | failed ${R.failed??"-"} | aborted ${R.aborted??"-"}`,`points ${R.points}`].join(`
125
+ `):"stats: waiting for first frame...",[R]);f.useEffect(()=>{!(K===null?!0:G.some((J,U)=>String(pt(J,U))===String(K)))&&K!==null&&Ne(null);const _=E.current;!(_===null?!0:G.some((J,U)=>String(pt(J,U))===String(_)))&&_!==null&&(E.current=null,fe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[G,K,v,Ne]);const Rt=f.useCallback(b=>{Te&&M(b);const _=z.current;_&&_(b),$.current?.(),A.current?.()},[Te]);f.useEffect(()=>{if(!he){Ce(!1);return}Ce(!0)},[he,t?.id]),f.useEffect(()=>{V!=="cursor"&&E.current!==null&&(E.current=null,fe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[V,v]);const ze=f.useCallback((b,_)=>{const D=B.current;if(!D)return null;const J=D.screenToWorld(b,_);if(!Array.isArray(J)||J.length<2)return null;const U=Number(J[0]),re=Number(J[1]);return!Number.isFinite(U)||!Number.isFinite(re)?null:[U,re]},[]),Mt=f.useCallback((b,_)=>{const D=B.current;if(!D)return null;const J=D.worldToScreen(b,_);if(!Array.isArray(J)||J.length<2)return null;const U=Number(J[0]),re=Number(J[1]);return!Number.isFinite(U)||!Number.isFinite(re)?null:[U,re]},[]),Et=f.useCallback(()=>{B.current?.requestRender(),$.current?.(),A.current?.()},[]),Pt=f.useMemo(()=>p??B.current?.getViewState()??null,[p]),St=f.useMemo(()=>{if(!t)return null;const b=Pt;return b?{source:t,viewState:b,drawTool:V,interactionLock:se,worldToScreen:Mt,screenToWorld:ze,requestRedraw:Et}:null},[t,Pt,V,se,Mt,ze,Et]),gn=f.useCallback(b=>{const _=b.target===ne.current,D=ze(b.clientX,b.clientY);if(y){const tt=!!D&&D[0]>=0&&D[1]>=0&&!!t&&D[0]<=t.width&&D[1]<=t.height;y({coordinate:D,clientX:b.clientX,clientY:b.clientY,insideImage:tt})}if(V!=="cursor")return;if(!_){E.current!==null&&(E.current=null,fe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}));return}if(!D||!G.length)return;const J=Vt(D,G),U=J?.regionId??null,re=E.current;String(re)!==String(U)&&(E.current=U,fe(U),v?.({region:J?.region??null,regionId:U,regionIndex:J?.regionIndex??-1,coordinate:D}))},[V,G,ze,v,y,t]),pn=f.useCallback(()=>{y?.({coordinate:null,clientX:-1,clientY:-1,insideImage:!1}),E.current!==null&&(E.current=null,fe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[v,y]),bn=f.useCallback(b=>{if(V!=="cursor"||b.target!==ne.current)return;if(!G.length){Ne(null);return}const _=ze(b.clientX,b.clientY);if(!_)return;const D=Vt(_,G);if(!D){Ne(null);return}const J=K!==null&&String(K)===String(D.regionId)?null:D.regionId;Ne(J),S?.({region:D.region,regionId:D.regionId,regionIndex:D.regionIndex,coordinate:_})},[V,G,ze,S,K,Ne]);return f.useEffect(()=>{const b=ne.current;if(!b||!t)return;const _=new dn(b,t,{onViewStateChange:Rt,onStats:Ct,onTileError:i,onContextLost:o,onContextRestored:s,authToken:w,ctrlDragRotate:m});return B.current=_,e&&_.setViewState(e),_.setInteractionLock(se),Te&&M(_.getViewState()),()=>{_.destroy(),B.current=null}},[t,Ct,i,o,s,w,m,Rt,Te]),f.useEffect(()=>{const b=B.current;!b||!e||b.setViewState(e)},[e]),f.useEffect(()=>{const b=B.current;b&&b.fitToImage()},[c]),f.useEffect(()=>{const b=B.current;b&&b.resetRotation()},[l]),f.useEffect(()=>{const b=B.current;!b||!T||b.setPointPalette(T)},[T]),f.useEffect(()=>{const b=B.current;b&&b.setPointData(Re)},[Re]),f.useEffect(()=>{if(!X)return;const _=hn(P?Re:g,G,{paletteIndexToTermId:ye,includeEmptyRegions:!0});X(_)},[X,P,g,Re,G,ye]),f.useEffect(()=>{const b=B.current;b&&b.setInteractionLock(se)},[se]),me.jsxs("div",{className:O,style:De,onPointerMove:gn,onPointerLeave:pn,onClick:bn,children:[me.jsx("canvas",{ref:ne,className:"wsi-render-canvas",style:{position:"absolute",inset:0,zIndex:1,width:"100%",height:"100%",display:"block",touchAction:"none",cursor:V==="cursor"&&Pe!==null?"pointer":se?"crosshair":"grab"}}),t&&St&&Array.isArray(q)&&q.length>0?q.map((b,_)=>me.jsx("div",{className:b.className,style:{position:"absolute",inset:0,zIndex:b.zIndex??3,pointerEvents:b.pointerEvents??"none",...b.style},children:b.render(St)},b.id??_)):null,t?me.jsx(Kt,{tool:V,enabled:V!=="cursor",imageWidth:t.width,imageHeight:t.height,imageMpp:t.mpp,imageZoom:t.maxTierZoom,stampOptions:Ae,projectorRef:B,viewStateSignal:e,persistedRegions:G,patchRegions:L,regionStrokeStyle:le,regionStrokeHoverStyle:ae,regionStrokeActiveStyle:Ie,patchStrokeStyle:ee,resolveRegionStrokeStyle:Me,overlayShapes:ie,hoveredRegionId:Pe,activeRegionId:K,regionLabelStyle:Ee,invalidateRef:$,onDrawComplete:Z,onPatchComplete:oe}):null,a?me.jsx("pre",{"data-open-plant-debug-overlay":!0,style:pe,children:mn}):null,t&&he?_e?me.jsxs(me.Fragment,{children:[me.jsx(Qt,{source:t,projectorRef:B,authToken:w,options:Q,invalidateRef:A}),me.jsx("button",{type:"button","aria-label":"Hide overview map",onClick:()=>Ce(!1),style:{position:"absolute",zIndex:6,...qe.includes("left")?{left:Be}:{right:Be},...qe.includes("top")?{top:Be+vt+8}:{bottom:Be+vt+8},width:20,height:20,borderRadius:999,border:"1px solid rgba(255,255,255,0.4)",background:"rgba(8, 14, 22, 0.9)",color:"#fff",fontSize:13,lineHeight:1,cursor:"pointer",padding:0},children:"×"})]}):me.jsx("button",{type:"button","aria-label":"Show overview map",onClick:()=>Ce(!0),style:{position:"absolute",zIndex:6,...qe.includes("left")?{left:Be}:{right:Be},...qe.includes("top")?{top:Be}:{bottom:Be},height:24,minWidth:40,borderRadius:999,border:"1px solid rgba(255,255,255,0.45)",background:"rgba(8, 14, 22, 0.9)",color:"#dff8ff",fontSize:11,fontWeight:700,cursor:"pointer",padding:"0 8px"},children:"Map"}):null]})}exports.DEFAULT_POINT_COLOR=bt;exports.DrawLayer=Kt;exports.M1TileRenderer=Ot;exports.OverviewMap=Qt;exports.TileScheduler=fn;exports.TileViewerCanvas=Zn;exports.WsiTileRenderer=dn;exports.WsiViewerCanvas=Tr;exports.buildTermPalette=Sn;exports.calcScaleLength=Mn;exports.calcScaleResolution=wt;exports.clamp=H;exports.closeRing=we;exports.computeRoiPointGroups=hn;exports.createCircle=ft;exports.createRectangle=ht;exports.filterPointDataByPolygons=Oe;exports.filterPointDataByPolygonsHybrid=an;exports.filterPointDataByPolygonsInWorker=ln;exports.filterPointIndicesByPolygons=rn;exports.filterPointIndicesByPolygonsInWorker=lr;exports.getWebGpuCapabilities=ir;exports.hexToRgba=Gt;exports.isSameViewState=En;exports.normalizeImageInfo=Hn;exports.prefilterPointsByBoundsWebGpu=sn;exports.terminateRoiClipWorker=cr;exports.toBearerToken=Pn;exports.toTileUrl=yt;
126
126
  //# sourceMappingURL=index.cjs.map