open-plant 1.2.2 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -2
- package/dist/index.cjs +7 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1020 -974
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/react/draw-layer.d.ts +7 -1
- package/dist/types/react/draw-layer.d.ts.map +1 -1
- package/package.json +1 -1
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
|
|
1
|
+
"use strict";var bn=Object.defineProperty;var wn=(t,e,n)=>e in t?bn(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var d=(t,e,n)=>wn(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 yn(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 xn(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 Tn=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 vn=`#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
|
-
`,
|
|
21
|
+
`,Mn=`#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 Vt{constructor(e){d(this,"canvas");d(this,"gl");d(this,"camera",new Tn);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=xn(this.canvas),this.program=yn(this.gl,vn,Mn);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 $(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 Cn(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 Rn(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 Ot(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 Pn(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(Ot(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 Sn="rgba(255, 77, 79, 0.16)",An=3,In=2,Gt=96,_n=1,_t=[],lt=[],Bt=1e3,qt=2,$t=2,Bn=4096,Un=.2,Be={color:"#ff4d4f",width:2,lineJoin:"round",lineCap:"round",shadowColor:"rgba(0, 0, 0, 0)",shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0},Fn={color:"#4cc9f0",width:2,lineDash:[10,8],lineJoin:"round",lineCap:"round",shadowColor:"rgba(0, 0, 0, 0)",shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0},ye={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 Ln(t){return{rectangleAreaMm2:it(t?.rectangleAreaMm2,qt),circleAreaMm2:it(t?.circleAreaMm2,$t),rectanglePixelSize:it(t?.rectanglePixelSize,Bn)}}function Dn(t){return t*Bt*Bt}function Ut(t,e){return!t||!Number.isFinite(e)||e<=0?[]:be([[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 kn(t,e,n=Gt){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 be(r)}function be(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?[]:be([[t[0],t[1]],[e[0],t[1]],[e[0],e[1]],[t[0],e[1]]])}function ft(t,e,n=Gt){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 be(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)>_n}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 ze(t,e,n,r=!1,i=!1){e.length!==0&&(t.beginPath(),mt(t,e,r),i&&r&&(t.fillStyle=Sn,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 Ht(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):Be.width,r=typeof t?.shadowBlur=="number"&&Number.isFinite(t.shadowBlur)?Math.max(0,t.shadowBlur):Be.shadowBlur,i=typeof t?.shadowOffsetX=="number"&&Number.isFinite(t.shadowOffsetX)?t.shadowOffsetX:Be.shadowOffsetX,o=typeof t?.shadowOffsetY=="number"&&Number.isFinite(t.shadowOffsetY)?t.shadowOffsetY:Be.shadowOffsetY;return{color:t?.color||Be.color,width:n,lineDash:e.length?e:lt,lineJoin:t?.lineJoin||Be.lineJoin,lineCap:t?.lineCap||Be.lineCap,shadowColor:t?.shadowColor||Be.shadowColor,shadowBlur:r,shadowOffsetX:i,shadowOffsetY:o}}function We(t,e){return e?Ht({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 Zt(t){const e=t[0];return Array.isArray(e)?Array.isArray(e[0]):!1}function kt(t,e){const n=Zt(t)?t:[t],r=[];for(const i of n){if(!Array.isArray(i)||i.length<2)continue;const o=e?be(i):i.map(([s,a])=>[s,a]);o.length>=(e?4:2)&&r.push(o)}return r}function Nn(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 zn(t){const e=typeof t?.paddingX=="number"&&Number.isFinite(t.paddingX)?Math.max(0,t.paddingX):ye.paddingX,n=typeof t?.paddingY=="number"&&Number.isFinite(t.paddingY)?Math.max(0,t.paddingY):ye.paddingY,r=typeof t?.fontSize=="number"&&Number.isFinite(t.fontSize)?Math.max(8,t.fontSize):ye.fontSize,i=typeof t?.borderWidth=="number"&&Number.isFinite(t.borderWidth)?Math.max(0,t.borderWidth):ye.borderWidth,o=typeof t?.offsetY=="number"&&Number.isFinite(t.offsetY)?t.offsetY:ye.offsetY,s=typeof t?.borderRadius=="number"&&Number.isFinite(t.borderRadius)?Math.max(0,t.borderRadius):ye.borderRadius;return{fontFamily:t?.fontFamily||ye.fontFamily,fontSize:r,fontWeight:t?.fontWeight||ye.fontWeight,textColor:t?.textColor||ye.textColor,backgroundColor:t?.backgroundColor||ye.backgroundColor,borderColor:t?.borderColor||ye.borderColor,borderWidth:i,paddingX:e,paddingY:n,offsetY:o,borderRadius:s}}function Yn(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 Wn(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 Xn(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=et(n[0],u*.5+1,r-u*.5-1),w=et(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,Yn(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[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 jt({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:R,resolveRegionStrokeStyle:D,overlayShapes:A,hoveredRegionId:W=null,activeRegionId:we=null,regionLabelStyle:ie,invalidateRef:X,className:Pe,style:he}){const ue=f.useRef(null),Se=f.useRef(!1),Z=f.useRef(t),ee=f.useRef({isDrawing:!1,pointerId:null,start:null,current:null,points:[],stampCenter:null}),O=l??t!=="cursor",ce=f.useMemo(()=>w&&w.length>0?w:!g||g.length===0?_t:g.map((c,p)=>({id:p,coordinates:c})),[w,g]),Ae=f.useMemo(()=>m??_t,[m]),oe=f.useMemo(()=>Ht(x),[x]),T=f.useMemo(()=>We(oe,y),[oe,y]),v=f.useMemo(()=>We(oe,M),[oe,M]),P=f.useMemo(()=>We(Fn,R),[R]),z=f.useMemo(()=>zn(ie),[ie]),G=f.useMemo(()=>Ln(o),[o]),fe=f.useMemo(()=>({position:"absolute",inset:0,zIndex:2,width:"100%",height:"100%",display:"block",touchAction:"none",pointerEvents:O?"auto":"none",cursor:O?"crosshair":"default",...he}),[O,he]),K=f.useCallback(()=>{const c=ue.current;if(!c)return;const p=c.getBoundingClientRect(),C=Math.max(1,window.devicePixelRatio||1),I=Math.max(1,Math.round(p.width*C)),S=Math.max(1,Math.round(p.height*C));(c.width!==I||c.height!==S)&&(c.width=I,c.height=S)},[]),V=f.useCallback(c=>{const p=s.current;if(!p||c.length===0)return[];const C=new Array(c.length);for(let I=0;I<c.length;I+=1){const S=st(p.worldToScreen(c[I][0],c[I][1]));if(!S)return[];C[I]=S}return C},[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,C=typeof i=="number"&&Number.isFinite(i)?i:0,I=s.current?.getViewState?.().zoom,S=typeof I=="number"&&Number.isFinite(I)&&I>0?I:1,_=C+Math.log2(S),F=Math.max(1e-9,wt(p,C,_));return c/F/S},[r,i,s]),se=f.useCallback((c,p)=>{if(!p)return[];let C=0;if(c==="stamp-rectangle-4096px"){const _=G.rectanglePixelSize*.5;return Ut(p,_).map(k=>ot(k,e,n))}if(c==="stamp-rectangle"||c==="stamp-rectangle-2mm2"?C=c==="stamp-rectangle-2mm2"?qt:G.rectangleAreaMm2:(c==="stamp-circle"||c==="stamp-circle-2mm2"||c==="stamp-circle-hpf-0.2mm2")&&(C=c==="stamp-circle-hpf-0.2mm2"?Un:c==="stamp-circle-2mm2"?$t:G.circleAreaMm2),!Number.isFinite(C)||C<=0)return[];const I=Dn(C);let S=[];if(c==="stamp-rectangle"||c==="stamp-rectangle-2mm2"){const _=J(Math.sqrt(I)*.5);S=Ut(p,_)}else if(c==="stamp-circle"||c==="stamp-circle-2mm2"||c==="stamp-circle-hpf-0.2mm2"){const _=J(Math.sqrt(I/Math.PI));S=kn(p,_)}return S.length?S.map(_=>ot(_,e,n)):[]},[J,e,n,G]),ae=f.useCallback(()=>{const c=ee.current;return $e(t)?se(t,c.stampCenter):c.isDrawing?t==="freehand"?c.points:t==="rectangle"?ht(c.start,c.current):t==="circular"?ft(c.start,c.current):[]:[]},[t,se]),U=f.useCallback(()=>{K();const c=ue.current;if(!c)return;const p=c.getContext("2d");if(!p)return;const C=Math.max(1,window.devicePixelRatio||1),I=c.width/C,S=c.height/C;if(p.setTransform(1,0,0,1,0,0),p.clearRect(0,0,c.width,c.height),p.setTransform(C,0,0,C,0,0),ce.length>0)for(let _=0;_<ce.length;_+=1){const F=ce[_],k=F?.coordinates;if(!k||k.length<3)continue;const te=be(k),ne=V(te);if(ne.length>=4){const ve=F.id??_,Me=Dt(we,ve)?"active":Dt(W,ve)?"hover":"default";let Ce=Me==="active"?v:Me==="hover"?T:oe;if(D){const Ie=D({region:F,regionId:ve,regionIndex:_,state:Me});Ce=We(Ce,Ie||void 0)}ze(p,ne,Ce,!0,!1)}}if(Ae.length>0)for(let _=0;_<Ae.length;_+=1){const k=Ae[_]?.coordinates;if(!k||k.length<3)continue;const te=be(k),ne=V(te);ne.length<4||ze(p,ne,P,!0,!1)}if(Array.isArray(A)&&A.length>0){const _=V(be([[0,0],[e,0],[e,n],[0,n]]));for(let F=0;F<A.length;F+=1){const k=A[F];if(!k?.coordinates?.length||k.visible===!1)continue;const te=Zt(k.coordinates),ne=k.closed??te,ve=kt(k.coordinates,ne);if(k.invertedFill?.fillColor&&_.length>=4){const Ce=[],Ie=kt(k.coordinates,!0);for(const re of Ie){const Ee=V(re);Ee.length>=4&&Ce.push(Ee)}Nn(p,_,Ce,k.invertedFill.fillColor)}if(ve.length===0)continue;const Me=We(oe,k.stroke??k.strokeStyle);for(const Ce of ve){const Ie=V(Ce);Ie.length<2||ze(p,Ie,Me,ne,k.fill??!1)}}}if(O){const _=ae();if(_.length>0)if(t==="freehand"){const F=V(_);F.length>=2&&ze(p,F,oe,!1,!1),F.length>=3&&ze(p,V(be(_)),oe,!0,!0)}else{const F=V(_);F.length>=4&&ze(p,F,oe,!0,!0)}}if(ce.length>0)for(const _ of ce){if(!_.label)continue;const F=_?.coordinates;if(!F||F.length<3)continue;const k=be(F),te=Wn(k);if(!te)continue;const ne=st(s.current?.worldToScreen(te[0],te[1])??[]);ne&&Xn(p,_.label,ne,I,S,z)}},[O,t,ae,K,V,e,n,s,ce,A,W,we,oe,T,v,Ae,P,D,z]),E=f.useCallback(()=>{Se.current||(Se.current=!0,requestAnimationFrame(()=>{Se.current=!1,U()}))},[U]),Y=f.useCallback(()=>{const c=ee.current,p=ue.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 C=st(p.screenToWorld(c.clientX,c.clientY));return C?ot(C,e,n):null},[s,e,n]),j=f.useCallback(()=>{const c=ee.current;if(!c.isDrawing){Y(),E();return}let p=[];t==="freehand"?c.points.length>=An&&(p=be(c.points)):t==="rectangle"?p=ht(c.start,c.current):t==="circular"&&(p=ft(c.start,c.current)),(t==="freehand"||t==="rectangle"||t==="circular")&&Lt(p)&&a&&a({tool:t,intent:"roi",coordinates:p,bbox:Ft(p),areaPx:dt(p)}),Y(),E()},[t,a,Y,E]),le=f.useCallback((c,p)=>{const C=se(c,p);if(!Lt(C))return;const I=c==="stamp-rectangle-4096px"?"patch":"roi",S={tool:c,intent:I,coordinates:C,bbox:Ft(C),areaPx:dt(C)};a?.(S),I==="patch"&&u&&u(S)},[se,a,u]),Le=f.useCallback(c=>{if(!O||t==="cursor"||c.button!==0)return;const p=q(c);if(!p)return;if(c.preventDefault(),c.stopPropagation(),$e(t)){const S=ee.current;S.stampCenter=p,le(t,p),E();return}const C=ue.current;C&&C.setPointerCapture(c.pointerId);const I=ee.current;I.isDrawing=!0,I.pointerId=c.pointerId,I.start=p,I.current=p,I.points=t==="freehand"?[p]:[],E()},[O,t,q,le,E]),xe=f.useCallback(c=>{if(!O||t==="cursor")return;const p=q(c);if(!p)return;if($e(t)){const I=ee.current;I.stampCenter=p,c.preventDefault(),c.stopPropagation(),E();return}const C=ee.current;if(!(!C.isDrawing||C.pointerId!==c.pointerId)){if(c.preventDefault(),c.stopPropagation(),t==="freehand"){const I=s.current,S=Math.max(1e-6,I?.getViewState?.().zoom??1),_=In/S,F=_*_,k=C.points[C.points.length-1];if(!k)C.points.push(p);else{const te=p[0]-k[0],ne=p[1]-k[1];te*te+ne*ne>=F&&C.points.push(p)}}else C.current=p;E()}},[O,t,q,E,s]),Te=f.useCallback(c=>{const p=ee.current;if(!p.isDrawing||p.pointerId!==c.pointerId)return;c.preventDefault(),c.stopPropagation();const C=ue.current;if(C&&C.hasPointerCapture(c.pointerId))try{C.releasePointerCapture(c.pointerId)}catch{}j()},[j]),pe=f.useCallback(()=>{if(!$e(t))return;const c=ee.current;c.stampCenter&&(c.stampCenter=null,E())},[t,E]);return f.useEffect(()=>{K(),E();const c=ue.current;if(!c)return;const p=new ResizeObserver(()=>{K(),E()});return p.observe(c),()=>{p.disconnect()}},[K,E]),f.useEffect(()=>{O||Y(),E()},[O,E,Y]),f.useEffect(()=>{Z.current!==t&&(Z.current=t,Y(),E())},[t,Y,E]),f.useEffect(()=>{E()},[h,ce,A,E]),f.useEffect(()=>{if(X)return X.current=E,()=>{X.current===E&&(X.current=null)}},[X,E]),f.useEffect(()=>{if(!O)return;const c=p=>{p.key==="Escape"&&(Y(),E())};return window.addEventListener("keydown",c),()=>{window.removeEventListener("keydown",c)}},[O,Y,E]),me.jsx("canvas",{ref:ue,className:Pe,style:fe,onPointerDown:Le,onPointerMove:xe,onPointerUp:Te,onPointerCancel:Te,onPointerLeave:pe,onContextMenu:c=>{O&&c.preventDefault()},onWheel:c=>{O&&c.preventDefault()}})}function Nt(t){return String(t??"").replace(/\/+$/,"")}function Kt(t){const e=String(t??"");return e.startsWith("/")?e:`/${e}`}function Vn(t){const e=Nt(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=Nt(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 On(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=Kt(u),m=Vn(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 yt(t,e,n,r){if(t.tileUrlBuilder)return t.tileUrlBuilder(e,n,r);const i=Kt(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 Ye(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 Jt({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=Ye(r?.width,de.width,64),x=Ye(r?.height,de.height,48),y=Ye(r?.margin,de.margin,0),M=Ye(r?.borderRadius,de.borderRadius,0),R=Ye(r?.borderWidth,de.borderWidth,0),D=Math.max(1,Math.round(Ye(r?.maxThumbnailTiles,de.maxThumbnailTiles,1))),A=r?.backgroundColor||de.backgroundColor,W=r?.borderColor||de.borderColor,we=r?.viewportStrokeColor||de.viewportStrokeColor,ie=r?.viewportFillColor||de.viewportFillColor,X=r?.interactive??de.interactive,Pe=r?.showThumbnail??de.showThumbnail,he=r?.position||de.position,ue=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:X?"auto":"none",touchAction:"none",boxShadow:"0 10px 22px rgba(0, 0, 0, 0.3)",...s}},[y,he,g,x,M,X,s]),Se=f.useCallback(()=>{const T=a.current;if(!T)return;const v=T.getContext("2d");if(!v)return;const P=g,z=x,G=Math.max(1,window.devicePixelRatio||1),fe=Math.max(1,Math.round(P*G)),K=Math.max(1,Math.round(z*G));(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(G,0,0,G,0,0),v.fillStyle=A,v.fillRect(0,0,P,z);const V=u.current;V&&v.drawImage(V,0,0,P,z),v.strokeStyle=W,v.lineWidth=R,v.strokeRect(R*.5,R*.5,P-R,z-R);const J=e.current,se=J?.getViewBounds?.(),ae=J?.getViewCorners?.(),U=He(se)?se:He(l.current)?l.current:null;if(!U)return;l.current=U;const E=P/Math.max(1,t.width),Y=z/Math.max(1,t.height),q=Array.isArray(ae)&&ae.length>=4&&ae.every(c=>Array.isArray(c)&&c.length>=2&&Number.isFinite(c[0])&&Number.isFinite(c[1]))?ae:null;if(q){v.beginPath();for(let c=0;c<q.length;c+=1){const p=q[c],C=$(p[0]*E,0,P),I=$(p[1]*Y,0,z);c===0?v.moveTo(C,I):v.lineTo(C,I)}v.closePath(),v.fillStyle=ie,v.fill(),v.strokeStyle=we,v.lineWidth=1.5,v.stroke();return}const j=$(U[0]*E,0,P),le=$(U[1]*Y,0,z),Le=$(U[2]*E,0,P),xe=$(U[3]*Y,0,z),Te=Math.max(1,Le-j),pe=Math.max(1,xe-le);v.fillStyle=ie,v.fillRect(j,le,Te,pe),v.strokeStyle=we,v.lineWidth=1.5,v.strokeRect(j+.5,le+.5,Math.max(1,Te-1),Math.max(1,pe-1))},[g,x,A,W,R,e,t.width,t.height,ie,we]),Z=f.useCallback(()=>{m.current||(m.current=!0,w.current=requestAnimationFrame(()=>{m.current=!1,w.current=null,Se()}))},[Se]),ee=f.useCallback((T,v)=>{const P=a.current;if(!P)return null;const z=P.getBoundingClientRect();if(!z.width||!z.height)return null;const G=$((T-z.left)/z.width,0,1),fe=$((v-z.top)/z.height,0,1);return[G*t.width,fe*t.height]},[t.width,t.height]),O=f.useCallback((T,v)=>{const P=e.current;if(!P)return;if(P.setViewCenter){P.setViewCenter(T,v),Z();return}const z=P.getViewBounds?.(),G=He(z)?z:He(l.current)?l.current:null;if(!G)return;const fe=Math.max(1e-6,G[2]-G[0]),K=Math.max(1e-6,G[3]-G[1]);P.setViewState({offsetX:T-fe*.5,offsetY:v-K*.5}),Z()},[e,Z]),ce=f.useCallback(T=>{if(!X||T.button!==0)return;const v=a.current;if(!v)return;const P=ee(T.clientX,T.clientY);P&&(T.preventDefault(),T.stopPropagation(),v.setPointerCapture(T.pointerId),h.current={active:!0,pointerId:T.pointerId},O(P[0],P[1]))},[X,ee,O]),Ae=f.useCallback(T=>{const v=h.current;if(!v.active||v.pointerId!==T.pointerId)return;const P=ee(T.clientX,T.clientY);P&&(T.preventDefault(),T.stopPropagation(),O(P[0],P[1]))},[ee,O]),oe=f.useCallback(T=>{const v=h.current;if(!v.active||v.pointerId!==T.pointerId)return;const P=a.current;if(P&&P.hasPointerCapture(T.pointerId))try{P.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,P=2**(t.maxTierZoom-v),z=Math.ceil(t.width/P),G=Math.ceil(t.height/P),fe=Math.max(1,Math.ceil(z/t.tileSize)),K=Math.max(1,Math.ceil(G/t.tileSize)),V=fe*K;if(!Pe||V>D)return;const J=document.createElement("canvas");J.width=Math.max(1,Math.round(g)),J.height=Math.max(1,Math.round(x));const se=J.getContext("2d");if(!se)return;se.fillStyle=A,se.fillRect(0,0,J.width,J.height);const ae=[];for(let U=0;U<K;U+=1)for(let E=0;E<fe;E+=1){const Y=E*t.tileSize*P,q=U*t.tileSize*P,j=Math.min((E+1)*t.tileSize,z)*P,le=Math.min((U+1)*t.tileSize,G)*P;ae.push({url:yt(t,v,E,U),bounds:[Y,q,j,le]})}return Promise.allSettled(ae.map(async U=>{const E=!!n,Y=await fetch(U.url,{headers:E?{Authorization:n}:void 0});if(!Y.ok)throw new Error(`HTTP ${Y.status}`);const q=await createImageBitmap(await Y.blob());return{tile:U,bitmap:q}})).then(U=>{if(T){for(const q of U)q.status==="fulfilled"&&q.value.bitmap.close();return}const E=J.width/Math.max(1,t.width),Y=J.height/Math.max(1,t.height);for(const q of U){if(q.status!=="fulfilled")continue;const{tile:{bounds:j},bitmap:le}=q.value,Le=j[0]*E,xe=j[1]*Y,Te=Math.max(1,(j[2]-j[0])*E),pe=Math.max(1,(j[3]-j[1])*Y);se.drawImage(le,Le,xe,Te,pe),le.close()}u.current=J,Z()}),()=>{T=!0}},[t,n,g,x,A,Pe,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:ue,onPointerDown:ce,onPointerMove:Ae,onPointerUp:oe,onPointerCancel:oe,onContextMenu:T=>{T.preventDefault()},onWheel:T=>{T.preventDefault(),T.stopPropagation()}})}function Gn({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 Vt({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 Qt(t){return Math.max(0,Math.min(Math.floor(t.count??0),Math.floor((t.positions?.length??0)/2),t.paletteIndices?.length??0))}function qn(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 en(t){const e=[];for(const n of t??[]){const r=qn(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 $n(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 tn(t,e,n){for(const r of n)if(!(t<r.minX||t>r.maxX||e<r.minY||e>r.maxY)&&$n(t,e,r.ring))return!0;return!1}function Ve(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return null;const n=en(e??[]);if(n.length===0)return{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)};const r=Qt(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];tn(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 nn(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return new Uint32Array(0);const n=en(e??[]);if(n.length===0)return new Uint32Array(0);const r=Qt(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];tn(u,l,n)&&(o[s]=a,s+=1)}return o.subarray(0,s)}let Ze=null;const Hn=`
|
|
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 Zn(){if(typeof navigator>"u")return!1;const t=navigator;return typeof t.gpu=="object"&&t.gpu!==null}function rn(){if(!Zn())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,jn=globalThis.GPUBufferUsage?.COPY_SRC??4,Kn=globalThis.GPUBufferUsage?.UNIFORM??64,Jn=globalThis.GPUBufferUsage?.MAP_READ??1,Qn=globalThis.GPUMapMode?.READ??1;async function er(){const t=rn();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 tr(){return Ze||(Ze=(async()=>{const t=rn();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:Hn}),entryPoint:"main"}});return{device:n,pipeline:i,bindGroupLayout:r}})(),Ze)}function Je(t,e){return Math.ceil(t/e)*e}async function on(t,e,n){const r=await tr();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:Je(a,4),usage:at|Ke}),m=r.device.createBuffer({size:Je(u,4),usage:at|Ke}),g=r.device.createBuffer({size:Je(l,4),usage:at|jn}),x=r.device.createBuffer({size:16,usage:Kn|Ke}),y=r.device.createBuffer({size:Je(l,4),usage:Ke|Jn});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}}]}),R=r.device.createCommandEncoder(),D=R.beginComputePass();D.setPipeline(r.pipeline),D.setBindGroup(0,M),D.dispatchWorkgroups(Math.ceil(s/256)),D.end(),R.copyBufferToBuffer(g,0,y,0,l),r.device.queue.submit([R.finish()]),await y.mapAsync(Qn);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 Re(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function nr(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 rr(t){const e=[];for(const n of t??[]){const r=nr(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 ir(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 zt(t,e,n){for(const r of n)if(!(t<r.minX||t>r.maxX||e<r.minY||e>r.maxY)&&ir(t,e,r.ring))return!0;return!1}async function sn(t,e,n={}){const r=Re(),i=n.bridgeToDraw===!0;if(!t||!t.count||!t.positions||!t.paletteIndices)return{data:null,meta:{mode:"hybrid-webgpu",durationMs:Re()-r,usedWebGpu:!1,candidateCount:0,bridgedToDraw:!1}};const o=rr(e??[]);if(o.length===0)return{data:{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)},meta:{mode:"hybrid-webgpu",durationMs:Re()-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:Re()-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,R=o[y];a[M]=R.minX,a[M+1]=R.minY,a[M+2]=R.maxX,a[M+3]=R.maxY}let u=null,l=!1;try{u=await on(t.positions,s,a),l=!!u}catch{u=null,l=!1}if(!u)return{data:Ve(t,e),meta:{mode:"hybrid-webgpu",durationMs:Re()-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:Re()-r,usedWebGpu:!0,candidateCount:0,bridgedToDraw:!0}}:{data:{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)},meta:{mode:"hybrid-webgpu",durationMs:Re()-r,usedWebGpu:!0,candidateCount:0,bridgedToDraw:!1}};if(i){const y=new Uint32Array(h);let M=0;for(let R=0;R<h;R+=1){const D=w[R]??0,A=t.positions[D*2],W=t.positions[D*2+1];zt(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:Re()-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,R=t.positions[M*2],D=t.positions[M*2+1];zt(R,D,o)&&(m[x*2]=R,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:Re()-r,usedWebGpu:!0,candidateCount:h,bridgedToDraw:!1}}}let ge=null,gt=!0,an=1;const Fe=new Map;function Ue(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function un(){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=Fe.get(e.id);if(!n)return;if(Fe.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:Ue()-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:Ue()-n.startMs}})}function Tt(){gt=!1,ge&&(ge.removeEventListener("message",xt),ge.removeEventListener("error",Tt),ge.terminate(),ge=null);for(const[,t]of Fe)t.reject(new Error("worker crashed"));Fe.clear()}function or(){if(ge){ge.removeEventListener("message",xt),ge.removeEventListener("error",Tt),ge.terminate(),ge=null;for(const[,t]of Fe)t.reject(new Error("worker terminated"));Fe.clear()}}async function cn(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return{data:null,meta:{mode:"worker",durationMs:0}};const n=un();if(!n){const u=Ue();return{data:Ve(t,e),meta:{mode:"sync",durationMs:Ue()-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=an++,a=Ue();return new Promise((u,l)=>{Fe.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 sr(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return{indices:new Uint32Array(0),meta:{mode:"worker",durationMs:0}};const n=un();if(!n){const a=Ue();return{indices:nn(t,e),meta:{mode:"sync",durationMs:Ue()-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=an++,s=Ue();return new Promise((a,u)=>{Fe.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 ar(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 ur(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 cr(t){const e=[];for(let n=0;n<t.length;n+=1){const r=t[n];if(!r?.coordinates?.length)continue;const i=ar(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,ur(i))})}return e}function lr(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 hr(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 ln(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 R=m[M];R>=r||(x[y]=R,y+=1)}i=x}else i=new Uint32Array(0)}const o=i?i.length:r,s=cr(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||lr(x,y,A.ring)&&(!M||A.area<M.area)&&(M=A);if(!M)continue;l+=1;const R=t.paletteIndices[g]??0,D=a.get(M.regionIndex)??new Map;D.set(R,(D.get(R)??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,R])=>({termId:hr(M,n.paletteIndexToTermId),paletteIndex:M,count:R})).sort((M,R)=>R.count-M.count||M.paletteIndex-R.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 Qe(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function fr(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 hn{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=fr(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:Qe()+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 Yt=.35;class dr{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=Xe(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=Xe(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=Xe(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 Xe(t){return t*Math.PI/180}function Wt(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function De(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 fn{constructor(e,n,r={}){d(this,"canvas");d(this,"source");d(this,"gl");d(this,"camera",new dr);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):Yt;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 hn({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=Xe(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,R=(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 we=W*this.source.tileSize*r,ie=A*this.source.tileSize*r,X=Math.min((W+1)*this.source.tileSize,i)*r,Pe=Math.min((A+1)*this.source.tileSize,o)*r,he=W-M,ue=A-R;D.push({key:`${e}/${W}/${A}`,tier:e,x:W,y:A,bounds:[we,ie,X,Pe],distance2:he*he+ue*ue,url:yt(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=Wt();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:Wt()-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/Yt,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=Xe(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=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=
|
|
85
|
+
}`),o=De(e,i,"uCamera"),s=De(e,i,"uBounds"),a=De(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=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=
|
|
125
|
-
`):"stats: waiting for first frame...",[
|
|
124
|
+
}`),o=De(e,i,"uCamera"),s=De(e,i,"uPointSize"),a=De(e,i,"uPalette"),u=De(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=[],mr=[],gr={count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)};function pt(t,e){return t.id??e}function pr(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 Xt(t,e){for(let n=e.length-1;n>=0;n-=1){const r=e[n];if(r?.coordinates?.length&&pr(t,r.coordinates))return{region:r,regionIndex:n,regionId:pt(r,n)}}return null}function br({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:R=!1,clipMode:D="worker",onClipStats:A,onRoiPointGroups:W,roiPaletteIndexToTermId:we,interactionLock:ie=!1,drawTool:X="cursor",stampOptions:Pe,regionStrokeStyle:he,regionStrokeHoverStyle:ue,regionStrokeActiveStyle:Se,patchStrokeStyle:Z,resolveRegionStrokeStyle:ee,overlayShapes:O,customLayers:ce,patchRegions:Ae,regionLabelStyle:oe,onPointerWorldMove:T,onRegionHover:v,onRegionClick:P,onActiveRegionChange:z,onDrawComplete:G,onPatchComplete:fe,showOverviewMap:K=!1,overviewMapOptions:V,className:J,style:se}){const ae=f.useRef(null),U=f.useRef(null),E=f.useRef(null),Y=f.useRef(null),q=f.useRef(n),j=f.useRef(r),le=f.useRef(a),[Le,xe]=f.useState(!0),[Te,pe]=f.useState(null),[c,p]=f.useState(null),[C,I]=f.useState(null),[S,_]=f.useState(null),F=f.useRef(null),k=f.useRef(0),te=y??ct,ne=Ae??ct,ve=M??mr,Me=(ce?.length??0)>0,Ce=f.useMemo(()=>({position:"relative",width:"100%",height:"100%",...se}),[se]),Ie=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]),re=f.useMemo(()=>te.length>0?te:ve.length===0?ct:ve.map((b,B)=>({id:B,coordinates:b})),[te,ve]),Ee=f.useMemo(()=>re.map(b=>b.coordinates),[re]),[Oe,Ge]=f.useState(g);f.useEffect(()=>{const b=++k.current;let B=!1;if(!R)return Ge(g),()=>{B=!0};if(!g||!g.count||!g.positions||!g.paletteIndices)return Ge(null),()=>{B=!0};if(Ee.length===0)return Ge(gr),A?.({mode:D,durationMs:0,inputCount:g.count,outputCount:0,polygonCount:0}),()=>{B=!0};const N=(L,Q)=>{if(B||b!==k.current)return;const tt=L?.drawIndices?L.drawIndices.length:L?.count??0;Ge(L),A?.({mode:Q.mode,durationMs:Q.durationMs,inputCount:g.count,outputCount:tt,polygonCount:Ee.length,usedWebGpu:Q.usedWebGpu,candidateCount:Q.candidateCount,bridgedToDraw:Q.bridgedToDraw})};return(async()=>{if(D==="sync"){const L=performance.now(),Q=Ve(g,Ee);N(Q,{mode:"sync",durationMs:performance.now()-L});return}if(D==="hybrid-webgpu"){const L=await sn(g,Ee,{bridgeToDraw:!0});N(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 cn(g,Ee);N(L.data,{mode:L.meta.mode,durationMs:L.meta.durationMs})}catch{const L=performance.now(),Q=Ve(g,Ee);N(Q,{mode:"sync",durationMs:performance.now()-L})}})(),()=>{B=!0}},[R,D,g,Ee,A]),f.useMemo(()=>{const b=Number(V?.width??220);return Number.isFinite(b)?Math.max(64,b):220},[V?.width]);const vt=f.useMemo(()=>{const b=Number(V?.height??140);return Number.isFinite(b)?Math.max(48,b):140},[V?.height]),_e=f.useMemo(()=>{const b=Number(V?.margin??16);return Number.isFinite(b)?Math.max(0,b):16},[V?.margin]),qe=V?.position||"bottom-right",ke=f.useCallback(b=>{p(B=>String(B)===String(b)?B:(z?.(b),b))},[z]);f.useEffect(()=>{q.current=n},[n]),f.useEffect(()=>{j.current=r},[r]),f.useEffect(()=>{le.current=a,a||_(null)},[a]);const Mt=f.useCallback(b=>{j.current?.(b),le.current&&_(b)},[]),dn=f.useMemo(()=>S?[`tier ${S.tier} | frame ${S.frameMs?.toFixed(2)??"-"} ms | drawCalls ${S.drawCalls??"-"}`,`tiles visible ${S.visible} | rendered ${S.rendered} | fallback ${S.fallback}`,`cache size ${S.cache} | hit ${S.cacheHits??"-"} | miss ${S.cacheMisses??"-"}`,`queue inflight ${S.inflight} | queued ${S.queued??"-"} | retries ${S.retries??"-"} | failed ${S.failed??"-"} | aborted ${S.aborted??"-"}`,`points ${S.points}`].join(`
|
|
125
|
+
`):"stats: waiting for first frame...",[S]);f.useEffect(()=>{!(c===null?!0:re.some((H,L)=>String(pt(H,L))===String(c)))&&c!==null&&ke(null);const B=F.current;!(B===null?!0:re.some((H,L)=>String(pt(H,L))===String(B)))&&B!==null&&(F.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[re,c,v,ke]);const Ct=f.useCallback(b=>{Me&&I(b);const B=q.current;B&&B(b),E.current?.(),Y.current?.()},[Me]);f.useEffect(()=>{if(!K){xe(!1);return}xe(!0)},[K,t?.id]),f.useEffect(()=>{X!=="cursor"&&F.current!==null&&(F.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[X,v]);const Ne=f.useCallback((b,B)=>{const N=U.current;if(!N)return null;const H=N.screenToWorld(b,B);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,B)=>{const N=U.current;if(!N)return null;const H=N.worldToScreen(b,B);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]},[]),Rt=f.useCallback(()=>{U.current?.requestRender(),E.current?.(),Y.current?.()},[]),Pt=f.useMemo(()=>C??U.current?.getViewState()??null,[C]),St=f.useMemo(()=>{if(!t)return null;const b=Pt;return b?{source:t,viewState:b,drawTool:X,interactionLock:ie,worldToScreen:Et,screenToWorld:Ne,requestRedraw:Rt}:null},[t,Pt,X,ie,Et,Ne,Rt]),mn=f.useCallback(b=>{const B=b.target===ae.current,N=Ne(b.clientX,b.clientY);if(T){const tt=!!N&&N[0]>=0&&N[1]>=0&&!!t&&N[0]<=t.width&&N[1]<=t.height;T({coordinate:N,clientX:b.clientX,clientY:b.clientY,insideImage:tt})}if(X!=="cursor")return;if(!B){F.current!==null&&(F.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}));return}if(!N||!re.length)return;const H=Xt(N,re),L=H?.regionId??null,Q=F.current;String(Q)!==String(L)&&(F.current=L,pe(L),v?.({region:H?.region??null,regionId:L,regionIndex:H?.regionIndex??-1,coordinate:N}))},[X,re,Ne,v,T,t]),gn=f.useCallback(()=>{T?.({coordinate:null,clientX:-1,clientY:-1,insideImage:!1}),F.current!==null&&(F.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[v,T]),pn=f.useCallback(b=>{if(X!=="cursor"||b.target!==ae.current)return;if(!re.length){ke(null);return}const B=Ne(b.clientX,b.clientY);if(!B)return;const N=Xt(B,re);if(!N){ke(null);return}const H=c!==null&&String(c)===String(N.regionId)?null:N.regionId;ke(H),P?.({region:N.region,regionId:N.regionId,regionIndex:N.regionIndex,coordinate:B})},[X,re,Ne,P,c,ke]);return f.useEffect(()=>{const b=ae.current;if(!b||!t)return;const B=new fn(b,t,{onViewStateChange:Ct,onStats:Mt,onTileError:i,onContextLost:o,onContextRestored:s,authToken:w,ctrlDragRotate:m});return U.current=B,e&&B.setViewState(e),B.setInteractionLock(ie),Me&&I(B.getViewState()),()=>{B.destroy(),U.current=null}},[t,Mt,i,o,s,w,m,Ct,Me]),f.useEffect(()=>{const b=U.current;!b||!e||b.setViewState(e)},[e]),f.useEffect(()=>{const b=U.current;b&&b.fitToImage()},[l]),f.useEffect(()=>{const b=U.current;b&&b.resetRotation()},[h]),f.useEffect(()=>{const b=U.current;!b||!x||b.setPointPalette(x)},[x]),f.useEffect(()=>{const b=U.current;b&&b.setPointData(Oe)},[Oe]),f.useEffect(()=>{if(!W)return;const B=ln(R?Oe:g,re,{paletteIndexToTermId:we,includeEmptyRegions:!0});W(B)},[W,R,g,Oe,re,we]),f.useEffect(()=>{const b=U.current;b&&b.setInteractionLock(ie)},[ie]),me.jsxs("div",{className:J,style:Ce,onPointerMove:mn,onPointerLeave:gn,onClick:pn,children:[me.jsx("canvas",{ref:ae,className:"wsi-render-canvas",style:{position:"absolute",inset:0,zIndex:1,width:"100%",height:"100%",display:"block",touchAction:"none",cursor:X==="cursor"&&Te!==null?"pointer":ie?"crosshair":"grab"}}),t&&St&&Array.isArray(ce)&&ce.length>0?ce.map((b,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??B)):null,t?me.jsx(jt,{tool:X,enabled:X!=="cursor",imageWidth:t.width,imageHeight:t.height,imageMpp:t.mpp,imageZoom:t.maxTierZoom,stampOptions:Pe,projectorRef:U,viewStateSignal:e,persistedRegions:re,patchRegions:ne,regionStrokeStyle:he,regionStrokeHoverStyle:ue,regionStrokeActiveStyle:Se,patchStrokeStyle:Z,resolveRegionStrokeStyle:ee,overlayShapes:O,hoveredRegionId:Te,activeRegionId:c,regionLabelStyle:oe,invalidateRef:E,onDrawComplete:G,onPatchComplete:fe}):null,a?me.jsx("pre",{"data-open-plant-debug-overlay":!0,style:Ie,children:dn}):null,t&&K?Le?me.jsxs(me.Fragment,{children:[me.jsx(Jt,{source:t,projectorRef:U,authToken:w,options:V,invalidateRef:Y}),me.jsx("button",{type:"button","aria-label":"Hide overview map",onClick:()=>xe(!1),style:{position:"absolute",zIndex:6,...qe.includes("left")?{left:_e}:{right:_e},...qe.includes("top")?{top:_e+vt+8}:{bottom:_e+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:()=>xe(!0),style:{position:"absolute",zIndex:6,...qe.includes("left")?{left:_e}:{right:_e},...qe.includes("top")?{top:_e}:{bottom:_e},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=jt;exports.M1TileRenderer=Vt;exports.OverviewMap=Jt;exports.TileScheduler=hn;exports.TileViewerCanvas=Gn;exports.WsiTileRenderer=fn;exports.WsiViewerCanvas=br;exports.buildTermPalette=Pn;exports.calcScaleLength=Cn;exports.calcScaleResolution=wt;exports.clamp=$;exports.closeRing=be;exports.computeRoiPointGroups=ln;exports.createCircle=ft;exports.createRectangle=ht;exports.filterPointDataByPolygons=Ve;exports.filterPointDataByPolygonsHybrid=sn;exports.filterPointDataByPolygonsInWorker=cn;exports.filterPointIndicesByPolygons=nn;exports.filterPointIndicesByPolygonsInWorker=sr;exports.getWebGpuCapabilities=er;exports.hexToRgba=Ot;exports.isSameViewState=En;exports.normalizeImageInfo=On;exports.prefilterPointsByBoundsWebGpu=on;exports.terminateRoiClipWorker=or;exports.toBearerToken=Rn;exports.toTileUrl=yt;
|
|
126
126
|
//# sourceMappingURL=index.cjs.map
|