open-plant 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -1
- package/README.md +20 -2
- package/dist/assets/roi-clip-worker-DdVYCepx.js +2 -0
- package/dist/assets/roi-clip-worker-DdVYCepx.js.map +1 -0
- package/dist/index.cjs +7 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1336 -1265
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +9 -9
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/react/draw-layer.d.ts +10 -1
- package/dist/types/react/draw-layer.d.ts.map +1 -1
- package/dist/types/react/wsi-viewer-canvas.d.ts +24 -3
- package/dist/types/react/wsi-viewer-canvas.d.ts.map +1 -1
- package/dist/types/wsi/point-clip-worker-client.d.ts +5 -0
- package/dist/types/wsi/point-clip-worker-client.d.ts.map +1 -1
- package/dist/types/wsi/point-clip-worker-protocol.d.ts +17 -2
- package/dist/types/wsi/point-clip-worker-protocol.d.ts.map +1 -1
- package/dist/types/wsi/point-clip.d.ts +1 -0
- package/dist/types/wsi/point-clip.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/assets/roi-clip-worker-i1SE1Dpa.js +0 -2
- package/dist/assets/roi-clip-worker-i1SE1Dpa.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,10 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
- No changes yet.
|
|
11
|
+
|
|
12
|
+
## [1.2.2] - 2026-02-25
|
|
13
|
+
|
|
10
14
|
### Added
|
|
11
15
|
- Camera rotation support in `WsiViewState` via `rotationDeg`.
|
|
12
16
|
- `Ctrl/Cmd + drag` rotation input path with configurable renderer option.
|
|
@@ -22,6 +26,10 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
22
26
|
- Hybrid WebGPU draw bridge payload support via `WsiPointData.drawIndices`.
|
|
23
27
|
- Hybrid clip option `bridgeToDraw` and clip stat flag `bridgedToDraw`.
|
|
24
28
|
- Unit test coverage for ROI term stats with draw-index bridge input.
|
|
29
|
+
- Patch-intent draw path for `stamp-rectangle-4096px` with dedicated `onPatchComplete` callback.
|
|
30
|
+
- Patch overlay channel on viewer (`patchRegions`, `patchStrokeStyle`) separated from ROI hover/active interaction.
|
|
31
|
+
- Custom React overlay layer slots via `customLayers` for host-owned rendering pipelines.
|
|
32
|
+
- Point-index clipping primitives for export workflows: `filterPointIndicesByPolygons` and worker variant.
|
|
25
33
|
|
|
26
34
|
### Changed
|
|
27
35
|
- `WsiTileRenderer` projection, bounds, and zoom anchoring now account for rotation.
|
|
@@ -33,7 +41,7 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
33
41
|
- Publish gate now enforces `npm run release:gate` via `prepublishOnly`.
|
|
34
42
|
|
|
35
43
|
### Docs
|
|
36
|
-
- Updated EN/KO API and guides for rotation, pointer world callbacks, overlay shapes, 4096px
|
|
44
|
+
- Updated EN/KO API and guides for rotation, pointer world callbacks, overlay shapes, 4096px patch intent flow, custom layers, and ROI term stats.
|
|
37
45
|
- Updated `todo.md` gap table with current support status and code-path references.
|
|
38
46
|
- Added EN/KO migration guides with API stability/deprecation policy and release-gate contract.
|
|
39
47
|
- Added EN/KO contributing pages and linked them across docs navigation.
|
package/README.md
CHANGED
|
@@ -129,15 +129,31 @@ import { WsiViewerCanvas } from "open-plant";
|
|
|
129
129
|
clipPointsToRois
|
|
130
130
|
clipMode="worker"
|
|
131
131
|
onClipStats={(s) => console.log(s.mode, s.durationMs)}
|
|
132
|
-
drawTool="stamp-
|
|
132
|
+
drawTool="stamp-rectangle-4096px"
|
|
133
133
|
stampOptions={{
|
|
134
134
|
rectangleAreaMm2: 2,
|
|
135
135
|
circleAreaMm2: 0.2, // HPF 예시
|
|
136
136
|
rectanglePixelSize: 4096,
|
|
137
137
|
}}
|
|
138
|
+
patchRegions={patchRegions}
|
|
139
|
+
patchStrokeStyle={{ color: "#8ad8ff", lineDash: [10, 8], width: 2 }}
|
|
140
|
+
customLayers={[
|
|
141
|
+
{
|
|
142
|
+
id: "patch-labels",
|
|
143
|
+
render: ({ worldToScreen }) => {
|
|
144
|
+
/* host overlay */
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
]}
|
|
138
148
|
onPointerWorldMove={(e) => console.log(e.coordinate)}
|
|
139
149
|
onRoiPointGroups={(stats) => console.log(stats.groups)}
|
|
140
|
-
onDrawComplete={
|
|
150
|
+
onDrawComplete={(result) => {
|
|
151
|
+
if (result.intent === "roi") handleRoi(result);
|
|
152
|
+
}}
|
|
153
|
+
onPatchComplete={(patch) => {
|
|
154
|
+
// stamp-rectangle-4096px 전용
|
|
155
|
+
handlePatch(patch);
|
|
156
|
+
}}
|
|
141
157
|
onViewStateChange={handleViewChange}
|
|
142
158
|
onStats={setStats}
|
|
143
159
|
/>
|
|
@@ -164,6 +180,8 @@ Freehand, Rectangle, Circular + Stamp(사각형/원) 드로잉 오버레이.
|
|
|
164
180
|
| `normalizeImageInfo(raw, tileBaseUrl)` | API 응답 + 타일 베이스 URL을 `WsiImageSource`로 변환 |
|
|
165
181
|
| `filterPointDataByPolygons()` | ROI 폴리곤으로 포인트 필터링 |
|
|
166
182
|
| `filterPointDataByPolygonsInWorker()` | 워커 스레드 ROI 필터링 |
|
|
183
|
+
| `filterPointIndicesByPolygons()` | 폴리곤 내부 원본 포인트 인덱스 추출(패치 JSON export용) |
|
|
184
|
+
| `filterPointIndicesByPolygonsInWorker()` | 포인트 인덱스 추출 워커 버전 |
|
|
167
185
|
| `filterPointDataByPolygonsHybrid()` | WebGPU bbox prefilter + polygon 정밀 판정(실험) |
|
|
168
186
|
| `getWebGpuCapabilities()` | WebGPU 지원/어댑터 정보 조회 |
|
|
169
187
|
| `buildTermPalette()` | Term 기반 컬러 팔레트 생성 |
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
(function(){"use strict";function l(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function I(t){if(!Array.isArray(t)||t.length<3)return[];const n=t.map(([i,s])=>[i,s]),o=n[0],e=n[n.length-1];return!o||!e?[]:((o[0]!==e[0]||o[1]!==e[1])&&n.push([o[0],o[1]]),n)}function x(t){const n=[];for(const o of t??[]){const e=I(o);if(e.length<4)continue;let i=1/0,s=1/0,c=-1/0,a=-1/0;for(const[r,f]of e)r<i&&(i=r),r>c&&(c=r),f<s&&(s=f),f>a&&(a=f);!Number.isFinite(i)||!Number.isFinite(s)||n.push({ring:e,minX:i,minY:s,maxX:c,maxY:a})}return n}function A(t,n,o){let e=!1;for(let i=0,s=o.length-1;i<o.length;s=i,i+=1){const c=o[i][0],a=o[i][1],r=o[s][0],f=o[s][1];a>n!=f>n&&t<(r-c)*(n-a)/(f-a||Number.EPSILON)+c&&(e=!e)}return e}function m(t,n,o){for(const e of o)if(!(t<e.minX||t>e.maxX||n<e.minY||n>e.maxY)&&A(t,n,e.ring))return!0;return!1}function g(t){if(t instanceof Error)return t.message;try{return String(t)}catch{return"unknown worker error"}}const d=self;function b(t){const n=l(),o=Math.max(0,Math.floor(t.count)),e=new Float32Array(t.positions),i=new Uint16Array(t.paletteIndices),s=Math.floor(e.length/2),c=Math.max(0,Math.min(o,s,i.length)),a=x(t.polygons??[]);if(c===0||a.length===0)return{type:"roi-clip-success",id:t.id,count:0,positions:new Float32Array(0).buffer,paletteIndices:new Uint16Array(0).buffer,durationMs:l()-n};const r=new Float32Array(c*2),f=new Uint16Array(c);let u=0;for(let p=0;p<c;p+=1){const M=e[p*2],w=e[p*2+1];m(M,w,a)&&(r[u*2]=M,r[u*2+1]=w,f[u]=i[p],u+=1)}const y=r.slice(0,u*2),h=f.slice(0,u);return{type:"roi-clip-success",id:t.id,count:u,positions:y.buffer,paletteIndices:h.buffer,durationMs:l()-n}}function P(t){const n=l(),o=Math.max(0,Math.floor(t.count)),e=new Float32Array(t.positions),i=Math.floor(e.length/2),s=Math.max(0,Math.min(o,i)),c=x(t.polygons??[]);if(s===0||c.length===0)return{type:"roi-clip-index-success",id:t.id,count:0,indices:new Uint32Array(0).buffer,durationMs:l()-n};const a=new Uint32Array(s);let r=0;for(let u=0;u<s;u+=1){const y=e[u*2],h=e[u*2+1];m(y,h,c)&&(a[r]=u,r+=1)}const f=a.slice(0,r);return{type:"roi-clip-index-success",id:t.id,count:r,indices:f.buffer,durationMs:l()-n}}d.addEventListener("message",t=>{const n=t.data;if(!(!n||n.type!=="roi-clip-request"&&n.type!=="roi-clip-index-request"))try{if(n.type==="roi-clip-index-request"){const e=P(n);d.postMessage(e,[e.indices]);return}const o=b(n);d.postMessage(o,[o.positions,o.paletteIndices])}catch(o){const e={type:"roi-clip-failure",id:n.id,error:g(o)};d.postMessage(e)}})})();
|
|
2
|
+
//# sourceMappingURL=roi-clip-worker-DdVYCepx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roi-clip-worker-DdVYCepx.js","sources":["../src/workers/roi-clip-worker.ts"],"sourcesContent":["import type { RoiCoordinate, RoiPolygon } from \"../wsi/point-clip\";\nimport type {\n RoiClipWorkerDataRequest,\n RoiClipWorkerIndexRequest,\n RoiClipWorkerIndexSuccess,\n RoiClipWorkerRequest,\n RoiClipWorkerResponse,\n RoiClipWorkerSuccess,\n} from \"../wsi/point-clip-worker-protocol\";\n\ninterface PreparedPolygon {\n ring: RoiPolygon;\n minX: number;\n minY: number;\n maxX: number;\n maxY: number;\n}\n\nfunction nowMs(): number {\n if (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n return performance.now();\n }\n return Date.now();\n}\n\nfunction closeRing(coords: RoiPolygon): RoiPolygon {\n if (!Array.isArray(coords) || coords.length < 3) return [];\n const out = coords.map(([x, y]) => [x, y] as RoiCoordinate);\n const first = out[0];\n const last = out[out.length - 1];\n if (!first || !last) return [];\n if (first[0] !== last[0] || first[1] !== last[1]) {\n out.push([first[0], first[1]]);\n }\n return out;\n}\n\nfunction preparePolygons(polygons: RoiPolygon[]): PreparedPolygon[] {\n const prepared: PreparedPolygon[] = [];\n for (const poly of polygons ?? []) {\n const ring = closeRing(poly);\n if (ring.length < 4) continue;\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const [x, y] of ring) {\n if (x < minX) minX = x;\n if (x > maxX) maxX = x;\n if (y < minY) minY = y;\n if (y > maxY) maxY = y;\n }\n if (!Number.isFinite(minX) || !Number.isFinite(minY)) continue;\n prepared.push({ ring, minX, minY, maxX, maxY });\n }\n return prepared;\n}\n\nfunction isInsideRing(x: number, y: number, ring: RoiPolygon): boolean {\n let inside = false;\n for (let i = 0, j = ring.length - 1; i < ring.length; j = i, i += 1) {\n const xi = ring[i][0];\n const yi = ring[i][1];\n const xj = ring[j][0];\n const yj = ring[j][1];\n const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi || Number.EPSILON) + xi;\n if (intersect) inside = !inside;\n }\n return inside;\n}\n\nfunction isInsideAnyPolygon(x: number, y: number, polygons: PreparedPolygon[]): boolean {\n for (const poly of polygons) {\n if (x < poly.minX || x > poly.maxX || y < poly.minY || y > poly.maxY) {\n continue;\n }\n if (isInsideRing(x, y, poly.ring)) {\n return true;\n }\n }\n return false;\n}\n\nfunction toErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n try {\n return String(error);\n } catch {\n return \"unknown worker error\";\n }\n}\n\ninterface WorkerScope {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: \"message\", listener: (event: MessageEvent<RoiClipWorkerRequest>) => void): void;\n}\n\nconst workerScope = self as unknown as WorkerScope;\n\nfunction handleDataRequest(msg: RoiClipWorkerDataRequest): RoiClipWorkerSuccess {\n const start = nowMs();\n const count = Math.max(0, Math.floor(msg.count));\n const positions = new Float32Array(msg.positions);\n const terms = new Uint16Array(msg.paletteIndices);\n\n const maxCountByPositions = Math.floor(positions.length / 2);\n const safeCount = Math.max(0, Math.min(count, maxCountByPositions, terms.length));\n const prepared = preparePolygons(msg.polygons ?? []);\n\n if (safeCount === 0 || prepared.length === 0) {\n return {\n type: \"roi-clip-success\",\n id: msg.id,\n count: 0,\n positions: new Float32Array(0).buffer,\n paletteIndices: new Uint16Array(0).buffer,\n durationMs: nowMs() - start,\n };\n }\n\n const nextPositions = new Float32Array(safeCount * 2);\n const nextTerms = new Uint16Array(safeCount);\n let cursor = 0;\n\n for (let i = 0; i < safeCount; i += 1) {\n const x = positions[i * 2];\n const y = positions[i * 2 + 1];\n if (!isInsideAnyPolygon(x, y, prepared)) continue;\n nextPositions[cursor * 2] = x;\n nextPositions[cursor * 2 + 1] = y;\n nextTerms[cursor] = terms[i];\n cursor += 1;\n }\n\n const outPositions = nextPositions.slice(0, cursor * 2);\n const outTerms = nextTerms.slice(0, cursor);\n\n return {\n type: \"roi-clip-success\",\n id: msg.id,\n count: cursor,\n positions: outPositions.buffer,\n paletteIndices: outTerms.buffer,\n durationMs: nowMs() - start,\n };\n}\n\nfunction handleIndexRequest(msg: RoiClipWorkerIndexRequest): RoiClipWorkerIndexSuccess {\n const start = nowMs();\n const count = Math.max(0, Math.floor(msg.count));\n const positions = new Float32Array(msg.positions);\n const maxCountByPositions = Math.floor(positions.length / 2);\n const safeCount = Math.max(0, Math.min(count, maxCountByPositions));\n const prepared = preparePolygons(msg.polygons ?? []);\n\n if (safeCount === 0 || prepared.length === 0) {\n return {\n type: \"roi-clip-index-success\",\n id: msg.id,\n count: 0,\n indices: new Uint32Array(0).buffer,\n durationMs: nowMs() - start,\n };\n }\n\n const out = new Uint32Array(safeCount);\n let cursor = 0;\n for (let i = 0; i < safeCount; i += 1) {\n const x = positions[i * 2];\n const y = positions[i * 2 + 1];\n if (!isInsideAnyPolygon(x, y, prepared)) continue;\n out[cursor] = i;\n cursor += 1;\n }\n\n const outIndices = out.slice(0, cursor);\n return {\n type: \"roi-clip-index-success\",\n id: msg.id,\n count: cursor,\n indices: outIndices.buffer,\n durationMs: nowMs() - start,\n };\n}\n\nworkerScope.addEventListener(\"message\", (event: MessageEvent<RoiClipWorkerRequest>) => {\n const data = event.data;\n if (!data || (data.type !== \"roi-clip-request\" && data.type !== \"roi-clip-index-request\")) return;\n\n try {\n if (data.type === \"roi-clip-index-request\") {\n const response = handleIndexRequest(data);\n workerScope.postMessage(response, [response.indices]);\n return;\n }\n const response = handleDataRequest(data);\n workerScope.postMessage(response, [response.positions, response.paletteIndices]);\n } catch (error) {\n const fail: RoiClipWorkerResponse = {\n type: \"roi-clip-failure\",\n id: data.id,\n error: toErrorMessage(error),\n };\n workerScope.postMessage(fail);\n }\n});\n"],"names":["nowMs","closeRing","coords","out","x","y","first","last","preparePolygons","polygons","prepared","poly","ring","minX","minY","maxX","maxY","isInsideRing","inside","j","xi","yi","xj","yj","isInsideAnyPolygon","toErrorMessage","error","workerScope","handleDataRequest","msg","start","count","positions","terms","maxCountByPositions","safeCount","nextPositions","nextTerms","cursor","i","outPositions","outTerms","handleIndexRequest","outIndices","event","data","response","fail"],"mappings":"yBAkBA,SAASA,GAAgB,CACvB,OAAI,OAAO,YAAgB,KAAe,OAAO,YAAY,KAAQ,WAC5D,YAAY,IAAA,EAEd,KAAK,IAAA,CACd,CAEA,SAASC,EAAUC,EAAgC,CACjD,GAAI,CAAC,MAAM,QAAQA,CAAM,GAAKA,EAAO,OAAS,EAAG,MAAO,CAAA,EACxD,MAAMC,EAAMD,EAAO,IAAI,CAAC,CAACE,EAAGC,CAAC,IAAM,CAACD,EAAGC,CAAC,CAAkB,EACpDC,EAAQH,EAAI,CAAC,EACbI,EAAOJ,EAAIA,EAAI,OAAS,CAAC,EAC/B,MAAI,CAACG,GAAS,CAACC,EAAa,CAAA,IACxBD,EAAM,CAAC,IAAMC,EAAK,CAAC,GAAKD,EAAM,CAAC,IAAMC,EAAK,CAAC,IAC7CJ,EAAI,KAAK,CAACG,EAAM,CAAC,EAAGA,EAAM,CAAC,CAAC,CAAC,EAExBH,EACT,CAEA,SAASK,EAAgBC,EAA2C,CAClE,MAAMC,EAA8B,CAAA,EACpC,UAAWC,KAAQF,GAAY,GAAI,CACjC,MAAMG,EAAOX,EAAUU,CAAI,EAC3B,GAAIC,EAAK,OAAS,EAAG,SACrB,IAAIC,EAAO,IACPC,EAAO,IACPC,EAAO,KACPC,EAAO,KACX,SAAW,CAACZ,EAAGC,CAAC,IAAKO,EACfR,EAAIS,IAAMA,EAAOT,GACjBA,EAAIW,IAAMA,EAAOX,GACjBC,EAAIS,IAAMA,EAAOT,GACjBA,EAAIW,IAAMA,EAAOX,GAEnB,CAAC,OAAO,SAASQ,CAAI,GAAK,CAAC,OAAO,SAASC,CAAI,GACnDJ,EAAS,KAAK,CAAE,KAAAE,EAAM,KAAAC,EAAM,KAAAC,EAAM,KAAAC,EAAM,KAAAC,EAAM,CAChD,CACA,OAAON,CACT,CAEA,SAASO,EAAab,EAAWC,EAAWO,EAA2B,CACrE,IAAIM,EAAS,GACb,QAAS,EAAI,EAAGC,EAAIP,EAAK,OAAS,EAAG,EAAIA,EAAK,OAAQO,EAAI,EAAG,GAAK,EAAG,CACnE,MAAMC,EAAKR,EAAK,CAAC,EAAE,CAAC,EACdS,EAAKT,EAAK,CAAC,EAAE,CAAC,EACdU,EAAKV,EAAKO,CAAC,EAAE,CAAC,EACdI,EAAKX,EAAKO,CAAC,EAAE,CAAC,EACFE,EAAKhB,GAAMkB,EAAKlB,GAAKD,GAAMkB,EAAKF,IAAOf,EAAIgB,IAAQE,EAAKF,GAAM,OAAO,SAAWD,MAC1E,CAACF,EAC3B,CACA,OAAOA,CACT,CAEA,SAASM,EAAmBpB,EAAWC,EAAWI,EAAsC,CACtF,UAAWE,KAAQF,EACjB,GAAI,EAAAL,EAAIO,EAAK,MAAQP,EAAIO,EAAK,MAAQN,EAAIM,EAAK,MAAQN,EAAIM,EAAK,OAG5DM,EAAab,EAAGC,EAAGM,EAAK,IAAI,EAC9B,MAAO,GAGX,MAAO,EACT,CAEA,SAASc,EAAeC,EAAwB,CAC9C,GAAIA,aAAiB,MAAO,OAAOA,EAAM,QACzC,GAAI,CACF,OAAO,OAAOA,CAAK,CACrB,MAAQ,CACN,MAAO,sBACT,CACF,CAOA,MAAMC,EAAc,KAEpB,SAASC,EAAkBC,EAAqD,CAC9E,MAAMC,EAAQ9B,EAAA,EACR+B,EAAQ,KAAK,IAAI,EAAG,KAAK,MAAMF,EAAI,KAAK,CAAC,EACzCG,EAAY,IAAI,aAAaH,EAAI,SAAS,EAC1CI,EAAQ,IAAI,YAAYJ,EAAI,cAAc,EAE1CK,EAAsB,KAAK,MAAMF,EAAU,OAAS,CAAC,EACrDG,EAAY,KAAK,IAAI,EAAG,KAAK,IAAIJ,EAAOG,EAAqBD,EAAM,MAAM,CAAC,EAC1EvB,EAAWF,EAAgBqB,EAAI,UAAY,CAAA,CAAE,EAEnD,GAAIM,IAAc,GAAKzB,EAAS,SAAW,EACzC,MAAO,CACL,KAAM,mBACN,GAAImB,EAAI,GACR,MAAO,EACP,UAAW,IAAI,aAAa,CAAC,EAAE,OAC/B,eAAgB,IAAI,YAAY,CAAC,EAAE,OACnC,WAAY7B,IAAU8B,CAAA,EAI1B,MAAMM,EAAgB,IAAI,aAAaD,EAAY,CAAC,EAC9CE,EAAY,IAAI,YAAYF,CAAS,EAC3C,IAAIG,EAAS,EAEb,QAASC,EAAI,EAAGA,EAAIJ,EAAWI,GAAK,EAAG,CACrC,MAAMnC,EAAI4B,EAAUO,EAAI,CAAC,EACnBlC,EAAI2B,EAAUO,EAAI,EAAI,CAAC,EACxBf,EAAmBpB,EAAGC,EAAGK,CAAQ,IACtC0B,EAAcE,EAAS,CAAC,EAAIlC,EAC5BgC,EAAcE,EAAS,EAAI,CAAC,EAAIjC,EAChCgC,EAAUC,CAAM,EAAIL,EAAMM,CAAC,EAC3BD,GAAU,EACZ,CAEA,MAAME,EAAeJ,EAAc,MAAM,EAAGE,EAAS,CAAC,EAChDG,EAAWJ,EAAU,MAAM,EAAGC,CAAM,EAE1C,MAAO,CACL,KAAM,mBACN,GAAIT,EAAI,GACR,MAAOS,EACP,UAAWE,EAAa,OACxB,eAAgBC,EAAS,OACzB,WAAYzC,IAAU8B,CAAA,CAE1B,CAEA,SAASY,EAAmBb,EAA2D,CACrF,MAAMC,EAAQ9B,EAAA,EACR+B,EAAQ,KAAK,IAAI,EAAG,KAAK,MAAMF,EAAI,KAAK,CAAC,EACzCG,EAAY,IAAI,aAAaH,EAAI,SAAS,EAC1CK,EAAsB,KAAK,MAAMF,EAAU,OAAS,CAAC,EACrDG,EAAY,KAAK,IAAI,EAAG,KAAK,IAAIJ,EAAOG,CAAmB,CAAC,EAC5DxB,EAAWF,EAAgBqB,EAAI,UAAY,CAAA,CAAE,EAEnD,GAAIM,IAAc,GAAKzB,EAAS,SAAW,EACzC,MAAO,CACL,KAAM,yBACN,GAAImB,EAAI,GACR,MAAO,EACP,QAAS,IAAI,YAAY,CAAC,EAAE,OAC5B,WAAY7B,IAAU8B,CAAA,EAI1B,MAAM3B,EAAM,IAAI,YAAYgC,CAAS,EACrC,IAAIG,EAAS,EACb,QAASC,EAAI,EAAGA,EAAIJ,EAAWI,GAAK,EAAG,CACrC,MAAMnC,EAAI4B,EAAUO,EAAI,CAAC,EACnBlC,EAAI2B,EAAUO,EAAI,EAAI,CAAC,EACxBf,EAAmBpB,EAAGC,EAAGK,CAAQ,IACtCP,EAAImC,CAAM,EAAIC,EACdD,GAAU,EACZ,CAEA,MAAMK,EAAaxC,EAAI,MAAM,EAAGmC,CAAM,EACtC,MAAO,CACL,KAAM,yBACN,GAAIT,EAAI,GACR,MAAOS,EACP,QAASK,EAAW,OACpB,WAAY3C,IAAU8B,CAAA,CAE1B,CAEAH,EAAY,iBAAiB,UAAYiB,GAA8C,CACrF,MAAMC,EAAOD,EAAM,KACnB,GAAI,GAACC,GAASA,EAAK,OAAS,oBAAsBA,EAAK,OAAS,0BAEhE,GAAI,CACF,GAAIA,EAAK,OAAS,yBAA0B,CAC1C,MAAMC,EAAWJ,EAAmBG,CAAI,EACxClB,EAAY,YAAYmB,EAAU,CAACA,EAAS,OAAO,CAAC,EACpD,MACF,CACA,MAAMA,EAAWlB,EAAkBiB,CAAI,EACvClB,EAAY,YAAYmB,EAAU,CAACA,EAAS,UAAWA,EAAS,cAAc,CAAC,CACjF,OAASpB,EAAO,CACd,MAAMqB,EAA8B,CAClC,KAAM,mBACN,GAAIF,EAAK,GACT,MAAOpB,EAAeC,CAAK,CAAA,EAE7BC,EAAY,YAAYoB,CAAI,CAC9B,CACF,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var mn=Object.defineProperty;var gn=(t,e,n)=>e in t?mn(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var d=(t,e,n)=>gn(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const me=require("react/jsx-runtime"),f=require("react");var nt=typeof document<"u"?document.currentScript:null;function Rt(t,e,n){const r=t.createShader(e);if(!r)throw new Error("Failed to create shader.");if(t.shaderSource(r,n),t.compileShader(r),!t.getShaderParameter(r,t.COMPILE_STATUS)){const o=t.getShaderInfoLog(r)??"unknown shader error";throw t.deleteShader(r),new Error(o)}return r}function pn(t,e,n){const r=Rt(t,t.VERTEX_SHADER,e),i=Rt(t,t.FRAGMENT_SHADER,n),o=t.createProgram();if(!o)throw t.deleteShader(r),t.deleteShader(i),new Error("Failed to create program.");if(t.attachShader(o,r),t.attachShader(o,i),t.linkProgram(o),t.deleteShader(r),t.deleteShader(i),!t.getProgramParameter(o,t.LINK_STATUS)){const a=t.getProgramInfoLog(o)??"unknown link error";throw t.deleteProgram(o),new Error(a)}return o}function rt(t,e,n){const r=t.getUniformLocation(e,n);if(!r)throw new Error(`Failed to get uniform location: ${n}`);return r}function bn(t){const e=t.getContext("webgl2",{alpha:!1,antialias:!1,depth:!1,stencil:!1,preserveDrawingBuffer:!1,powerPreference:"high-performance"});if(!e)throw new Error("WebGL2 is not available.");return e}let wn=class{constructor(){d(this,"viewportWidth",1);d(this,"viewportHeight",1);d(this,"viewState",{offsetX:0,offsetY:0,zoom:1})}setViewport(e,n){this.viewportWidth=Math.max(1,e),this.viewportHeight=Math.max(1,n)}getViewportSize(){return{width:this.viewportWidth,height:this.viewportHeight}}setViewState(e){e.offsetX!==void 0&&(this.viewState.offsetX=e.offsetX),e.offsetY!==void 0&&(this.viewState.offsetY=e.offsetY),e.zoom!==void 0&&(this.viewState.zoom=Math.max(1e-4,e.zoom))}getViewState(){return{...this.viewState}}getMatrix(){const e=this.viewportWidth/this.viewState.zoom,n=this.viewportHeight/this.viewState.zoom,r=2/e,i=-2/n,o=-1-this.viewState.offsetX*r,s=1-this.viewState.offsetY*i;return new Float32Array([r,0,0,0,i,0,o,s,1])}};const yn=`#version 300 es
|
|
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
|
+
`,xn=`#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 St{constructor(e){d(this,"canvas");d(this,"gl");d(this,"camera",new Jt);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=Kt(this.canvas),this.program=jt(this.gl,Qt,en);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=Ze(this.gl,this.program,"uCamera"),this.uBoundsLocation=Ze(this.gl,this.program,"uBounds"),this.uTextureLocation=Ze(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 at=[160,160,160,255];function q(t,e,n){return Math.max(e,Math.min(n,t))}function ut(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*ut(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 nn(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 At(t){const n=String(t??"").trim().match(/^#?([0-9a-fA-F]{6})$/);if(!n)return[...at];const r=Number.parseInt(n[1],16);return[r>>16&255,r>>8&255,r&255,255]}function on(t){const e=[[...at]],n=new Map;for(const i of t??[]){const o=String(i?.termId??"");!o||n.has(o)||(n.set(o,e.length),e.push(At(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 pt(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,un=2,It=96,cn=1,ln=[],tt=[],bt=1e3,_t=2,Bt=2,hn=4096,fn=.2,Pe={color:"#ff4d4f",width:2,lineJoin:"round",lineCap:"round",shadowColor:"rgba(0, 0, 0, 0)",shadowBlur:0,shadowOffsetX:0,shadowOffsetY:0},ge={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 ke(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 je(t,e){return typeof t!="number"||!Number.isFinite(t)||t<=0?e:t}function dn(t){return{rectangleAreaMm2:je(t?.rectangleAreaMm2,_t),circleAreaMm2:je(t?.circleAreaMm2,Bt),rectanglePixelSize:je(t?.rectanglePixelSize,hn)}}function mn(t){return t*bt*bt}function wt(t,e){return!t||!Number.isFinite(e)||e<=0?[]:xe([[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 gn(t,e,n=It){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 xe(r)}function xe(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 nt(t,e){return!t||!e?[]:xe([[t[0],t[1]],[e[0],t[1]],[e[0],e[1]],[t[0],e[1]]])}function rt(t,e,n=It){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 l=a/n*Math.PI*2;s.push([r+Math.cos(l)*o,i+Math.sin(l)*o])}return xe(s)}function it(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 yt(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 xt(t){return Array.isArray(t)&&t.length>=4&&it(t)>cn}function _e(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=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(tt),t.shadowColor="rgba(0, 0, 0, 0)",t.shadowBlur=0,t.shadowOffsetX=0,t.shadowOffsetY=0}}function Ut(t){const e=Array.isArray(t?.lineDash)?t.lineDash.filter(s=>Number.isFinite(s)&&s>=0):tt,n=typeof t?.width=="number"&&Number.isFinite(t.width)?Math.max(0,t.width):Pe.width,r=typeof t?.shadowBlur=="number"&&Number.isFinite(t.shadowBlur)?Math.max(0,t.shadowBlur):Pe.shadowBlur,i=typeof t?.shadowOffsetX=="number"&&Number.isFinite(t.shadowOffsetX)?t.shadowOffsetX:Pe.shadowOffsetX,o=typeof t?.shadowOffsetY=="number"&&Number.isFinite(t.shadowOffsetY)?t.shadowOffsetY:Pe.shadowOffsetY;return{color:t?.color||Pe.color,width:n,lineDash:e.length?e:tt,lineJoin:t?.lineJoin||Pe.lineJoin,lineCap:t?.lineCap||Pe.lineCap,shadowColor:t?.shadowColor||Pe.shadowColor,shadowBlur:r,shadowOffsetX:i,shadowOffsetY:o}}function Ne(t,e){return e?Ut({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 Tt(t,e){return t==null||e===null||e===void 0?!1:String(t)===String(e)}function pn(t){const e=typeof t?.paddingX=="number"&&Number.isFinite(t.paddingX)?Math.max(0,t.paddingX):ge.paddingX,n=typeof t?.paddingY=="number"&&Number.isFinite(t.paddingY)?Math.max(0,t.paddingY):ge.paddingY,r=typeof t?.fontSize=="number"&&Number.isFinite(t.fontSize)?Math.max(8,t.fontSize):ge.fontSize,i=typeof t?.borderWidth=="number"&&Number.isFinite(t.borderWidth)?Math.max(0,t.borderWidth):ge.borderWidth,o=typeof t?.offsetY=="number"&&Number.isFinite(t.offsetY)?t.offsetY:ge.offsetY,s=typeof t?.borderRadius=="number"&&Number.isFinite(t.borderRadius)?Math.max(0,t.borderRadius):ge.borderRadius;return{fontFamily:t?.fontFamily||ge.fontFamily,fontSize:r,fontWeight:t?.fontWeight||ge.fontWeight,textColor:t?.textColor||ge.textColor,backgroundColor:t?.backgroundColor||ge.backgroundColor,borderColor:t?.borderColor||ge.borderColor,borderWidth:i,paddingX:e,paddingY:n,offsetY:o,borderRadius:s}}function bn(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 yn(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 l=t.measureText(s).width+o.paddingX*2,u=o.fontSize+o.paddingY*2,h=qe(n[0],l*.5+1,r-l*.5-1),b=qe(n[1]-o.offsetY,u*.5+1,i-u*.5-1),m=h-l*.5,p=b-u*.5;t.fillStyle=o.backgroundColor,t.strokeStyle=o.borderColor,t.lineWidth=o.borderWidth,bn(t,m,p,l,u,o.borderRadius),t.fill(),o.borderWidth>0&&t.stroke(),t.fillStyle=o.textColor,t.fillText(s,h,b+.5),t.restore()}function Ke(t,e,n){return[qe(t[0],0,e),qe(t[1],0,n)]}function Je(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 Dt({tool:t,imageWidth:e,imageHeight:n,imageMpp:r,imageZoom:i,stampOptions:o,projectorRef:s,onDrawComplete:a,enabled:l,viewStateSignal:u,persistedRegions:h,persistedPolygons:b,regionStrokeStyle:m,regionStrokeHoverStyle:p,regionStrokeActiveStyle:x,resolveRegionStrokeStyle:w,overlayShapes:T,hoveredRegionId:S=null,activeRegionId:F=null,regionLabelStyle:I,invalidateRef:L,className:pe,style:ae}){const z=f.useRef(null),be=f.useRef(!1),ue=f.useRef(t),Q=f.useRef({isDrawing:!1,pointerId:null,start:null,current:null,points:[],stampCenter:null}),$=l??t!=="cursor",X=f.useMemo(()=>h&&h.length>0?h:!b||b.length===0?ln:b.map((c,g)=>({id:g,coordinates:c})),[h,b]),j=f.useMemo(()=>Ut(m),[m]),Te=f.useMemo(()=>Ne(j,p),[j,p]),we=f.useMemo(()=>Ne(j,x),[j,x]),oe=f.useMemo(()=>pn(I),[I]),ye=f.useMemo(()=>dn(o),[o]),P=f.useMemo(()=>({position:"absolute",inset:0,zIndex:2,width:"100%",height:"100%",display:"block",touchAction:"none",pointerEvents:$?"auto":"none",cursor:$?"crosshair":"default",...ae}),[$,ae]),R=f.useCallback(()=>{const c=z.current;if(!c)return;const g=c.getBoundingClientRect(),M=Math.max(1,window.devicePixelRatio||1),E=Math.max(1,Math.round(g.width*M)),k=Math.max(1,Math.round(g.height*M));(c.width!==E||c.height!==k)&&(c.width=E,c.height=k)},[]),C=f.useCallback(c=>{const g=s.current;if(!g||c.length===0)return[];const M=new Array(c.length);for(let E=0;E<c.length;E+=1){const k=Je(g.worldToScreen(c[E][0],c[E][1]));if(!k)return[];M[E]=k}return M},[s]),_=f.useCallback(c=>{if(!Number.isFinite(c)||c<=0)return 0;const g=typeof r=="number"&&Number.isFinite(r)&&r>0?r:1,M=typeof i=="number"&&Number.isFinite(i)?i:0,E=s.current?.getViewState?.().zoom,k=typeof E=="number"&&Number.isFinite(E)&&E>0?E:1,v=M+Math.log2(k),B=Math.max(1e-9,ut(g,M,v));return c/B/k},[r,i,s]),V=f.useCallback((c,g)=>{if(!g)return[];let M=0;if(c==="stamp-rectangle-4096px"){const v=ye.rectanglePixelSize*.5;return wt(g,v).map(O=>Ke(O,e,n))}if(c==="stamp-rectangle"||c==="stamp-rectangle-2mm2"?M=c==="stamp-rectangle-2mm2"?_t:ye.rectangleAreaMm2:(c==="stamp-circle"||c==="stamp-circle-2mm2"||c==="stamp-circle-hpf-0.2mm2")&&(M=c==="stamp-circle-hpf-0.2mm2"?fn:c==="stamp-circle-2mm2"?Bt:ye.circleAreaMm2),!Number.isFinite(M)||M<=0)return[];const E=mn(M);let k=[];if(c==="stamp-rectangle"||c==="stamp-rectangle-2mm2"){const v=_(Math.sqrt(E)*.5);k=wt(g,v)}else if(c==="stamp-circle"||c==="stamp-circle-2mm2"||c==="stamp-circle-hpf-0.2mm2"){const v=_(Math.sqrt(E/Math.PI));k=gn(g,v)}return k.length?k.map(v=>Ke(v,e,n)):[]},[_,e,n,ye]),te=f.useCallback(()=>{const c=Q.current;return ke(t)?V(t,c.stampCenter):c.isDrawing?t==="freehand"?c.points:t==="rectangle"?nt(c.start,c.current):t==="circular"?rt(c.start,c.current):[]:[]},[t,V]),ne=f.useCallback(()=>{R();const c=z.current;if(!c)return;const g=c.getContext("2d");if(!g)return;const M=Math.max(1,window.devicePixelRatio||1),E=c.width/M,k=c.height/M;if(g.setTransform(1,0,0,1,0,0),g.clearRect(0,0,c.width,c.height),g.setTransform(M,0,0,M,0,0),X.length>0)for(let v=0;v<X.length;v+=1){const B=X[v],O=B?.coordinates;if(!O||O.length<3)continue;const J=xe(O),le=C(J);if(le.length>=4){const Re=B.id??v,Z=Tt(F,Re)?"active":Tt(S,Re)?"hover":"default";let me=Z==="active"?we:Z==="hover"?Te:j;if(w){const Se=w({region:B,regionId:Re,regionIndex:v,state:Z});me=Ne(me,Se||void 0)}_e(g,le,me,!0,!1)}}if(Array.isArray(T)&&T.length>0)for(let v=0;v<T.length;v+=1){const B=T[v];if(!B?.coordinates?.length)continue;const O=B.closed??!1,J=O?xe(B.coordinates):B.coordinates,le=C(J);if(le.length<2)continue;const Re=Ne(j,B.strokeStyle);_e(g,le,Re,O,B.fill??!1)}if($){const v=te();if(v.length>0)if(t==="freehand"){const B=C(v);B.length>=2&&_e(g,B,j,!1,!1),B.length>=3&&_e(g,C(xe(v)),j,!0,!0)}else{const B=C(v);B.length>=4&&_e(g,B,j,!0,!0)}}if(X.length>0)for(const v of X){if(!v.label)continue;const B=v?.coordinates;if(!B||B.length<3)continue;const O=xe(B),J=wn(O);if(!J)continue;const le=Je(s.current?.worldToScreen(J[0],J[1])??[]);le&&yn(g,v.label,le,E,k,oe)}},[$,t,te,R,C,s,X,T,S,F,j,Te,we,w,oe]),A=f.useCallback(()=>{be.current||(be.current=!0,requestAnimationFrame(()=>{be.current=!1,ne()}))},[ne]),W=f.useCallback(()=>{const c=Q.current,g=z.current;if(g&&c.pointerId!==null&&g.hasPointerCapture(c.pointerId))try{g.releasePointerCapture(c.pointerId)}catch{}c.isDrawing=!1,c.pointerId=null,c.start=null,c.current=null,c.points=[],c.stampCenter=null},[]),re=f.useCallback(c=>{const g=s.current;if(!g||e<=0||n<=0)return null;const M=Je(g.screenToWorld(c.clientX,c.clientY));return M?Ke(M,e,n):null},[s,e,n]),ce=f.useCallback(()=>{const c=Q.current;if(!c.isDrawing){W(),A();return}let g=[];t==="freehand"?c.points.length>=an&&(g=xe(c.points)):t==="rectangle"?g=nt(c.start,c.current):t==="circular"&&(g=rt(c.start,c.current)),(t==="freehand"||t==="rectangle"||t==="circular")&&xt(g)&&a&&a({tool:t,coordinates:g,bbox:yt(g),areaPx:it(g)}),W(),A()},[t,a,W,A]),Y=f.useCallback((c,g)=>{const M=V(c,g);!xt(M)||!a||a({tool:c,coordinates:M,bbox:yt(M),areaPx:it(M)})},[V,a]),H=f.useCallback(c=>{if(!$||t==="cursor"||c.button!==0)return;const g=re(c);if(!g)return;if(c.preventDefault(),c.stopPropagation(),ke(t)){const k=Q.current;k.stampCenter=g,Y(t,g),A();return}const M=z.current;M&&M.setPointerCapture(c.pointerId);const E=Q.current;E.isDrawing=!0,E.pointerId=c.pointerId,E.start=g,E.current=g,E.points=t==="freehand"?[g]:[],A()},[$,t,re,Y,A]),ee=f.useCallback(c=>{if(!$||t==="cursor")return;const g=re(c);if(!g)return;if(ke(t)){const E=Q.current;E.stampCenter=g,c.preventDefault(),c.stopPropagation(),A();return}const M=Q.current;if(!(!M.isDrawing||M.pointerId!==c.pointerId)){if(c.preventDefault(),c.stopPropagation(),t==="freehand"){const E=s.current,k=Math.max(1e-6,E?.getViewState?.().zoom??1),v=un/k,B=v*v,O=M.points[M.points.length-1];if(!O)M.points.push(g);else{const J=g[0]-O[0],le=g[1]-O[1];J*J+le*le>=B&&M.points.push(g)}}else M.current=g;A()}},[$,t,re,A,s]),G=f.useCallback(c=>{const g=Q.current;if(!g.isDrawing||g.pointerId!==c.pointerId)return;c.preventDefault(),c.stopPropagation();const M=z.current;if(M&&M.hasPointerCapture(c.pointerId))try{M.releasePointerCapture(c.pointerId)}catch{}ce()},[ce]),K=f.useCallback(()=>{if(!ke(t))return;const c=Q.current;c.stampCenter&&(c.stampCenter=null,A())},[t,A]);return f.useEffect(()=>{R(),A();const c=z.current;if(!c)return;const g=new ResizeObserver(()=>{R(),A()});return g.observe(c),()=>{g.disconnect()}},[R,A]),f.useEffect(()=>{$||W(),A()},[$,A,W]),f.useEffect(()=>{ue.current!==t&&(ue.current=t,W(),A())},[t,W,A]),f.useEffect(()=>{A()},[u,X,T,A]),f.useEffect(()=>{if(L)return L.current=A,()=>{L.current===A&&(L.current=null)}},[L,A]),f.useEffect(()=>{if(!$)return;const c=g=>{g.key==="Escape"&&(W(),A())};return window.addEventListener("keydown",c),()=>{window.removeEventListener("keydown",c)}},[$,W,A]),de.jsx("canvas",{ref:z,className:pe,style:P,onPointerDown:H,onPointerMove:ee,onPointerUp:G,onPointerCancel:G,onPointerLeave:K,onContextMenu:c=>{$&&c.preventDefault()},onWheel:c=>{$&&c.preventDefault()}})}function vt(t){return String(t??"").replace(/\/+$/,"")}function Ft(t){const e=String(t??"");return e.startsWith("/")?e:`/${e}`}function xn(t){const e=vt(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=vt(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 Tn(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),l=String(n.path??t?.path??""),u=Number(n.mpp??t?.mpp??0);if(!i||!o||!s||!l)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??"")})):[],b=Ft(l),m=xn(e),p=r?(x,w,T)=>`${m}${b}/${x}/${T}_${w}.webp`:void 0;return{id:t?._id||"unknown",name:t?.name||"unknown",width:i,height:o,mpp:Number.isFinite(u)&&u>0?u:void 0,tileSize:s,maxTierZoom:Number.isFinite(a)?Math.max(0,Math.floor(a)):0,tilePath:l,tileBaseUrl:e,terms:h,tileUrlBuilder:p}}function ct(t,e,n,r){if(t.tileUrlBuilder)return t.tileUrlBuilder(e,n,r);const i=Ft(t.tilePath);return`${t.tileBaseUrl}${i}/${e}/${r}_${n}.webp`}const he={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 Ie(t,e,n=1){return typeof t!="number"||!Number.isFinite(t)?e:Math.max(n,t)}function ze(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 Lt({source:t,projectorRef:e,authToken:n="",options:r,invalidateRef:i,className:o,style:s}){const a=f.useRef(null),l=f.useRef(null),u=f.useRef(null),h=f.useRef({active:!1,pointerId:null}),b=f.useRef(null),m=f.useRef(!1),p=Ie(r?.width,he.width,64),x=Ie(r?.height,he.height,48),w=Ie(r?.margin,he.margin,0),T=Ie(r?.borderRadius,he.borderRadius,0),S=Ie(r?.borderWidth,he.borderWidth,0),F=Math.max(1,Math.round(Ie(r?.maxThumbnailTiles,he.maxThumbnailTiles,1))),I=r?.backgroundColor||he.backgroundColor,L=r?.borderColor||he.borderColor,pe=r?.viewportStrokeColor||he.viewportStrokeColor,ae=r?.viewportFillColor||he.viewportFillColor,z=r?.interactive??he.interactive,be=r?.showThumbnail??he.showThumbnail,ue=r?.position||he.position,Q=f.useMemo(()=>{const P={};return ue==="top-left"||ue==="bottom-left"?P.left=w:P.right=w,ue==="top-left"||ue==="top-right"?P.top=w:P.bottom=w,{position:"absolute",...P,width:p,height:x,borderRadius:T,overflow:"hidden",zIndex:4,pointerEvents:z?"auto":"none",touchAction:"none",boxShadow:"0 10px 22px rgba(0, 0, 0, 0.3)",...s}},[w,ue,p,x,T,z,s]),$=f.useCallback(()=>{const P=a.current;if(!P)return;const R=P.getContext("2d");if(!R)return;const C=p,_=x,V=Math.max(1,window.devicePixelRatio||1),te=Math.max(1,Math.round(C*V)),ne=Math.max(1,Math.round(_*V));(P.width!==te||P.height!==ne)&&(P.width=te,P.height=ne),R.setTransform(1,0,0,1,0,0),R.clearRect(0,0,P.width,P.height),R.setTransform(V,0,0,V,0,0),R.fillStyle=I,R.fillRect(0,0,C,_);const A=l.current;A&&R.drawImage(A,0,0,C,_),R.strokeStyle=L,R.lineWidth=S,R.strokeRect(S*.5,S*.5,C-S,_-S);const W=e.current,re=W?.getViewBounds?.(),ce=W?.getViewCorners?.(),Y=ze(re)?re:ze(u.current)?u.current:null;if(!Y)return;u.current=Y;const H=C/Math.max(1,t.width),ee=_/Math.max(1,t.height),G=Array.isArray(ce)&&ce.length>=4&&ce.every(v=>Array.isArray(v)&&v.length>=2&&Number.isFinite(v[0])&&Number.isFinite(v[1]))?ce:null;if(G){R.beginPath();for(let v=0;v<G.length;v+=1){const B=G[v],O=q(B[0]*H,0,C),J=q(B[1]*ee,0,_);v===0?R.moveTo(O,J):R.lineTo(O,J)}R.closePath(),R.fillStyle=ae,R.fill(),R.strokeStyle=pe,R.lineWidth=1.5,R.stroke();return}const K=q(Y[0]*H,0,C),c=q(Y[1]*ee,0,_),g=q(Y[2]*H,0,C),M=q(Y[3]*ee,0,_),E=Math.max(1,g-K),k=Math.max(1,M-c);R.fillStyle=ae,R.fillRect(K,c,E,k),R.strokeStyle=pe,R.lineWidth=1.5,R.strokeRect(K+.5,c+.5,Math.max(1,E-1),Math.max(1,k-1))},[p,x,I,L,S,e,t.width,t.height,ae,pe]),X=f.useCallback(()=>{m.current||(m.current=!0,b.current=requestAnimationFrame(()=>{m.current=!1,b.current=null,$()}))},[$]),j=f.useCallback((P,R)=>{const C=a.current;if(!C)return null;const _=C.getBoundingClientRect();if(!_.width||!_.height)return null;const V=q((P-_.left)/_.width,0,1),te=q((R-_.top)/_.height,0,1);return[V*t.width,te*t.height]},[t.width,t.height]),Te=f.useCallback((P,R)=>{const C=e.current;if(!C)return;if(C.setViewCenter){C.setViewCenter(P,R),X();return}const _=C.getViewBounds?.(),V=ze(_)?_:ze(u.current)?u.current:null;if(!V)return;const te=Math.max(1e-6,V[2]-V[0]),ne=Math.max(1e-6,V[3]-V[1]);C.setViewState({offsetX:P-te*.5,offsetY:R-ne*.5}),X()},[e,X]),we=f.useCallback(P=>{if(!z||P.button!==0)return;const R=a.current;if(!R)return;const C=j(P.clientX,P.clientY);C&&(P.preventDefault(),P.stopPropagation(),R.setPointerCapture(P.pointerId),h.current={active:!0,pointerId:P.pointerId},Te(C[0],C[1]))},[z,j,Te]),oe=f.useCallback(P=>{const R=h.current;if(!R.active||R.pointerId!==P.pointerId)return;const C=j(P.clientX,P.clientY);C&&(P.preventDefault(),P.stopPropagation(),Te(C[0],C[1]))},[j,Te]),ye=f.useCallback(P=>{const R=h.current;if(!R.active||R.pointerId!==P.pointerId)return;const C=a.current;if(C&&C.hasPointerCapture(P.pointerId))try{C.releasePointerCapture(P.pointerId)}catch{}h.current={active:!1,pointerId:null},X()},[X]);return f.useEffect(()=>{let P=!1;l.current=null,X();const R=0,C=2**(t.maxTierZoom-R),_=Math.ceil(t.width/C),V=Math.ceil(t.height/C),te=Math.max(1,Math.ceil(_/t.tileSize)),ne=Math.max(1,Math.ceil(V/t.tileSize)),A=te*ne;if(!be||A>F)return;const W=document.createElement("canvas");W.width=Math.max(1,Math.round(p)),W.height=Math.max(1,Math.round(x));const re=W.getContext("2d");if(!re)return;re.fillStyle=I,re.fillRect(0,0,W.width,W.height);const ce=[];for(let Y=0;Y<ne;Y+=1)for(let H=0;H<te;H+=1){const ee=H*t.tileSize*C,G=Y*t.tileSize*C,K=Math.min((H+1)*t.tileSize,_)*C,c=Math.min((Y+1)*t.tileSize,V)*C;ce.push({url:ct(t,R,H,Y),bounds:[ee,G,K,c]})}return Promise.allSettled(ce.map(async Y=>{const H=!!n,ee=await fetch(Y.url,{headers:H?{Authorization:n}:void 0});if(!ee.ok)throw new Error(`HTTP ${ee.status}`);const G=await createImageBitmap(await ee.blob());return{tile:Y,bitmap:G}})).then(Y=>{if(P){for(const G of Y)G.status==="fulfilled"&&G.value.bitmap.close();return}const H=W.width/Math.max(1,t.width),ee=W.height/Math.max(1,t.height);for(const G of Y){if(G.status!=="fulfilled")continue;const{tile:{bounds:K},bitmap:c}=G.value,g=K[0]*H,M=K[1]*ee,E=Math.max(1,(K[2]-K[0])*H),k=Math.max(1,(K[3]-K[1])*ee);re.drawImage(c,g,M,E,k),c.close()}l.current=W,X()}),()=>{P=!0}},[t,n,p,x,I,be,F,X]),f.useEffect(()=>{X()},[X]),f.useEffect(()=>{if(i)return i.current=X,()=>{i.current===X&&(i.current=null)}},[i,X]),f.useEffect(()=>()=>{h.current={active:!1,pointerId:null},b.current!==null&&(cancelAnimationFrame(b.current),b.current=null),m.current=!1},[]),de.jsx("canvas",{ref:a,className:o,style:Q,onPointerDown:we,onPointerMove:oe,onPointerUp:ye,onPointerCancel:ye,onContextMenu:P=>{P.preventDefault()},onWheel:P=>{P.preventDefault(),P.stopPropagation()}})}function vn({imageWidth:t,imageHeight:e,tiles:n,viewState:r,className:i,style:o}){const s=f.useRef(null),a=f.useRef(null),l=f.useMemo(()=>({width:"100%",height:"100%",display:"block",...o}),[o]);return f.useEffect(()=>{const u=s.current;if(!u)return;const h=new St({canvas:u,imageWidth:t,imageHeight:e,initialViewState:r});return a.current=h,h.setTiles(n),()=>{h.destroy(),a.current=null}},[t,e]),f.useEffect(()=>{const u=a.current;u&&u.setTiles(n)},[n]),f.useEffect(()=>{const u=a.current;!u||!r||u.setViewState(r)},[r]),de.jsx("canvas",{ref:s,className:i,style:l})}function En(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 Pn(t){const e=[];for(const n of t??[]){const r=En(n);if(r.length<4)continue;let i=1/0,o=1/0,s=-1/0,a=-1/0;for(const[l,u]of r)l<i&&(i=l),l>s&&(s=l),u<o&&(o=u),u>a&&(a=u);!Number.isFinite(i)||!Number.isFinite(o)||e.push({ring:r,minX:i,minY:o,maxX:s,maxY:a})}return e}function Rn(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],l=n[o][0],u=n[o][1];a>e!=u>e&&t<(l-s)*(e-a)/(u-a||Number.EPSILON)+s&&(r=!r)}return r}function Mn(t,e,n){for(const r of n)if(!(t<r.minX||t>r.maxX||e<r.minY||e>r.maxY)&&Rn(t,e,r.ring))return!0;return!1}function Ue(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return null;const n=Pn(e??[]);if(n.length===0)return{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)};const r=t.count,i=t.positions,o=t.paletteIndices,s=new Float32Array(r*2),a=new Uint16Array(r);let l=0;for(let u=0;u<r;u+=1){const h=i[u*2],b=i[u*2+1];Mn(h,b,n)&&(s[l*2]=h,s[l*2+1]=b,a[l]=o[u],l+=1)}return{count:l,positions:s.subarray(0,l*2),paletteIndices:a.subarray(0,l)}}let We=null;const Cn=`
|
|
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=`
|
|
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 Sn(){if(typeof navigator>"u")return!1;const t=navigator;return typeof t.gpu=="object"&&t.gpu!==null}function kt(){if(!Sn())return null;const e=navigator.gpu;if(!e||typeof e!="object")return null;const n=e;return typeof n.requestAdapter!="function"?null:n}const Ye=globalThis.GPUShaderStage?.COMPUTE??4,Qe=globalThis.GPUBufferUsage?.STORAGE??128,Xe=globalThis.GPUBufferUsage?.COPY_DST??8,An=globalThis.GPUBufferUsage?.COPY_SRC??4,In=globalThis.GPUBufferUsage?.UNIFORM??64,_n=globalThis.GPUBufferUsage?.MAP_READ??1,Bn=globalThis.GPUMapMode?.READ??1;async function Un(){const t=kt();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 Dn(){return We||(We=(async()=>{const t=kt();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:Ye,buffer:{type:"read-only-storage"}},{binding:1,visibility:Ye,buffer:{type:"read-only-storage"}},{binding:2,visibility:Ye,buffer:{type:"storage"}},{binding:3,visibility:Ye,buffer:{type:"uniform"}}]}),i=n.createComputePipeline({layout:n.createPipelineLayout({bindGroupLayouts:[r]}),compute:{module:n.createShaderModule({code:Cn}),entryPoint:"main"}});return{device:n,pipeline:i,bindGroupLayout:r}})(),We)}function Ve(t,e){return Math.ceil(t/e)*e}async function Nt(t,e,n){const r=await Dn();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,l=o*4*Float32Array.BYTES_PER_ELEMENT,u=s*Uint32Array.BYTES_PER_ELEMENT,h=Number(r.device.limits.maxStorageBufferBindingSize);if(a>h||l>h||u>h)return null;const b=r.device.createBuffer({size:Ve(a,4),usage:Qe|Xe}),m=r.device.createBuffer({size:Ve(l,4),usage:Qe|Xe}),p=r.device.createBuffer({size:Ve(u,4),usage:Qe|An}),x=r.device.createBuffer({size:16,usage:In|Xe}),w=r.device.createBuffer({size:Ve(u,4),usage:Xe|_n});r.device.queue.writeBuffer(b,0,t.buffer,t.byteOffset,a),r.device.queue.writeBuffer(m,0,n.buffer,n.byteOffset,l),r.device.queue.writeBuffer(x,0,new Uint32Array([s,o,0,0]));const T=r.device.createBindGroup({layout:r.bindGroupLayout,entries:[{binding:0,resource:{buffer:b}},{binding:1,resource:{buffer:m}},{binding:2,resource:{buffer:p}},{binding:3,resource:{buffer:x}}]}),S=r.device.createCommandEncoder(),F=S.beginComputePass();F.setPipeline(r.pipeline),F.setBindGroup(0,T),F.dispatchWorkgroups(Math.ceil(s/256)),F.end(),S.copyBufferToBuffer(p,0,w,0,u),r.device.queue.submit([S.finish()]),await w.mapAsync(Bn);const I=w.getMappedRange(),L=new Uint32Array(I.slice(0));return w.unmap(),b.destroy(),m.destroy(),p.destroy(),x.destroy(),w.destroy(),L}function ve(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function Fn(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 Ln(t){const e=[];for(const n of t??[]){const r=Fn(n);if(r.length<4)continue;let i=1/0,o=1/0,s=-1/0,a=-1/0;for(const[l,u]of r)l<i&&(i=l),l>s&&(s=l),u<o&&(o=u),u>a&&(a=u);!Number.isFinite(i)||!Number.isFinite(o)||e.push({ring:r,minX:i,minY:o,maxX:s,maxY:a})}return e}function kn(t,e,n){let r=!1;for(let i=0,o=n.length-1;i<n.length;o=i,i+=1){const s=n[i][0],a=n[i][1],l=n[o][0],u=n[o][1];a>e!=u>e&&t<(l-s)*(e-a)/(u-a||Number.EPSILON)+s&&(r=!r)}return r}function Et(t,e,n){for(const r of n)if(!(t<r.minX||t>r.maxX||e<r.minY||e>r.maxY)&&kn(t,e,r.ring))return!0;return!1}async function zt(t,e,n={}){const r=ve(),i=n.bridgeToDraw===!0;if(!t||!t.count||!t.positions||!t.paletteIndices)return{data:null,meta:{mode:"hybrid-webgpu",durationMs:ve()-r,usedWebGpu:!1,candidateCount:0,bridgedToDraw:!1}};const o=Ln(e??[]);if(o.length===0)return{data:{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)},meta:{mode:"hybrid-webgpu",durationMs:ve()-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:ve()-r,usedWebGpu:!1,candidateCount:0,bridgedToDraw:!1}};const a=new Float32Array(o.length*4);for(let w=0;w<o.length;w+=1){const T=w*4,S=o[w];a[T]=S.minX,a[T+1]=S.minY,a[T+2]=S.maxX,a[T+3]=S.maxY}let l=null,u=!1;try{l=await Nt(t.positions,s,a),u=!!l}catch{l=null,u=!1}if(!l)return{data:Ue(t,e),meta:{mode:"hybrid-webgpu",durationMs:ve()-r,usedWebGpu:!1,candidateCount:s,bridgedToDraw:!1}};let h=0;for(let w=0;w<s;w+=1)l[w]===1&&(h+=1);const b=new Uint32Array(h);if(h>0){let w=0;for(let T=0;T<s;T+=1)l[T]===1&&(b[w]=T,w+=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:ve()-r,usedWebGpu:!0,candidateCount:0,bridgedToDraw:!0}}:{data:{count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)},meta:{mode:"hybrid-webgpu",durationMs:ve()-r,usedWebGpu:!0,candidateCount:0,bridgedToDraw:!1}};if(i){const w=new Uint32Array(h);let T=0;for(let S=0;S<h;S+=1){const F=b[S]??0,I=t.positions[F*2],L=t.positions[F*2+1];Et(I,L,o)&&(w[T]=F,T+=1)}return{data:{count:s,positions:t.positions.subarray(0,s*2),paletteIndices:t.paletteIndices.subarray(0,s),drawIndices:w.subarray(0,T)},meta:{mode:"hybrid-webgpu",durationMs:ve()-r,usedWebGpu:!0,candidateCount:h,bridgedToDraw:!0}}}const m=new Float32Array(h*2),p=new Uint16Array(h);let x=0;for(let w=0;w<h;w+=1){const T=b[w]??0,S=t.positions[T*2],F=t.positions[T*2+1];Et(S,F,o)&&(m[x*2]=S,m[x*2+1]=F,p[x]=t.paletteIndices[T],x+=1)}return{data:{count:x,positions:m.subarray(0,x*2),paletteIndices:p.subarray(0,x)},meta:{mode:"hybrid-webgpu",durationMs:ve()-r,usedWebGpu:!0,candidateCount:h,bridgedToDraw:!1}}}let fe=null,ot=!0,Nn=1;const Ce=new Map;function Ge(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function zn(){if(!ot)return null;if(fe)return fe;try{const t=new Worker(new URL("/assets/roi-clip-worker-i1SE1Dpa.js",typeof document>"u"?require("url").pathToFileURL(__filename).href:He&&He.tagName.toUpperCase()==="SCRIPT"&&He.src||new URL("index.cjs",document.baseURI).href),{type:"module"});return t.addEventListener("message",lt),t.addEventListener("error",ht),fe=t,t}catch{return ot=!1,null}}function lt(t){const e=t.data;if(!e)return;const n=Ce.get(e.id);if(!n)return;if(Ce.delete(e.id),e.type==="roi-clip-failure"){n.reject(new Error(e.error||"worker clip failed"));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:Ge()-n.startMs}})}function ht(){ot=!1,fe&&(fe.removeEventListener("message",lt),fe.removeEventListener("error",ht),fe.terminate(),fe=null);for(const[,t]of Ce)t.reject(new Error("worker crashed"));Ce.clear()}function Wn(){if(fe){fe.removeEventListener("message",lt),fe.removeEventListener("error",ht),fe.terminate(),fe=null;for(const[,t]of Ce)t.reject(new Error("worker terminated"));Ce.clear()}}async function Wt(t,e){if(!t||!t.count||!t.positions||!t.paletteIndices)return{data:null,meta:{mode:"worker",durationMs:0}};const n=zn();if(!n){const l=Ge();return{data:Ue(t,e),meta:{mode:"sync",durationMs:Ge()-l}}}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=Nn++,a=Ge();return new Promise((l,u)=>{Ce.set(s,{resolve:l,reject:u,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])})}function Yn(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 Xn(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 Vn(t){const e=[];for(let n=0;n<t.length;n+=1){const r=t[n];if(!r?.coordinates?.length)continue;const i=Yn(r.coordinates);if(i.length<4)continue;let o=1/0,s=1/0,a=-1/0,l=-1/0;for(const[u,h]of i)u<o&&(o=u),u>a&&(a=u),h<s&&(s=h),h>l&&(l=h);!Number.isFinite(o)||!Number.isFinite(s)||!Number.isFinite(a)||!Number.isFinite(l)||e.push({regionId:r.id??n,regionIndex:n,ring:i,minX:o,minY:s,maxX:a,maxY:l,area:Math.max(1e-6,Xn(i))})}return e}function On(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],l=n[o][0],u=n[o][1];a>e!=u>e&&t<(l-s)*(e-a)/(u-a||Number.EPSILON)+s&&(r=!r)}return r}function Gn(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 Yt(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 p=m.length;for(let x=0;x<m.length;x+=1)m[x]<r||(p-=1);if(p===m.length)i=m;else if(p>0){const x=new Uint32Array(p);let w=0;for(let T=0;T<m.length;T+=1){const S=m[T];S>=r||(x[w]=S,w+=1)}i=x}else i=new Uint32Array(0)}const o=i?i.length:r,s=Vn(e??[]);if(!t||o===0||s.length===0)return{groups:[],inputPointCount:o,pointsInsideAnyRegion:0,unmatchedPointCount:o};const a=new Map,l=new Map;let u=0;for(let m=0;m<o;m+=1){const p=i?i[m]:m,x=t.positions[p*2],w=t.positions[p*2+1];let T=null;for(const I of s)x<I.minX||x>I.maxX||w<I.minY||w>I.maxY||On(x,w,I.ring)&&(!T||I.area<T.area)&&(T=I);if(!T)continue;u+=1;const S=t.paletteIndices[p]??0,F=a.get(T.regionIndex)??new Map;F.set(S,(F.get(S)??0)+1),a.set(T.regionIndex,F),l.set(T.regionIndex,(l.get(T.regionIndex)??0)+1)}const h=n.includeEmptyRegions??!1,b=[];for(const m of s){const p=l.get(m.regionIndex)??0;if(!h&&p<=0)continue;const x=a.get(m.regionIndex)??new Map,w=Array.from(x.entries()).map(([T,S])=>({termId:Gn(T,n.paletteIndexToTermId),paletteIndex:T,count:S})).sort((T,S)=>S.count-T.count||T.paletteIndex-S.paletteIndex);b.push({regionId:m.regionId,regionIndex:m.regionIndex,totalCount:p,termCounts:w})}return{groups:b,inputPointCount:o,pointsInsideAnyRegion:u,unmatchedPointCount:Math.max(0,o-u)}}function Oe(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function qn(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 Xt{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:Oe()};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-Oe());this.timerId=window.setTimeout(()=>{this.timerId=null,this.pump()},n)}takeNextReadyQueueItem(){if(this.queue.length===0)return null;const e=Oe(),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=qn(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,l=this.getRetryDelay(a),u={tile:e.tile,attempt:a,readyAt:Oe()+l},h=this.queuedByKey.get(e.tile.key);h?(h.tile=u.tile,h.readyAt=Math.min(h.readyAt,u.readyAt),h.attempt=Math.max(h.attempt,u.attempt)):(this.queue.push(u),this.queuedByKey.set(u.tile.key,u)),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 Pt=.35;class $n{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,l=(n-this.viewportHeight*.5)/i,u=Be(r.rotationDeg),h=Math.cos(u),b=Math.sin(u);return[o+a*h-l*b,s+a*b+l*h]}worldToScreen(e,n){const r=this.viewState,i=Math.max(1e-6,r.zoom),[o,s]=this.getCenter(),a=e-o,l=n-s,u=Be(r.rotationDeg),h=Math.cos(u),b=Math.sin(u),m=a*h+l*b,p=-a*b+l*h;return[this.viewportWidth*.5+m*i,this.viewportHeight*.5+p*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=Be(this.viewState.rotationDeg),o=Math.cos(i),s=Math.sin(i),a=2*e*o/this.viewportWidth,l=2*e*s/this.viewportWidth,u=2*e*s/this.viewportHeight,h=-2*e*o/this.viewportHeight,b=-(a*n+l*r),m=-(u*n+h*r);return new Float32Array([a,u,0,l,h,0,b,m,1])}}function Be(t){return t*Math.PI/180}function Rt(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function Me(t,e,n){const r=t.getUniformLocation(e,n);if(!r)throw new Error(`uniform location lookup failed: ${n}`);return r}function et(t,e){return!t||!e?t===e:t.buffer===e.buffer&&t.byteOffset===e.byteOffset&&t.byteLength===e.byteLength}class Vt{constructor(e,n,r={}){d(this,"canvas");d(this,"source");d(this,"gl");d(this,"camera",new $n);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):Pt;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 Xt({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=q(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 l=this.pointBuffersDirty||!a||a.count!==n||!et(a.positions,r)||!et(a.paletteIndices,i),u=this.pointBuffersDirty||o&&(!a?.drawIndices||!et(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;l&&(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&&u&&(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,(l||u)&&(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,l]=r[s-1],[u,h]=r[s];if(n<=a)break;const b=q((n-a)/Math.max(1e-6,u-a),0,1);i=l+(h-l)*b}const o=r[r.length-1];return n>o[0]&&(i+=(n-o[0])*4),q(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:q(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=q(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 l=this.camera.getViewport(),u=n-l.width*.5,h=r-l.height*.5,b=Be(this.camera.getViewState().rotationDeg),m=Math.cos(b),p=Math.sin(b),x=u/o*m-h/o*p,w=u/o*p+h/o*m;this.camera.setCenter(s-x,a-w),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(),l=n*.5,u=r*.5,h=l-i,b=this.source.width-l+i,m=u-o,p=this.source.height-u+o,x=h<=b?q(s,h,b):this.source.width*.5,w=m<=p?q(a,m,p):this.source.height*.5;this.camera.setCenter(x,w)}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 q(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)),l=n[0],u=n[1],h=n[2],b=n[3],m=q(Math.floor(l/r/this.source.tileSize),0,s-1),p=q(Math.floor((h-1)/r/this.source.tileSize),0,s-1),x=q(Math.floor(u/r/this.source.tileSize),0,a-1),w=q(Math.floor((b-1)/r/this.source.tileSize),0,a-1);if(m>p||x>w)return[];const T=(l+h)*.5/r/this.source.tileSize,S=(u+b)*.5/r/this.source.tileSize,F=[];for(let I=x;I<=w;I+=1)for(let L=m;L<=p;L+=1){const pe=L*this.source.tileSize*r,ae=I*this.source.tileSize*r,z=Math.min((L+1)*this.source.tileSize,i)*r,be=Math.min((I+1)*this.source.tileSize,o)*r,ue=L-T,Q=I-S;F.push({key:`${e}/${L}/${I}`,tier:e,x:L,y:I,bounds:[pe,ae,z,be],distance2:ue*ue+Q*Q,url:ct(this.source,e,L,I)})}return F.sort((I,L)=>I.distance2-L.distance2),F}trimCache(){if(this.cache.size<=this.maxCacheTiles)return;const e=Array.from(this.cache.entries());e.sort((r,i)=>r[1].lastUsed-i[1].lastUsed);const n=this.cache.size-this.maxCacheTiles;for(let r=0;r<n;r+=1){const[i,o]=e[r];this.gl.deleteTexture(o.texture),this.cache.delete(i)}}render(){if(this.destroyed||this.contextLost||this.gl.isContextLost())return;const e=Rt();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 l=[];for(const[,m]of this.cache)a.has(m.key)||this.intersectsBounds(m.bounds,s)&&l.push(m);l.sort((m,p)=>m.tier-p.tier);for(const m of l)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 u=0;const h=[];for(const m of o){const p=this.cache.get(m.key);if(!p){h.push(m);continue}p.lastUsed=this.frameSerial,n.activeTexture(n.TEXTURE0),n.bindTexture(n.TEXTURE_2D,p.texture),n.uniform4f(r.uBounds,p.bounds[0],p.bounds[1],p.bounds[2],p.bounds[3]),n.drawArrays(n.TRIANGLE_STRIP,0,4),u+=1}this.tileScheduler.schedule(h),n.bindTexture(n.TEXTURE_2D,null),n.bindVertexArray(null);let b=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),b=this.pointCount),this.onStats){const m=this.tileScheduler.getSnapshot(),p=u,x=h.length,w=l.length+u+(b>0?1:0);this.onStats({tier:this.currentTier,visible:o.length,rendered:u,points:b,fallback:l.length,cache:this.cache.size,inflight:m.inflight,queued:m.queued,retries:m.retries,failed:m.failed,aborted:m.aborted,cacheHits:p,cacheMisses:x,drawCalls:w,frameMs:Rt()-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)),l=this.rotationDragSensitivityDegPerPixel/Pt,u=this.camera.getViewState();this.camera.setViewState({rotationDeg:u.rotationDeg-a*180/Math.PI*l})}}else{const i=this.camera.getViewState(),o=Math.max(1e-6,i.zoom),s=Be(i.rotationDeg),a=Math.cos(s),l=Math.sin(s),u=(n*a-r*l)/o,h=(n*l+r*a)/o;this.camera.setViewState({offsetX:i.offsetX-u,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=pt(e,`#version 300 es
|
|
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
|
|
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=Fe(e,i,"uCamera"),s=Fe(e,i,"uBounds"),a=Fe(e,i,"uTexture"),u=e.createVertexArray(),l=e.createBuffer();if(!u||!l)throw new Error("buffer allocation failed");e.bindVertexArray(u),e.bindBuffer(e.ARRAY_BUFFER,l),e.bufferData(e.ARRAY_BUFFER,new Float32Array([0,0,0,0,1,0,1,0,0,1,0,1,1,1,1,1]),e.STATIC_DRAW);const h=e.getAttribLocation(i,"aUnit"),w=e.getAttribLocation(i,"aUv");if(h<0||w<0)throw new Error("tile attribute lookup failed");return e.enableVertexAttribArray(h),e.enableVertexAttribArray(w),e.vertexAttribPointer(h,2,e.FLOAT,!1,16,0),e.vertexAttribPointer(w,2,e.FLOAT,!1,16,8),e.bindVertexArray(null),e.bindBuffer(e.ARRAY_BUFFER,null),{program:i,vao:u,vbo:l,uCamera:o,uBounds:s,uTexture:a}}initPointProgram(){const e=this.gl,i=At(e,`#version 300 es
|
|
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=Fe(e,i,"uCamera"),s=Fe(e,i,"uPointSize"),a=Fe(e,i,"uPalette"),u=Fe(e,i,"uPaletteSize"),l=e.createVertexArray(),h=e.createBuffer(),w=e.createBuffer(),m=e.createBuffer(),g=e.createTexture();if(!l||!h||!w||!m||!g)throw new Error("point buffer allocation failed");e.bindVertexArray(l),e.bindBuffer(e.ARRAY_BUFFER,h),e.bufferData(e.ARRAY_BUFFER,0,e.DYNAMIC_DRAW);const x=e.getAttribLocation(i,"aPosition");if(x<0)throw new Error("point position attribute not found");e.enableVertexAttribArray(x),e.vertexAttribPointer(x,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,w),e.bufferData(e.ARRAY_BUFFER,0,e.DYNAMIC_DRAW);const y=e.getAttribLocation(i,"aTerm");if(y<0)throw new Error("point term attribute not found");return e.enableVertexAttribArray(y),e.vertexAttribIPointer(y,1,e.UNSIGNED_SHORT,0,0),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,m),e.bufferData(e.ELEMENT_ARRAY_BUFFER,0,e.DYNAMIC_DRAW),e.bindVertexArray(null),e.bindBuffer(e.ARRAY_BUFFER,null),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,null),e.bindTexture(e.TEXTURE_2D,g),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,1,1,0,e.RGBA,e.UNSIGNED_BYTE,new Uint8Array([160,160,160,255])),e.bindTexture(e.TEXTURE_2D,null),{program:i,vao:l,posBuffer:h,termBuffer:w,indexBuffer:m,paletteTexture:g,uCamera:o,uPointSize:s,uPalette:a,uPaletteSize:u}}handleTileLoaded(e,n){if(this.destroyed||this.contextLost||this.gl.isContextLost()){n.close();return}if(this.cache.has(e.key)){n.close();return}const r=this.createTextureFromBitmap(n);n.close(),r&&(this.cache.set(e.key,{key:e.key,texture:r,bounds:e.bounds,tier:e.tier,lastUsed:this.frameSerial}),this.trimCache(),this.requestRender())}createTextureFromBitmap(e){if(this.contextLost||this.gl.isContextLost())return null;const n=this.gl,r=n.createTexture();return r?(n.bindTexture(n.TEXTURE_2D,r),n.pixelStorei(n.UNPACK_FLIP_Y_WEBGL,1),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_S,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_WRAP_T,n.CLAMP_TO_EDGE),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MIN_FILTER,n.LINEAR),n.texParameteri(n.TEXTURE_2D,n.TEXTURE_MAG_FILTER,n.LINEAR),n.texImage2D(n.TEXTURE_2D,0,n.RGBA,n.RGBA,n.UNSIGNED_BYTE,e),n.bindTexture(n.TEXTURE_2D,null),r):null}}const ct=[],lr=[],hr={count:0,positions:new Float32Array(0),paletteIndices:new Uint16Array(0)};function gt(t,e){return t.id??e}function fr(t,e){if(!Array.isArray(e)||e.length<3)return!1;const[n,r]=t;let i=!1;for(let o=0,s=e.length-1;o<e.length;s=o++){const[a,u]=e[o],[l,h]=e[s];u>r!=h>r&&n<(l-a)*(r-u)/Math.max(1e-12,h-u)+a&&(i=!i)}return i}function Wt(t,e){for(let n=e.length-1;n>=0;n-=1){const r=e[n];if(r?.coordinates?.length&&fr(t,r.coordinates))return{region:r,regionIndex:n,regionId:gt(r,n)}}return null}function dr({source:t,viewState:e,onViewStateChange:n,onStats:r,onTileError:i,onContextLost:o,onContextRestored:s,debugOverlay:a=!1,debugOverlayStyle:u,fitNonce:l=0,rotationResetNonce:h=0,authToken:w="",ctrlDragRotate:m=!0,pointData:g=null,pointPalette:x=null,roiRegions:y,roiPolygons:M,clipPointsToRois:P=!1,clipMode:D="worker",onClipStats:A,onRoiPointGroups:W,roiPaletteIndexToTermId:be,interactionLock:re=!1,drawTool:Y="cursor",stampOptions:Ee,regionStrokeStyle:he,regionStrokeHoverStyle:ae,regionStrokeActiveStyle:Ce,patchStrokeStyle:Z,resolveRegionStrokeStyle:ee,overlayShapes:X,customLayers:ue,patchRegions:Pe,regionLabelStyle:ie,onPointerWorldMove:T,onRegionHover:v,onRegionClick:S,onActiveRegionChange:N,onDrawComplete:V,onPatchComplete:fe,showOverviewMap:K=!1,overviewMapOptions:G,className:J,style:oe}){const se=f.useRef(null),F=f.useRef(null),C=f.useRef(null),z=f.useRef(null),q=f.useRef(n),j=f.useRef(r),ce=f.useRef(a),[Be,xe]=f.useState(!0),[Te,pe]=f.useState(null),[c,p]=f.useState(null),[E,B]=f.useState(null),[R,I]=f.useState(null),_=f.useRef(null),O=f.useRef(0),te=y??ct,ne=Pe??ct,ve=M??lr,Se=(ue?.length??0)>0,ze=f.useMemo(()=>({position:"relative",width:"100%",height:"100%",...oe}),[oe]),et=f.useMemo(()=>({position:"absolute",top:8,left:8,zIndex:7,margin:0,padding:"8px 10px",maxWidth:"min(420px, 80%)",pointerEvents:"none",whiteSpace:"pre-wrap",lineHeight:1.35,fontFamily:"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",fontSize:11,color:"#cde6ff",background:"rgba(6, 12, 20, 0.82)",border:"1px solid rgba(173, 216, 255, 0.28)",borderRadius:8,boxShadow:"0 8px 22px rgba(0,0,0,0.35)",...u}),[u]),le=f.useMemo(()=>te.length>0?te:ve.length===0?ct:ve.map((b,U)=>({id:U,coordinates:b})),[te,ve]),Ue=f.useMemo(()=>le.map(b=>b.coordinates),[le]),[Ve,Oe]=f.useState(g);f.useEffect(()=>{const b=++O.current;let U=!1;if(!P)return Oe(g),()=>{U=!0};if(!g||!g.count||!g.positions||!g.paletteIndices)return Oe(null),()=>{U=!0};if(Ue.length===0)return Oe(hr),A?.({mode:D,durationMs:0,inputCount:g.count,outputCount:0,polygonCount:0}),()=>{U=!0};const k=(L,Q)=>{if(U||b!==O.current)return;const tt=L?.drawIndices?L.drawIndices.length:L?.count??0;Oe(L),A?.({mode:Q.mode,durationMs:Q.durationMs,inputCount:g.count,outputCount:tt,polygonCount:Ue.length,usedWebGpu:Q.usedWebGpu,candidateCount:Q.candidateCount,bridgedToDraw:Q.bridgedToDraw})};return(async()=>{if(D==="sync"){const L=performance.now(),Q=Xe(g,Ue);k(Q,{mode:"sync",durationMs:performance.now()-L});return}if(D==="hybrid-webgpu"){const L=await nn(g,Ue,{bridgeToDraw:!0});k(L.data,{mode:L.meta.mode,durationMs:L.meta.durationMs,usedWebGpu:L.meta.usedWebGpu,candidateCount:L.meta.candidateCount,bridgedToDraw:L.meta.bridgedToDraw});return}try{const L=await sn(g,Ue);k(L.data,{mode:L.meta.mode,durationMs:L.meta.durationMs})}catch{const L=performance.now(),Q=Xe(g,Ue);k(Q,{mode:"sync",durationMs:performance.now()-L})}})(),()=>{U=!0}},[P,D,g,Ue,A]),f.useMemo(()=>{const b=Number(G?.width??220);return Number.isFinite(b)?Math.max(64,b):220},[G?.width]);const Tt=f.useMemo(()=>{const b=Number(G?.height??140);return Number.isFinite(b)?Math.max(48,b):140},[G?.height]),Re=f.useMemo(()=>{const b=Number(G?.margin??16);return Number.isFinite(b)?Math.max(0,b):16},[G?.margin]),Ge=G?.position||"bottom-right",Le=f.useCallback(b=>{p(U=>String(U)===String(b)?U:(N?.(b),b))},[N]);f.useEffect(()=>{q.current=n},[n]),f.useEffect(()=>{j.current=r},[r]),f.useEffect(()=>{ce.current=a,a||I(null)},[a]);const vt=f.useCallback(b=>{j.current?.(b),ce.current&&I(b)},[]),ln=f.useMemo(()=>R?[`tier ${R.tier} | frame ${R.frameMs?.toFixed(2)??"-"} ms | drawCalls ${R.drawCalls??"-"}`,`tiles visible ${R.visible} | rendered ${R.rendered} | fallback ${R.fallback}`,`cache size ${R.cache} | hit ${R.cacheHits??"-"} | miss ${R.cacheMisses??"-"}`,`queue inflight ${R.inflight} | queued ${R.queued??"-"} | retries ${R.retries??"-"} | failed ${R.failed??"-"} | aborted ${R.aborted??"-"}`,`points ${R.points}`].join(`
|
|
125
|
+
`):"stats: waiting for first frame...",[R]);f.useEffect(()=>{!(c===null?!0:le.some((H,L)=>String(gt(H,L))===String(c)))&&c!==null&&Le(null);const U=_.current;!(U===null?!0:le.some((H,L)=>String(gt(H,L))===String(U)))&&U!==null&&(_.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[le,c,v,Le]);const Mt=f.useCallback(b=>{Se&&B(b);const U=q.current;U&&U(b),C.current?.(),z.current?.()},[Se]);f.useEffect(()=>{if(!K){xe(!1);return}xe(!0)},[K,t?.id]),f.useEffect(()=>{Y!=="cursor"&&_.current!==null&&(_.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[Y,v]);const De=f.useCallback((b,U)=>{const k=F.current;if(!k)return null;const H=k.screenToWorld(b,U);if(!Array.isArray(H)||H.length<2)return null;const L=Number(H[0]),Q=Number(H[1]);return!Number.isFinite(L)||!Number.isFinite(Q)?null:[L,Q]},[]),Et=f.useCallback((b,U)=>{const k=F.current;if(!k)return null;const H=k.worldToScreen(b,U);if(!Array.isArray(H)||H.length<2)return null;const L=Number(H[0]),Q=Number(H[1]);return!Number.isFinite(L)||!Number.isFinite(Q)?null:[L,Q]},[]),Ct=f.useCallback(()=>{F.current?.requestRender(),C.current?.(),z.current?.()},[]),Pt=f.useMemo(()=>E??F.current?.getViewState()??null,[E]),St=f.useMemo(()=>{if(!t)return null;const b=Pt;return b?{source:t,viewState:b,drawTool:Y,interactionLock:re,worldToScreen:Et,screenToWorld:De,requestRedraw:Ct}:null},[t,Pt,Y,re,Et,De,Ct]),hn=f.useCallback(b=>{const U=b.target===se.current,k=De(b.clientX,b.clientY);if(T){const tt=!!k&&k[0]>=0&&k[1]>=0&&!!t&&k[0]<=t.width&&k[1]<=t.height;T({coordinate:k,clientX:b.clientX,clientY:b.clientY,insideImage:tt})}if(Y!=="cursor")return;if(!U){_.current!==null&&(_.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}));return}if(!k||!le.length)return;const H=Wt(k,le),L=H?.regionId??null,Q=_.current;String(Q)!==String(L)&&(_.current=L,pe(L),v?.({region:H?.region??null,regionId:L,regionIndex:H?.regionIndex??-1,coordinate:k}))},[Y,le,De,v,T,t]),fn=f.useCallback(()=>{T?.({coordinate:null,clientX:-1,clientY:-1,insideImage:!1}),_.current!==null&&(_.current=null,pe(null),v?.({region:null,regionId:null,regionIndex:-1,coordinate:null}))},[v,T]),dn=f.useCallback(b=>{if(Y!=="cursor"||b.target!==se.current)return;if(!le.length){Le(null);return}const U=De(b.clientX,b.clientY);if(!U)return;const k=Wt(U,le);if(!k){Le(null);return}const H=c!==null&&String(c)===String(k.regionId)?null:k.regionId;Le(H),S?.({region:k.region,regionId:k.regionId,regionIndex:k.regionIndex,coordinate:U})},[Y,le,De,S,c,Le]);return f.useEffect(()=>{const b=se.current;if(!b||!t)return;const U=new cn(b,t,{onViewStateChange:Mt,onStats:vt,onTileError:i,onContextLost:o,onContextRestored:s,authToken:w,ctrlDragRotate:m});return F.current=U,e&&U.setViewState(e),U.setInteractionLock(re),Se&&B(U.getViewState()),()=>{U.destroy(),F.current=null}},[t,vt,i,o,s,w,m,Mt,Se]),f.useEffect(()=>{const b=F.current;!b||!e||b.setViewState(e)},[e]),f.useEffect(()=>{const b=F.current;b&&b.fitToImage()},[l]),f.useEffect(()=>{const b=F.current;b&&b.resetRotation()},[h]),f.useEffect(()=>{const b=F.current;!b||!x||b.setPointPalette(x)},[x]),f.useEffect(()=>{const b=F.current;b&&b.setPointData(Ve)},[Ve]),f.useEffect(()=>{if(!W)return;const U=an(P?Ve:g,le,{paletteIndexToTermId:be,includeEmptyRegions:!0});W(U)},[W,P,g,Ve,le,be]),f.useEffect(()=>{const b=F.current;b&&b.setInteractionLock(re)},[re]),me.jsxs("div",{className:J,style:ze,onPointerMove:hn,onPointerLeave:fn,onClick:dn,children:[me.jsx("canvas",{ref:se,className:"wsi-render-canvas",style:{position:"absolute",inset:0,zIndex:1,width:"100%",height:"100%",display:"block",touchAction:"none",cursor:Y==="cursor"&&Te!==null?"pointer":re?"crosshair":"grab"}}),t&&St&&Array.isArray(ue)&&ue.length>0?ue.map((b,U)=>me.jsx("div",{className:b.className,style:{position:"absolute",inset:0,zIndex:b.zIndex??3,pointerEvents:b.pointerEvents??"none",...b.style},children:b.render(St)},b.id??U)):null,t?me.jsx($t,{tool:Y,enabled:Y!=="cursor",imageWidth:t.width,imageHeight:t.height,imageMpp:t.mpp,imageZoom:t.maxTierZoom,stampOptions:Ee,projectorRef:F,viewStateSignal:e,persistedRegions:le,patchRegions:ne,regionStrokeStyle:he,regionStrokeHoverStyle:ae,regionStrokeActiveStyle:Ce,patchStrokeStyle:Z,resolveRegionStrokeStyle:ee,overlayShapes:X,hoveredRegionId:Te,activeRegionId:c,regionLabelStyle:ie,invalidateRef:C,onDrawComplete:V,onPatchComplete:fe}):null,a?me.jsx("pre",{"data-open-plant-debug-overlay":!0,style:et,children:ln}):null,t&&K?Be?me.jsxs(me.Fragment,{children:[me.jsx(Zt,{source:t,projectorRef:F,authToken:w,options:G,invalidateRef:z}),me.jsx("button",{type:"button","aria-label":"Hide overview map",onClick:()=>xe(!1),style:{position:"absolute",zIndex:6,...Ge.includes("left")?{left:Re}:{right:Re},...Ge.includes("top")?{top:Re+Tt+8}:{bottom:Re+Tt+8},width:20,height:20,borderRadius:999,border:"1px solid rgba(255,255,255,0.4)",background:"rgba(8, 14, 22, 0.9)",color:"#fff",fontSize:13,lineHeight:1,cursor:"pointer",padding:0},children:"×"})]}):me.jsx("button",{type:"button","aria-label":"Show overview map",onClick:()=>xe(!0),style:{position:"absolute",zIndex:6,...Ge.includes("left")?{left:Re}:{right:Re},...Ge.includes("top")?{top:Re}:{bottom:Re},height:24,minWidth:40,borderRadius:999,border:"1px solid rgba(255,255,255,0.45)",background:"rgba(8, 14, 22, 0.9)",color:"#dff8ff",fontSize:11,fontWeight:700,cursor:"pointer",padding:"0 8px"},children:"Map"}):null]})}exports.DEFAULT_POINT_COLOR=pt;exports.DrawLayer=$t;exports.M1TileRenderer=Yt;exports.OverviewMap=Zt;exports.TileScheduler=un;exports.TileViewerCanvas=Yn;exports.WsiTileRenderer=cn;exports.WsiViewerCanvas=dr;exports.buildTermPalette=En;exports.calcScaleLength=Tn;exports.calcScaleResolution=bt;exports.clamp=$;exports.closeRing=ye;exports.computeRoiPointGroups=an;exports.createCircle=ft;exports.createRectangle=ht;exports.filterPointDataByPolygons=Xe;exports.filterPointDataByPolygonsHybrid=nn;exports.filterPointDataByPolygonsInWorker=sn;exports.filterPointIndicesByPolygons=Qt;exports.filterPointIndicesByPolygonsInWorker=nr;exports.getWebGpuCapabilities=jn;exports.hexToRgba=Xt;exports.isSameViewState=vn;exports.normalizeImageInfo=Wn;exports.prefilterPointsByBoundsWebGpu=tn;exports.terminateRoiClipWorker=tr;exports.toBearerToken=Mn;exports.toTileUrl=wt;
|
|
126
126
|
//# sourceMappingURL=index.cjs.map
|