open-plant 1.3.0 → 1.3.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.
Files changed (78) hide show
  1. package/dist/assets/point-hit-index-worker-CNFA6pZm.js +2 -0
  2. package/dist/assets/point-hit-index-worker-CNFA6pZm.js.map +1 -0
  3. package/dist/assets/roi-clip-worker-BDVQwN2T.js.map +1 -1
  4. package/dist/index.cjs +17 -13
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.js +3233 -3037
  7. package/dist/index.js.map +1 -1
  8. package/dist/types/core/ortho-camera.d.ts +7 -0
  9. package/dist/types/core/ortho-camera.d.ts.map +1 -1
  10. package/dist/types/index.d.ts +2 -0
  11. package/dist/types/index.d.ts.map +1 -1
  12. package/dist/types/react/draw-layer-brush.d.ts +9 -0
  13. package/dist/types/react/draw-layer-brush.d.ts.map +1 -0
  14. package/dist/types/react/draw-layer-label.d.ts +21 -0
  15. package/dist/types/react/draw-layer-label.d.ts.map +1 -0
  16. package/dist/types/react/draw-layer-overlay.d.ts +18 -0
  17. package/dist/types/react/draw-layer-overlay.d.ts.map +1 -0
  18. package/dist/types/react/draw-layer-stamp.d.ts +24 -0
  19. package/dist/types/react/draw-layer-stamp.d.ts.map +1 -0
  20. package/dist/types/react/draw-layer-types.d.ts +243 -0
  21. package/dist/types/react/draw-layer-types.d.ts.map +1 -0
  22. package/dist/types/react/draw-layer-utils.d.ts +30 -0
  23. package/dist/types/react/draw-layer-utils.d.ts.map +1 -0
  24. package/dist/types/react/draw-layer.d.ts +4 -187
  25. package/dist/types/react/draw-layer.d.ts.map +1 -1
  26. package/dist/types/react/wsi-region-hit-utils.d.ts +21 -0
  27. package/dist/types/react/wsi-region-hit-utils.d.ts.map +1 -0
  28. package/dist/types/react/wsi-viewer-canvas.d.ts +2 -1
  29. package/dist/types/react/wsi-viewer-canvas.d.ts.map +1 -1
  30. package/dist/types/wsi/brush-stroke.d.ts.map +1 -1
  31. package/dist/types/wsi/image-info.d.ts +26 -1
  32. package/dist/types/wsi/image-info.d.ts.map +1 -1
  33. package/dist/types/wsi/point-clip-hybrid.d.ts.map +1 -1
  34. package/dist/types/wsi/point-clip-worker-client.d.ts.map +1 -1
  35. package/dist/types/wsi/point-clip.d.ts +1 -1
  36. package/dist/types/wsi/point-clip.d.ts.map +1 -1
  37. package/dist/types/wsi/point-hit-index-shared.d.ts +25 -0
  38. package/dist/types/wsi/point-hit-index-shared.d.ts.map +1 -0
  39. package/dist/types/wsi/point-hit-index-worker-client.d.ts.map +1 -1
  40. package/dist/types/wsi/roi-geometry.d.ts +3 -0
  41. package/dist/types/wsi/roi-geometry.d.ts.map +1 -1
  42. package/dist/types/wsi/roi-term-stats.d.ts.map +1 -1
  43. package/dist/types/wsi/tile-scheduler.d.ts.map +1 -1
  44. package/dist/types/wsi/utils.d.ts +3 -2
  45. package/dist/types/wsi/utils.d.ts.map +1 -1
  46. package/dist/types/wsi/worker-client.d.ts +28 -0
  47. package/dist/types/wsi/worker-client.d.ts.map +1 -0
  48. package/dist/types/wsi/wsi-canvas-lifecycle.d.ts +15 -0
  49. package/dist/types/wsi/wsi-canvas-lifecycle.d.ts.map +1 -0
  50. package/dist/types/wsi/wsi-input-handlers.d.ts +67 -0
  51. package/dist/types/wsi/wsi-input-handlers.d.ts.map +1 -0
  52. package/dist/types/wsi/wsi-interaction.d.ts +50 -0
  53. package/dist/types/wsi/wsi-interaction.d.ts.map +1 -0
  54. package/dist/types/wsi/wsi-lifecycle-ops.d.ts +38 -0
  55. package/dist/types/wsi/wsi-lifecycle-ops.d.ts.map +1 -0
  56. package/dist/types/wsi/wsi-normalize.d.ts +20 -0
  57. package/dist/types/wsi/wsi-normalize.d.ts.map +1 -0
  58. package/dist/types/wsi/wsi-point-data.d.ts +14 -0
  59. package/dist/types/wsi/wsi-point-data.d.ts.map +1 -0
  60. package/dist/types/wsi/wsi-render-pass.d.ts +40 -0
  61. package/dist/types/wsi/wsi-render-pass.d.ts.map +1 -0
  62. package/dist/types/wsi/wsi-renderer-types.d.ts +132 -0
  63. package/dist/types/wsi/wsi-renderer-types.d.ts.map +1 -0
  64. package/dist/types/wsi/wsi-shaders.d.ts +4 -0
  65. package/dist/types/wsi/wsi-shaders.d.ts.map +1 -0
  66. package/dist/types/wsi/wsi-tile-cache.d.ts +8 -0
  67. package/dist/types/wsi/wsi-tile-cache.d.ts.map +1 -0
  68. package/dist/types/wsi/wsi-tile-renderer.d.ts +11 -70
  69. package/dist/types/wsi/wsi-tile-renderer.d.ts.map +1 -1
  70. package/dist/types/wsi/wsi-tile-visibility.d.ts +22 -0
  71. package/dist/types/wsi/wsi-tile-visibility.d.ts.map +1 -0
  72. package/dist/types/wsi/wsi-view-animation.d.ts +4 -0
  73. package/dist/types/wsi/wsi-view-animation.d.ts.map +1 -0
  74. package/dist/types/wsi/wsi-view-ops.d.ts +17 -0
  75. package/dist/types/wsi/wsi-view-ops.d.ts.map +1 -0
  76. package/package.json +1 -1
  77. package/dist/assets/point-hit-index-worker-Bp1uxxyQ.js +0 -2
  78. package/dist/assets/point-hit-index-worker-Bp1uxxyQ.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ (function(){"use strict";function x(e,l,n){return(e*73856093^l*19349663)>>>0&n}function v(e,l,n){if(e<=0||l<=0||n<=0)return 256;const o=Math.max(1,e*l),r=Math.sqrt(o/Math.max(1,n))*4;return Math.max(24,Math.min(1024,r))}function Z(e,l){if(!(e instanceof Uint32Array)||e.length===0)return null;let n=!0;for(let r=0;r<e.length;r+=1)if(!(e[r]<l)){n=!1;break}if(n)return e;const o=new Uint32Array(e.length);let h=0;for(let r=0;r<e.length;r+=1)e[r]>=l||(o[h]=e[r],h+=1);return h>0?o.subarray(0,h):null}function q(e){const l=Math.max(0,Math.floor(e.count)),n=Math.floor(e.positions.length/2),o=Math.max(0,Math.min(l,n));if(o<=0)return null;const h=Z(e.drawIndices??null,o),r=h?h.length:o;if(r===0)return null;const L=v(e.sourceWidth,e.sourceHeight,r),H=1/L,O=new Int32Array(r),P=new Int32Array(r);let a=0;if(h)for(let t=0;t<r;t+=1){const i=h[t],c=e.positions[i*2],s=e.positions[i*2+1];!Number.isFinite(c)||!Number.isFinite(s)||(O[a]=Math.floor(c*H),P[a]=Math.floor(s*H),a+=1)}else for(let t=0;t<o;t+=1){const i=e.positions[t*2],c=e.positions[t*2+1];!Number.isFinite(i)||!Number.isFinite(c)||(O[a]=Math.floor(i*H),P[a]=Math.floor(c*H),a+=1)}if(a===0)return null;let g=Math.min(a,Math.max(64,a>>>3));(!Number.isFinite(g)||g<=0)&&(g=a);let u=1;for(;u<g*2;)u<<=1;let y=u-1,f=new Int32Array(u*2),d=new Int32Array(u);f.fill(2147483647);let I=0;const A=new Int32Array(a);for(let t=0;t<a;t+=1){const i=O[t],c=P[t];let s=x(i,c,y);for(;;){const T=f[s*2];if(T===2147483647){if(f[s*2]=i,f[s*2+1]=c,d[s]=1,A[t]=s,I+=1,I*4>u*3){const B=u;u<<=1,y=u-1;const S=new Int32Array(u*2),G=new Int32Array(u);S.fill(2147483647);for(let w=0;w<B;w+=1){if(f[w*2]===2147483647)continue;const R=f[w*2],Y=f[w*2+1];let M=x(R,Y,y);for(;S[M*2]!==2147483647;)M=M+1&y;S[M*2]=R,S[M*2+1]=Y,G[M]=d[w]}for(f=S,d=G,s=x(i,c,y);f[s*2]!==i||f[s*2+1]!==c;)s=s+1&y;A[t]=s}break}if(T===i&&f[s*2+1]===c){d[s]+=1,A[t]=s;break}s=s+1&y}}const b=new Int32Array(I*2),D=new Uint32Array(I),F=new Uint32Array(I),N=new Int32Array(u);N.fill(-1);let p=0,K=0;for(let t=0;t<u;t+=1)f[t*2]!==2147483647&&(b[p*2]=f[t*2],b[p*2+1]=f[t*2+1],D[p]=K,F[p]=d[t],N[t]=p,K+=d[t],p+=1);const U=new Uint32Array(a),_=new Uint32Array(I);if(_.set(D),h)for(let t=0;t<a;t+=1){const i=N[A[t]];U[_[i]]=h[t],_[i]+=1}else{let t=0;for(let i=0;i<o;i+=1){const c=e.positions[i*2],s=e.positions[i*2+1];if(!Number.isFinite(c)||!Number.isFinite(s))continue;const T=N[A[t]];U[_[T]]=i,_[T]+=1,t+=1}}let C=1;for(;C<I*2;)C<<=1;const z=C-1,E=new Int32Array(C);E.fill(-1);for(let t=0;t<I;t+=1){const i=b[t*2],c=b[t*2+1];let s=x(i,c,z);for(;E[s]!==-1;)s=s+1&z;E[s]=t}return{cellSize:L,safeCount:o,cellCount:I,hashCapacity:C,hashTable:E,cellKeys:b,cellOffsets:D,cellLengths:F,pointIndices:U}}function k(){return typeof performance<"u"&&typeof performance.now=="function"?performance.now():Date.now()}function X(e){if(e instanceof Error)return e.message;try{return String(e)}catch{return"unknown worker error"}}function W(e){const l=k(),n=q({count:e.count,positions:new Float32Array(e.positions),drawIndices:e.drawIndices?new Uint32Array(e.drawIndices):null,sourceWidth:e.sourceWidth,sourceHeight:e.sourceHeight});return n?{type:"point-hit-index-success",id:e.id,cellSize:n.cellSize,safeCount:n.safeCount,cellCount:n.cellCount,hashCapacity:n.hashCapacity,hashTable:n.hashTable.buffer,cellKeys:n.cellKeys.buffer,cellOffsets:n.cellOffsets.buffer,cellLengths:n.cellLengths.buffer,pointIndices:n.pointIndices.buffer,durationMs:k()-l}:null}const m=self;m.addEventListener("message",e=>{const l=e.data;if(!(!l||l.type!=="point-hit-index-request"))try{const n=W(l);if(!n){const o={type:"point-hit-index-success",id:l.id,cellSize:0,safeCount:0,cellCount:0,hashCapacity:0,hashTable:new Int32Array(0).buffer,cellKeys:new Int32Array(0).buffer,cellOffsets:new Uint32Array(0).buffer,cellLengths:new Uint32Array(0).buffer,pointIndices:new Uint32Array(0).buffer,durationMs:0};m.postMessage(o,[o.hashTable,o.cellKeys,o.cellOffsets,o.cellLengths,o.pointIndices]);return}m.postMessage(n,[n.hashTable,n.cellKeys,n.cellOffsets,n.cellLengths,n.pointIndices])}catch(n){const o={type:"point-hit-index-failure",id:l.id,error:X(n)};m.postMessage(o)}})})();
2
+ //# sourceMappingURL=point-hit-index-worker-CNFA6pZm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"point-hit-index-worker-CNFA6pZm.js","sources":["../src/wsi/point-hit-index-shared.ts","../src/wsi/utils.ts","../src/workers/point-hit-index-worker.ts"],"sourcesContent":["export const MIN_POINT_HIT_GRID_SIZE = 24;\nexport const MAX_POINT_HIT_GRID_SIZE = 1024;\nexport const POINT_HIT_GRID_DENSITY_SCALE = 4;\nexport const HASH_EMPTY = -1;\n\nexport interface PointHitIndexBuildInput {\n count: number;\n positions: Float32Array;\n drawIndices?: Uint32Array | null;\n sourceWidth: number;\n sourceHeight: number;\n}\n\nexport interface PointHitIndexBuildResult {\n cellSize: number;\n safeCount: number;\n cellCount: number;\n hashCapacity: number;\n hashTable: Int32Array;\n cellKeys: Int32Array;\n cellOffsets: Uint32Array;\n cellLengths: Uint32Array;\n pointIndices: Uint32Array;\n}\n\nexport function cellHash(cellX: number, cellY: number, mask: number): number {\n return (((cellX * 73856093) ^ (cellY * 19349663)) >>> 0) & mask;\n}\n\nfunction resolveGridSize(sourceWidth: number, sourceHeight: number, visibleCount: number): number {\n if (sourceWidth <= 0 || sourceHeight <= 0 || visibleCount <= 0) return 256;\n const area = Math.max(1, sourceWidth * sourceHeight);\n const avgSpacing = Math.sqrt(area / Math.max(1, visibleCount));\n const raw = avgSpacing * POINT_HIT_GRID_DENSITY_SCALE;\n return Math.max(MIN_POINT_HIT_GRID_SIZE, Math.min(MAX_POINT_HIT_GRID_SIZE, raw));\n}\n\nfunction sanitizeDrawIndices(raw: Uint32Array | null | undefined, safeCount: number): Uint32Array | null {\n if (!(raw instanceof Uint32Array) || raw.length === 0) {\n return null;\n }\n\n let allValid = true;\n for (let i = 0; i < raw.length; i += 1) {\n if (raw[i] < safeCount) continue;\n allValid = false;\n break;\n }\n if (allValid) {\n return raw;\n }\n\n const filtered = new Uint32Array(raw.length);\n let cursor = 0;\n for (let i = 0; i < raw.length; i += 1) {\n if (raw[i] >= safeCount) continue;\n filtered[cursor] = raw[i];\n cursor += 1;\n }\n return cursor > 0 ? filtered.subarray(0, cursor) : null;\n}\n\nexport function buildPointHitIndex(input: PointHitIndexBuildInput): PointHitIndexBuildResult | null {\n const count = Math.max(0, Math.floor(input.count));\n const maxCountByPositions = Math.floor(input.positions.length / 2);\n const safeCount = Math.max(0, Math.min(count, maxCountByPositions));\n if (safeCount <= 0) {\n return null;\n }\n\n const drawIndices = sanitizeDrawIndices(input.drawIndices ?? null, safeCount);\n const visibleCount = drawIndices ? drawIndices.length : safeCount;\n if (visibleCount === 0) {\n return null;\n }\n\n const cellSize = resolveGridSize(input.sourceWidth, input.sourceHeight, visibleCount);\n const invCellSize = 1.0 / cellSize;\n\n const pointCellX = new Int32Array(visibleCount);\n const pointCellY = new Int32Array(visibleCount);\n let validCount = 0;\n\n if (drawIndices) {\n for (let i = 0; i < visibleCount; i += 1) {\n const pi = drawIndices[i];\n const px = input.positions[pi * 2];\n const py = input.positions[pi * 2 + 1];\n if (!Number.isFinite(px) || !Number.isFinite(py)) continue;\n pointCellX[validCount] = Math.floor(px * invCellSize);\n pointCellY[validCount] = Math.floor(py * invCellSize);\n validCount += 1;\n }\n } else {\n for (let i = 0; i < safeCount; i += 1) {\n const px = input.positions[i * 2];\n const py = input.positions[i * 2 + 1];\n if (!Number.isFinite(px) || !Number.isFinite(py)) continue;\n pointCellX[validCount] = Math.floor(px * invCellSize);\n pointCellY[validCount] = Math.floor(py * invCellSize);\n validCount += 1;\n }\n }\n\n if (validCount === 0) {\n return null;\n }\n\n let estimatedCells = Math.min(validCount, Math.max(64, validCount >>> 3));\n if (!Number.isFinite(estimatedCells) || estimatedCells <= 0) {\n estimatedCells = validCount;\n }\n\n let hashCapacity = 1;\n while (hashCapacity < estimatedCells * 2) hashCapacity <<= 1;\n let hashMask = hashCapacity - 1;\n\n let tempHashKeys = new Int32Array(hashCapacity * 2);\n let tempHashCounts = new Int32Array(hashCapacity);\n tempHashKeys.fill(0x7fffffff);\n let cellCount = 0;\n\n const pointCellSlot = new Int32Array(validCount);\n\n for (let i = 0; i < validCount; i += 1) {\n const cx = pointCellX[i];\n const cy = pointCellY[i];\n let slot = cellHash(cx, cy, hashMask);\n\n while (true) {\n const kx = tempHashKeys[slot * 2];\n if (kx === 0x7fffffff) {\n tempHashKeys[slot * 2] = cx;\n tempHashKeys[slot * 2 + 1] = cy;\n tempHashCounts[slot] = 1;\n pointCellSlot[i] = slot;\n cellCount += 1;\n\n if (cellCount * 4 > hashCapacity * 3) {\n const oldCap = hashCapacity;\n hashCapacity <<= 1;\n hashMask = hashCapacity - 1;\n\n const newKeys = new Int32Array(hashCapacity * 2);\n const newCounts = new Int32Array(hashCapacity);\n newKeys.fill(0x7fffffff);\n\n for (let s = 0; s < oldCap; s += 1) {\n if (tempHashKeys[s * 2] === 0x7fffffff) continue;\n const ocx = tempHashKeys[s * 2];\n const ocy = tempHashKeys[s * 2 + 1];\n let ns = cellHash(ocx, ocy, hashMask);\n while (newKeys[ns * 2] !== 0x7fffffff) ns = (ns + 1) & hashMask;\n newKeys[ns * 2] = ocx;\n newKeys[ns * 2 + 1] = ocy;\n newCounts[ns] = tempHashCounts[s];\n }\n\n tempHashKeys = newKeys;\n tempHashCounts = newCounts;\n\n slot = cellHash(cx, cy, hashMask);\n while (\n tempHashKeys[slot * 2] !== cx ||\n tempHashKeys[slot * 2 + 1] !== cy\n ) {\n slot = (slot + 1) & hashMask;\n }\n pointCellSlot[i] = slot;\n }\n break;\n }\n\n if (kx === cx && tempHashKeys[slot * 2 + 1] === cy) {\n tempHashCounts[slot] += 1;\n pointCellSlot[i] = slot;\n break;\n }\n\n slot = (slot + 1) & hashMask;\n }\n }\n\n const cellKeys = new Int32Array(cellCount * 2);\n const cellOffsets = new Uint32Array(cellCount);\n const cellLengths = new Uint32Array(cellCount);\n const slotToCellIndex = new Int32Array(hashCapacity);\n slotToCellIndex.fill(HASH_EMPTY);\n\n let cellIdx = 0;\n let offset = 0;\n for (let s = 0; s < hashCapacity; s += 1) {\n if (tempHashKeys[s * 2] === 0x7fffffff) continue;\n cellKeys[cellIdx * 2] = tempHashKeys[s * 2];\n cellKeys[cellIdx * 2 + 1] = tempHashKeys[s * 2 + 1];\n cellOffsets[cellIdx] = offset;\n cellLengths[cellIdx] = tempHashCounts[s];\n slotToCellIndex[s] = cellIdx;\n offset += tempHashCounts[s];\n cellIdx += 1;\n }\n\n const pointIndices = new Uint32Array(validCount);\n const fillCursor = new Uint32Array(cellCount);\n fillCursor.set(cellOffsets);\n\n if (drawIndices) {\n for (let i = 0; i < validCount; i += 1) {\n const ci = slotToCellIndex[pointCellSlot[i]];\n pointIndices[fillCursor[ci]] = drawIndices[i];\n fillCursor[ci] += 1;\n }\n } else {\n let srcIdx = 0;\n for (let i = 0; i < safeCount; i += 1) {\n const px = input.positions[i * 2];\n const py = input.positions[i * 2 + 1];\n if (!Number.isFinite(px) || !Number.isFinite(py)) continue;\n const ci = slotToCellIndex[pointCellSlot[srcIdx]];\n pointIndices[fillCursor[ci]] = i;\n fillCursor[ci] += 1;\n srcIdx += 1;\n }\n }\n\n let finalCap = 1;\n while (finalCap < cellCount * 2) finalCap <<= 1;\n const finalMask = finalCap - 1;\n const hashTable = new Int32Array(finalCap);\n hashTable.fill(HASH_EMPTY);\n\n for (let i = 0; i < cellCount; i += 1) {\n const cx = cellKeys[i * 2];\n const cy = cellKeys[i * 2 + 1];\n let slot = cellHash(cx, cy, finalMask);\n while (hashTable[slot] !== HASH_EMPTY) slot = (slot + 1) & finalMask;\n hashTable[slot] = i;\n }\n\n return {\n cellSize,\n safeCount,\n cellCount,\n hashCapacity: finalCap,\n hashTable,\n cellKeys,\n cellOffsets,\n cellLengths,\n pointIndices,\n };\n}\n","import { DEFAULT_POINT_COLOR } from \"./constants\";\nimport type { TermPalette, WsiPointData, WsiViewState } from \"./types\";\n\nexport function clamp(value: number, min: number, max: number): number {\n\treturn Math.max(min, Math.min(max, value));\n}\n\nexport function calcScaleResolution(\n\timageMpp: number,\n\timageZoom: number,\n\tcurrentZoom: number,\n): number {\n\tconst mpp = Number(imageMpp);\n\tconst z0 = Number(imageZoom);\n\tconst z1 = Number(currentZoom);\n\tif (!Number.isFinite(mpp) || mpp <= 0) return 1;\n\tif (!Number.isFinite(z0) || !Number.isFinite(z1)) return mpp;\n\treturn Math.pow(2, z0 - z1) * mpp;\n}\n\nexport function calcScaleLength(\n\timageMpp: number,\n\timageZoom: number,\n\tcurrentZoom: number,\n): string {\n\tconst resolution = calcScaleResolution(imageMpp, imageZoom, currentZoom);\n\tlet length = 100 * resolution;\n\tif (Number(imageMpp)) {\n\t\tlet unit = \"μm\";\n\t\tif (length > 1000) {\n\t\t\tlength /= 1000;\n\t\t\tunit = \"mm\";\n\t\t}\n\t\treturn `${length.toPrecision(3)} ${unit}`;\n\t}\n\treturn `${Math.round(length * 1000) / 1000} pixels`;\n}\n\nexport function nowMs(): number {\n\tif (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n\t\treturn performance.now();\n\t}\n\treturn Date.now();\n}\n\nexport function sanitizePointCount(pointData: WsiPointData): number {\n\tconst fillModesLength =\n\t\tpointData.fillModes instanceof Uint8Array\n\t\t\t? pointData.fillModes.length\n\t\t\t: Number.MAX_SAFE_INTEGER;\n\treturn Math.max(\n\t\t0,\n\t\tMath.min(\n\t\t\tMath.floor(pointData.count ?? 0),\n\t\t\tMath.floor((pointData.positions?.length ?? 0) / 2),\n\t\t\tpointData.paletteIndices?.length ?? 0,\n\t\t\tfillModesLength,\n\t\t),\n\t);\n}\n\nexport function isSameViewState(\n\ta: Partial<WsiViewState> | null | undefined,\n\tb: Partial<WsiViewState> | null | undefined,\n): boolean {\n\tif (!a && !b) return true;\n\tif (!a || !b) return false;\n\treturn (\n\t\tMath.abs((a.zoom ?? 0) - (b.zoom ?? 0)) < 1e-6 &&\n\t\tMath.abs((a.offsetX ?? 0) - (b.offsetX ?? 0)) < 1e-6 &&\n\t\tMath.abs((a.offsetY ?? 0) - (b.offsetY ?? 0)) < 1e-6 &&\n\t\tMath.abs((a.rotationDeg ?? 0) - (b.rotationDeg ?? 0)) < 1e-6\n\t);\n}\n\nexport function toBearerToken(value: string | null | undefined): string {\n\tconst trimmed = String(value ?? \"\").trim();\n\tif (!trimmed) return \"\";\n\tif (/^bearer\\s+/i.test(trimmed)) {\n\t\tconst token = trimmed.replace(/^bearer\\s+/i, \"\").trim();\n\t\treturn token ? `Bearer ${token}` : \"\";\n\t}\n\treturn `Bearer ${trimmed}`;\n}\n\nexport function hexToRgba(\n\thex: string | null | undefined,\n): [number, number, number, number] {\n\tconst value = String(hex ?? \"\").trim();\n\tconst match = value.match(/^#?([0-9a-fA-F]{6})$/);\n\tif (!match) return [...DEFAULT_POINT_COLOR];\n\n\tconst n = Number.parseInt(match[1], 16);\n\treturn [(n >> 16) & 255, (n >> 8) & 255, n & 255, 255];\n}\n\nexport function buildTermPalette(\n\tterms:\n\t\t| Array<{ termId?: string | null; termColor?: string | null }>\n\t\t| null\n\t\t| undefined,\n): TermPalette {\n\tconst palette: Array<[number, number, number, number]> = [\n\t\t[...DEFAULT_POINT_COLOR],\n\t];\n\tconst termToPaletteIndex = new Map<string, number>();\n\n\tfor (const term of terms ?? []) {\n\t\tconst termId = String(term?.termId ?? \"\");\n\t\tif (!termId || termToPaletteIndex.has(termId)) continue;\n\n\t\ttermToPaletteIndex.set(termId, palette.length);\n\t\tpalette.push(hexToRgba(term?.termColor));\n\t}\n\n\tconst colors = new Uint8Array(palette.length * 4);\n\tfor (let i = 0; i < palette.length; i += 1) {\n\t\tcolors[i * 4] = palette[i][0];\n\t\tcolors[i * 4 + 1] = palette[i][1];\n\t\tcolors[i * 4 + 2] = palette[i][2];\n\t\tcolors[i * 4 + 3] = palette[i][3];\n\t}\n\n\treturn { colors, termToPaletteIndex };\n}\n","import { buildPointHitIndex } from \"../wsi/point-hit-index-shared\";\nimport type {\n PointHitIndexWorkerRequest,\n PointHitIndexWorkerResponse,\n PointHitIndexWorkerSuccess,\n} from \"../wsi/point-hit-index-worker-protocol\";\nimport { nowMs } from \"../wsi/utils\";\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\nfunction handleRequest(msg: PointHitIndexWorkerRequest): PointHitIndexWorkerSuccess | null {\n const start = nowMs();\n const result = buildPointHitIndex({\n count: msg.count,\n positions: new Float32Array(msg.positions),\n drawIndices: msg.drawIndices ? new Uint32Array(msg.drawIndices) : null,\n sourceWidth: msg.sourceWidth,\n sourceHeight: msg.sourceHeight,\n });\n\n if (!result) {\n return null;\n }\n\n return {\n type: \"point-hit-index-success\",\n id: msg.id,\n cellSize: result.cellSize,\n safeCount: result.safeCount,\n cellCount: result.cellCount,\n hashCapacity: result.hashCapacity,\n hashTable: result.hashTable.buffer as ArrayBuffer,\n cellKeys: result.cellKeys.buffer as ArrayBuffer,\n cellOffsets: result.cellOffsets.buffer as ArrayBuffer,\n cellLengths: result.cellLengths.buffer as ArrayBuffer,\n pointIndices: result.pointIndices.buffer as ArrayBuffer,\n durationMs: nowMs() - start,\n };\n}\n\ninterface WorkerScope {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: \"message\", listener: (event: MessageEvent<PointHitIndexWorkerRequest>) => void): void;\n}\n\nconst workerScope = self as unknown as WorkerScope;\n\nworkerScope.addEventListener(\"message\", (event: MessageEvent<PointHitIndexWorkerRequest>) => {\n const data = event.data;\n if (!data || data.type !== \"point-hit-index-request\") return;\n\n try {\n const result = handleRequest(data);\n if (!result) {\n const empty: PointHitIndexWorkerSuccess = {\n type: \"point-hit-index-success\",\n id: data.id,\n cellSize: 0,\n safeCount: 0,\n cellCount: 0,\n hashCapacity: 0,\n hashTable: new Int32Array(0).buffer,\n cellKeys: new Int32Array(0).buffer,\n cellOffsets: new Uint32Array(0).buffer,\n cellLengths: new Uint32Array(0).buffer,\n pointIndices: new Uint32Array(0).buffer,\n durationMs: 0,\n };\n workerScope.postMessage(empty, [\n empty.hashTable,\n empty.cellKeys,\n empty.cellOffsets,\n empty.cellLengths,\n empty.pointIndices,\n ]);\n return;\n }\n\n workerScope.postMessage(result, [\n result.hashTable,\n result.cellKeys,\n result.cellOffsets,\n result.cellLengths,\n result.pointIndices,\n ]);\n } catch (error) {\n const fail: PointHitIndexWorkerResponse = {\n type: \"point-hit-index-failure\",\n id: data.id,\n error: toErrorMessage(error),\n };\n workerScope.postMessage(fail);\n }\n});\n"],"names":["cellHash","cellX","cellY","mask","resolveGridSize","sourceWidth","sourceHeight","visibleCount","area","raw","sanitizeDrawIndices","safeCount","allValid","i","filtered","cursor","buildPointHitIndex","input","count","maxCountByPositions","drawIndices","cellSize","invCellSize","pointCellX","pointCellY","validCount","pi","px","py","estimatedCells","hashCapacity","hashMask","tempHashKeys","tempHashCounts","cellCount","pointCellSlot","cx","cy","slot","kx","oldCap","newKeys","newCounts","s","ocx","ocy","ns","cellKeys","cellOffsets","cellLengths","slotToCellIndex","cellIdx","offset","pointIndices","fillCursor","ci","srcIdx","finalCap","finalMask","hashTable","nowMs","toErrorMessage","error","handleRequest","msg","start","result","workerScope","event","data","empty","fail"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"roi-clip-worker-BDVQwN2T.js","sources":["../src/wsi/roi-geometry.ts","../src/workers/roi-clip-worker.ts"],"sourcesContent":["export type RoiCoordinate = [number, number];\nexport type RoiLinearRing = RoiCoordinate[];\nexport type RoiPolygonRings = RoiLinearRing[];\nexport type RoiMultiPolygon = RoiPolygonRings[];\nexport type RoiGeometry = RoiLinearRing | RoiPolygonRings | RoiMultiPolygon;\n\nexport interface PreparedRoiPolygon {\n\touter: RoiLinearRing;\n\tholes: RoiLinearRing[];\n\tminX: number;\n\tminY: number;\n\tmaxX: number;\n\tmaxY: number;\n\tarea: number;\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isFinite(value);\n}\n\nfunction isCoordinatePair(value: unknown): value is RoiCoordinate {\n\treturn (\n\t\tArray.isArray(value) &&\n\t\tvalue.length >= 2 &&\n\t\tisFiniteNumber(value[0]) &&\n\t\tisFiniteNumber(value[1])\n\t);\n}\n\nfunction isLinearRing(value: unknown): value is RoiLinearRing {\n\treturn Array.isArray(value) && value.length > 0 && value.every(point => isCoordinatePair(point));\n}\n\nfunction isPolygonRings(value: unknown): value is RoiPolygonRings {\n\treturn Array.isArray(value) && value.length > 0 && value.every(ring => isLinearRing(ring));\n}\n\nfunction isMultiPolygon(value: unknown): value is RoiMultiPolygon {\n\treturn Array.isArray(value) && value.length > 0 && value.every(polygon => isPolygonRings(polygon));\n}\n\nexport function closeRoiRing(coordinates: readonly RoiCoordinate[]): RoiLinearRing {\n\tif (!Array.isArray(coordinates) || coordinates.length < 3) return [];\n\tconst out: RoiLinearRing = [];\n\tfor (const point of coordinates) {\n\t\tif (!Array.isArray(point) || point.length < 2) continue;\n\t\tconst x = Number(point[0]);\n\t\tconst y = Number(point[1]);\n\t\tif (!Number.isFinite(x) || !Number.isFinite(y)) continue;\n\t\tconst prev = out[out.length - 1];\n\t\tif (prev && prev[0] === x && prev[1] === y) continue;\n\t\tout.push([x, y]);\n\t}\n\tif (out.length < 3) return [];\n\tconst first = out[0];\n\tconst last = out[out.length - 1];\n\tif (first[0] !== last[0] || first[1] !== last[1]) {\n\t\tout.push([first[0], first[1]]);\n\t}\n\treturn out.length >= 4 ? out : [];\n}\n\nexport function polygonSignedArea(ring: RoiLinearRing): number {\n\tif (!Array.isArray(ring) || ring.length < 4) return 0;\n\tlet sum = 0;\n\tfor (let i = 0; i < ring.length - 1; i += 1) {\n\t\tconst a = ring[i];\n\t\tconst b = ring[i + 1];\n\t\tsum += a[0] * b[1] - b[0] * a[1];\n\t}\n\treturn sum * 0.5;\n}\n\nfunction normalizePolygonRings(rings: RoiPolygonRings): RoiPolygonRings {\n\tif (!Array.isArray(rings) || rings.length === 0) return [];\n\tconst normalized: RoiLinearRing[] = [];\n\tfor (const ring of rings) {\n\t\tconst closed = closeRoiRing(ring);\n\t\tif (closed.length >= 4) normalized.push(closed);\n\t}\n\tif (normalized.length === 0) return [];\n\tif (normalized.length === 1) return [normalized[0]];\n\n\tlet outerIndex = 0;\n\tlet outerArea = 0;\n\tfor (let i = 0; i < normalized.length; i += 1) {\n\t\tconst area = Math.abs(polygonSignedArea(normalized[i]));\n\t\tif (area <= outerArea) continue;\n\t\touterArea = area;\n\t\touterIndex = i;\n\t}\n\n\tconst out: RoiPolygonRings = [normalized[outerIndex]];\n\tfor (let i = 0; i < normalized.length; i += 1) {\n\t\tif (i === outerIndex) continue;\n\t\tout.push(normalized[i]);\n\t}\n\treturn out;\n}\n\nexport function normalizeRoiGeometry(geometry: RoiGeometry | null | undefined): RoiMultiPolygon {\n\tif (!geometry) return [];\n\n\tif (isLinearRing(geometry)) {\n\t\tconst polygon = normalizePolygonRings([geometry]);\n\t\treturn polygon.length > 0 ? [polygon] : [];\n\t}\n\n\tif (isPolygonRings(geometry)) {\n\t\tconst polygon = normalizePolygonRings(geometry);\n\t\treturn polygon.length > 0 ? [polygon] : [];\n\t}\n\n\tif (isMultiPolygon(geometry)) {\n\t\tconst out: RoiMultiPolygon = [];\n\t\tfor (const polygon of geometry) {\n\t\t\tconst normalized = normalizePolygonRings(polygon);\n\t\t\tif (normalized.length > 0) out.push(normalized);\n\t\t}\n\t\treturn out;\n\t}\n\n\treturn [];\n}\n\nexport function pointInRing(x: number, y: number, ring: RoiLinearRing): boolean {\n\tlet inside = false;\n\tfor (let i = 0, j = ring.length - 1; i < ring.length; j = i, i += 1) {\n\t\tconst xi = ring[i][0];\n\t\tconst yi = ring[i][1];\n\t\tconst xj = ring[j][0];\n\t\tconst yj = ring[j][1];\n\t\tconst intersect =\n\t\t\tyi > y !== yj > y &&\n\t\t\tx < ((xj - xi) * (y - yi)) / ((yj - yi) || Number.EPSILON) + xi;\n\t\tif (intersect) inside = !inside;\n\t}\n\treturn inside;\n}\n\nexport function pointInPolygonWithHoles(\n\tx: number,\n\ty: number,\n\tpolygon: RoiPolygonRings,\n): boolean {\n\tif (!Array.isArray(polygon) || polygon.length === 0) return false;\n\tconst outer = polygon[0];\n\tif (!outer || outer.length < 4) return false;\n\tif (!pointInRing(x, y, outer)) return false;\n\tfor (let i = 1; i < polygon.length; i += 1) {\n\t\tconst hole = polygon[i];\n\t\tif (!hole || hole.length < 4) continue;\n\t\tif (pointInRing(x, y, hole)) return false;\n\t}\n\treturn true;\n}\n\nexport function prepareRoiPolygons(\n\tgeometries: readonly (RoiGeometry | null | undefined)[] | null | undefined,\n): PreparedRoiPolygon[] {\n\tconst prepared: PreparedRoiPolygon[] = [];\n\tfor (const geometry of geometries ?? []) {\n\t\tconst multipolygon = normalizeRoiGeometry(geometry);\n\t\tfor (const polygon of multipolygon) {\n\t\t\tconst outer = polygon[0];\n\t\t\tif (!outer || outer.length < 4) continue;\n\t\t\tlet minX = Infinity;\n\t\t\tlet minY = Infinity;\n\t\t\tlet maxX = -Infinity;\n\t\t\tlet maxY = -Infinity;\n\t\t\tfor (const [x, y] of outer) {\n\t\t\t\tif (x < minX) minX = x;\n\t\t\t\tif (x > maxX) maxX = x;\n\t\t\t\tif (y < minY) minY = y;\n\t\t\t\tif (y > maxY) maxY = y;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!Number.isFinite(minX) ||\n\t\t\t\t!Number.isFinite(minY) ||\n\t\t\t\t!Number.isFinite(maxX) ||\n\t\t\t\t!Number.isFinite(maxY)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet area = Math.abs(polygonSignedArea(outer));\n\t\t\tfor (let i = 1; i < polygon.length; i += 1) {\n\t\t\t\tarea -= Math.abs(polygonSignedArea(polygon[i]));\n\t\t\t}\n\t\t\tprepared.push({\n\t\t\t\touter,\n\t\t\t\tholes: polygon.slice(1),\n\t\t\t\tminX,\n\t\t\t\tminY,\n\t\t\t\tmaxX,\n\t\t\t\tmaxY,\n\t\t\t\tarea: Math.max(1e-6, area),\n\t\t\t});\n\t\t}\n\t}\n\treturn prepared;\n}\n\nexport function pointInPreparedPolygon(\n\tx: number,\n\ty: number,\n\tpolygon: PreparedRoiPolygon,\n): boolean {\n\tif (x < polygon.minX || x > polygon.maxX || y < polygon.minY || y > polygon.maxY) {\n\t\treturn false;\n\t}\n\tif (!pointInRing(x, y, polygon.outer)) return false;\n\tfor (const hole of polygon.holes) {\n\t\tif (pointInRing(x, y, hole)) return false;\n\t}\n\treturn true;\n}\n\nexport function pointInAnyPreparedPolygon(\n\tx: number,\n\ty: number,\n\tpolygons: readonly PreparedRoiPolygon[],\n): boolean {\n\tfor (const polygon of polygons) {\n\t\tif (!pointInPreparedPolygon(x, y, polygon)) continue;\n\t\treturn true;\n\t}\n\treturn false;\n}\n","import {\n pointInAnyPreparedPolygon,\n prepareRoiPolygons,\n} from \"../wsi/roi-geometry\";\nimport type {\n RoiClipWorkerDataRequest,\n RoiClipWorkerIndexRequest,\n RoiClipWorkerIndexSuccess,\n RoiClipWorkerRequest,\n RoiClipWorkerResponse,\n RoiClipWorkerSuccess,\n} from \"../wsi/point-clip-worker-protocol\";\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 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 const fillModes = msg.fillModes ? new Uint8Array(msg.fillModes) : null;\n const ids = msg.ids ? new Uint32Array(msg.ids) : null;\n\n const maxCountByPositions = Math.floor(positions.length / 2);\n const safeCount = Math.max(0, Math.min(count, maxCountByPositions, terms.length, fillModes ? fillModes.length : Number.MAX_SAFE_INTEGER));\n const hasFillModes = fillModes instanceof Uint8Array && fillModes.length >= safeCount;\n const hasIds = ids instanceof Uint32Array && ids.length >= safeCount;\n const prepared = prepareRoiPolygons(msg.polygons ?? []);\n\n if (safeCount === 0 || prepared.length === 0) {\n const empty: RoiClipWorkerSuccess = {\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 if (hasFillModes) {\n empty.fillModes = new Uint8Array(0).buffer;\n }\n if (hasIds) {\n empty.ids = new Uint32Array(0).buffer;\n }\n return empty;\n }\n\n const nextPositions = new Float32Array(safeCount * 2);\n const nextTerms = new Uint16Array(safeCount);\n const nextFillModes = hasFillModes ? new Uint8Array(safeCount) : null;\n const nextIds = hasIds ? new Uint32Array(safeCount) : null;\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 (!pointInAnyPreparedPolygon(x, y, prepared)) continue;\n nextPositions[cursor * 2] = x;\n nextPositions[cursor * 2 + 1] = y;\n nextTerms[cursor] = terms[i];\n if (nextFillModes) {\n nextFillModes[cursor] = fillModes![i];\n }\n if (nextIds) {\n nextIds[cursor] = ids![i];\n }\n cursor += 1;\n }\n\n const outPositions = nextPositions.slice(0, cursor * 2);\n const outTerms = nextTerms.slice(0, cursor);\n const outFillModes = nextFillModes ? nextFillModes.slice(0, cursor) : null;\n const outIds = nextIds ? nextIds.slice(0, cursor) : null;\n\n const success: RoiClipWorkerSuccess = {\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 if (outFillModes) {\n success.fillModes = outFillModes.buffer;\n }\n if (outIds) {\n success.ids = outIds.buffer;\n }\n return success;\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 = prepareRoiPolygons(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 (!pointInAnyPreparedPolygon(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 const transfer: Transferable[] = [response.positions, response.paletteIndices];\n if (response.fillModes) {\n transfer.push(response.fillModes);\n }\n if (response.ids) {\n transfer.push(response.ids);\n }\n workerScope.postMessage(response, transfer);\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":["isFiniteNumber","value","isCoordinatePair","isLinearRing","point","isPolygonRings","ring","isMultiPolygon","polygon","closeRoiRing","coordinates","out","x","y","prev","first","last","polygonSignedArea","sum","i","a","b","normalizePolygonRings","rings","normalized","closed","outerIndex","outerArea","area","normalizeRoiGeometry","geometry","pointInRing","inside","j","xi","yi","xj","yj","prepareRoiPolygons","geometries","prepared","multipolygon","outer","minX","minY","maxX","maxY","pointInPreparedPolygon","hole","pointInAnyPreparedPolygon","polygons","nowMs","toErrorMessage","error","workerScope","handleDataRequest","msg","start","count","positions","terms","fillModes","ids","maxCountByPositions","safeCount","hasFillModes","hasIds","empty","nextPositions","nextTerms","nextFillModes","nextIds","cursor","outPositions","outTerms","outFillModes","outIds","success","handleIndexRequest","outIndices","event","data","response","transfer","fail"],"mappings":"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"}
1
+ {"version":3,"file":"roi-clip-worker-BDVQwN2T.js","sources":["../src/wsi/roi-geometry.ts","../src/wsi/utils.ts","../src/workers/roi-clip-worker.ts"],"sourcesContent":["import type { WsiRegionCoordinates } from \"./types\";\n\nexport type RoiCoordinate = [number, number];\nexport type RoiLinearRing = RoiCoordinate[];\nexport type RoiPolygonRings = RoiLinearRing[];\nexport type RoiMultiPolygon = RoiPolygonRings[];\nexport type RoiGeometry = RoiLinearRing | RoiPolygonRings | RoiMultiPolygon;\n\nexport function toRoiGeometry(coords: WsiRegionCoordinates | null | undefined): RoiGeometry | null | undefined;\nexport function toRoiGeometry(coords: unknown): RoiGeometry | null | undefined;\nexport function toRoiGeometry(coords: unknown): RoiGeometry | null | undefined {\n\tif (coords == null) return null;\n\treturn coords as RoiGeometry;\n}\n\nexport interface PreparedRoiPolygon {\n\touter: RoiLinearRing;\n\tholes: RoiLinearRing[];\n\tminX: number;\n\tminY: number;\n\tmaxX: number;\n\tmaxY: number;\n\tarea: number;\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n\treturn typeof value === \"number\" && Number.isFinite(value);\n}\n\nfunction isCoordinatePair(value: unknown): value is RoiCoordinate {\n\treturn (\n\t\tArray.isArray(value) &&\n\t\tvalue.length >= 2 &&\n\t\tisFiniteNumber(value[0]) &&\n\t\tisFiniteNumber(value[1])\n\t);\n}\n\nfunction isLinearRing(value: unknown): value is RoiLinearRing {\n\treturn Array.isArray(value) && value.length > 0 && value.every(point => isCoordinatePair(point));\n}\n\nfunction isPolygonRings(value: unknown): value is RoiPolygonRings {\n\treturn Array.isArray(value) && value.length > 0 && value.every(ring => isLinearRing(ring));\n}\n\nfunction isMultiPolygon(value: unknown): value is RoiMultiPolygon {\n\treturn Array.isArray(value) && value.length > 0 && value.every(polygon => isPolygonRings(polygon));\n}\n\nexport function closeRoiRing(coordinates: readonly RoiCoordinate[]): RoiLinearRing {\n\tif (!Array.isArray(coordinates) || coordinates.length < 3) return [];\n\tconst out: RoiLinearRing = [];\n\tfor (const point of coordinates) {\n\t\tif (!Array.isArray(point) || point.length < 2) continue;\n\t\tconst x = Number(point[0]);\n\t\tconst y = Number(point[1]);\n\t\tif (!Number.isFinite(x) || !Number.isFinite(y)) continue;\n\t\tconst prev = out[out.length - 1];\n\t\tif (prev && prev[0] === x && prev[1] === y) continue;\n\t\tout.push([x, y]);\n\t}\n\tif (out.length < 3) return [];\n\tconst first = out[0];\n\tconst last = out[out.length - 1];\n\tif (first[0] !== last[0] || first[1] !== last[1]) {\n\t\tout.push([first[0], first[1]]);\n\t}\n\treturn out.length >= 4 ? out : [];\n}\n\nexport function polygonSignedArea(ring: RoiLinearRing): number {\n\tif (!Array.isArray(ring) || ring.length < 4) return 0;\n\tlet sum = 0;\n\tfor (let i = 0; i < ring.length - 1; i += 1) {\n\t\tconst a = ring[i];\n\t\tconst b = ring[i + 1];\n\t\tsum += a[0] * b[1] - b[0] * a[1];\n\t}\n\treturn sum * 0.5;\n}\n\nfunction normalizePolygonRings(rings: RoiPolygonRings): RoiPolygonRings {\n\tif (!Array.isArray(rings) || rings.length === 0) return [];\n\tconst normalized: RoiLinearRing[] = [];\n\tfor (const ring of rings) {\n\t\tconst closed = closeRoiRing(ring);\n\t\tif (closed.length >= 4) normalized.push(closed);\n\t}\n\tif (normalized.length === 0) return [];\n\tif (normalized.length === 1) return [normalized[0]];\n\n\tlet outerIndex = 0;\n\tlet outerArea = 0;\n\tfor (let i = 0; i < normalized.length; i += 1) {\n\t\tconst area = Math.abs(polygonSignedArea(normalized[i]));\n\t\tif (area <= outerArea) continue;\n\t\touterArea = area;\n\t\touterIndex = i;\n\t}\n\n\tconst out: RoiPolygonRings = [normalized[outerIndex]];\n\tfor (let i = 0; i < normalized.length; i += 1) {\n\t\tif (i === outerIndex) continue;\n\t\tout.push(normalized[i]);\n\t}\n\treturn out;\n}\n\nexport function normalizeRoiGeometry(geometry: RoiGeometry | null | undefined): RoiMultiPolygon {\n\tif (!geometry) return [];\n\n\tif (isLinearRing(geometry)) {\n\t\tconst polygon = normalizePolygonRings([geometry]);\n\t\treturn polygon.length > 0 ? [polygon] : [];\n\t}\n\n\tif (isPolygonRings(geometry)) {\n\t\tconst polygon = normalizePolygonRings(geometry);\n\t\treturn polygon.length > 0 ? [polygon] : [];\n\t}\n\n\tif (isMultiPolygon(geometry)) {\n\t\tconst out: RoiMultiPolygon = [];\n\t\tfor (const polygon of geometry) {\n\t\t\tconst normalized = normalizePolygonRings(polygon);\n\t\t\tif (normalized.length > 0) out.push(normalized);\n\t\t}\n\t\treturn out;\n\t}\n\n\treturn [];\n}\n\nexport function pointInRing(x: number, y: number, ring: RoiLinearRing): boolean {\n\tlet inside = false;\n\tfor (let i = 0, j = ring.length - 1; i < ring.length; j = i, i += 1) {\n\t\tconst xi = ring[i][0];\n\t\tconst yi = ring[i][1];\n\t\tconst xj = ring[j][0];\n\t\tconst yj = ring[j][1];\n\t\tconst intersect =\n\t\t\tyi > y !== yj > y &&\n\t\t\tx < ((xj - xi) * (y - yi)) / ((yj - yi) || Number.EPSILON) + xi;\n\t\tif (intersect) inside = !inside;\n\t}\n\treturn inside;\n}\n\nexport function pointInPolygonWithHoles(\n\tx: number,\n\ty: number,\n\tpolygon: RoiPolygonRings,\n): boolean {\n\tif (!Array.isArray(polygon) || polygon.length === 0) return false;\n\tconst outer = polygon[0];\n\tif (!outer || outer.length < 4) return false;\n\tif (!pointInRing(x, y, outer)) return false;\n\tfor (let i = 1; i < polygon.length; i += 1) {\n\t\tconst hole = polygon[i];\n\t\tif (!hole || hole.length < 4) continue;\n\t\tif (pointInRing(x, y, hole)) return false;\n\t}\n\treturn true;\n}\n\nexport function prepareRoiPolygons(\n\tgeometries: readonly (RoiGeometry | null | undefined)[] | null | undefined,\n): PreparedRoiPolygon[] {\n\tconst prepared: PreparedRoiPolygon[] = [];\n\tfor (const geometry of geometries ?? []) {\n\t\tconst multipolygon = normalizeRoiGeometry(geometry);\n\t\tfor (const polygon of multipolygon) {\n\t\t\tconst outer = polygon[0];\n\t\t\tif (!outer || outer.length < 4) continue;\n\t\t\tlet minX = Infinity;\n\t\t\tlet minY = Infinity;\n\t\t\tlet maxX = -Infinity;\n\t\t\tlet maxY = -Infinity;\n\t\t\tfor (const [x, y] of outer) {\n\t\t\t\tif (x < minX) minX = x;\n\t\t\t\tif (x > maxX) maxX = x;\n\t\t\t\tif (y < minY) minY = y;\n\t\t\t\tif (y > maxY) maxY = y;\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!Number.isFinite(minX) ||\n\t\t\t\t!Number.isFinite(minY) ||\n\t\t\t\t!Number.isFinite(maxX) ||\n\t\t\t\t!Number.isFinite(maxY)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlet area = Math.abs(polygonSignedArea(outer));\n\t\t\tfor (let i = 1; i < polygon.length; i += 1) {\n\t\t\t\tarea -= Math.abs(polygonSignedArea(polygon[i]));\n\t\t\t}\n\t\t\tprepared.push({\n\t\t\t\touter,\n\t\t\t\tholes: polygon.slice(1),\n\t\t\t\tminX,\n\t\t\t\tminY,\n\t\t\t\tmaxX,\n\t\t\t\tmaxY,\n\t\t\t\tarea: Math.max(1e-6, area),\n\t\t\t});\n\t\t}\n\t}\n\treturn prepared;\n}\n\nexport function pointInPreparedPolygon(\n\tx: number,\n\ty: number,\n\tpolygon: PreparedRoiPolygon,\n): boolean {\n\tif (x < polygon.minX || x > polygon.maxX || y < polygon.minY || y > polygon.maxY) {\n\t\treturn false;\n\t}\n\tif (!pointInRing(x, y, polygon.outer)) return false;\n\tfor (const hole of polygon.holes) {\n\t\tif (pointInRing(x, y, hole)) return false;\n\t}\n\treturn true;\n}\n\nexport function pointInAnyPreparedPolygon(\n\tx: number,\n\ty: number,\n\tpolygons: readonly PreparedRoiPolygon[],\n): boolean {\n\tfor (const polygon of polygons) {\n\t\tif (!pointInPreparedPolygon(x, y, polygon)) continue;\n\t\treturn true;\n\t}\n\treturn false;\n}\n","import { DEFAULT_POINT_COLOR } from \"./constants\";\nimport type { TermPalette, WsiPointData, WsiViewState } from \"./types\";\n\nexport function clamp(value: number, min: number, max: number): number {\n\treturn Math.max(min, Math.min(max, value));\n}\n\nexport function calcScaleResolution(\n\timageMpp: number,\n\timageZoom: number,\n\tcurrentZoom: number,\n): number {\n\tconst mpp = Number(imageMpp);\n\tconst z0 = Number(imageZoom);\n\tconst z1 = Number(currentZoom);\n\tif (!Number.isFinite(mpp) || mpp <= 0) return 1;\n\tif (!Number.isFinite(z0) || !Number.isFinite(z1)) return mpp;\n\treturn Math.pow(2, z0 - z1) * mpp;\n}\n\nexport function calcScaleLength(\n\timageMpp: number,\n\timageZoom: number,\n\tcurrentZoom: number,\n): string {\n\tconst resolution = calcScaleResolution(imageMpp, imageZoom, currentZoom);\n\tlet length = 100 * resolution;\n\tif (Number(imageMpp)) {\n\t\tlet unit = \"μm\";\n\t\tif (length > 1000) {\n\t\t\tlength /= 1000;\n\t\t\tunit = \"mm\";\n\t\t}\n\t\treturn `${length.toPrecision(3)} ${unit}`;\n\t}\n\treturn `${Math.round(length * 1000) / 1000} pixels`;\n}\n\nexport function nowMs(): number {\n\tif (typeof performance !== \"undefined\" && typeof performance.now === \"function\") {\n\t\treturn performance.now();\n\t}\n\treturn Date.now();\n}\n\nexport function sanitizePointCount(pointData: WsiPointData): number {\n\tconst fillModesLength =\n\t\tpointData.fillModes instanceof Uint8Array\n\t\t\t? pointData.fillModes.length\n\t\t\t: Number.MAX_SAFE_INTEGER;\n\treturn Math.max(\n\t\t0,\n\t\tMath.min(\n\t\t\tMath.floor(pointData.count ?? 0),\n\t\t\tMath.floor((pointData.positions?.length ?? 0) / 2),\n\t\t\tpointData.paletteIndices?.length ?? 0,\n\t\t\tfillModesLength,\n\t\t),\n\t);\n}\n\nexport function isSameViewState(\n\ta: Partial<WsiViewState> | null | undefined,\n\tb: Partial<WsiViewState> | null | undefined,\n): boolean {\n\tif (!a && !b) return true;\n\tif (!a || !b) return false;\n\treturn (\n\t\tMath.abs((a.zoom ?? 0) - (b.zoom ?? 0)) < 1e-6 &&\n\t\tMath.abs((a.offsetX ?? 0) - (b.offsetX ?? 0)) < 1e-6 &&\n\t\tMath.abs((a.offsetY ?? 0) - (b.offsetY ?? 0)) < 1e-6 &&\n\t\tMath.abs((a.rotationDeg ?? 0) - (b.rotationDeg ?? 0)) < 1e-6\n\t);\n}\n\nexport function toBearerToken(value: string | null | undefined): string {\n\tconst trimmed = String(value ?? \"\").trim();\n\tif (!trimmed) return \"\";\n\tif (/^bearer\\s+/i.test(trimmed)) {\n\t\tconst token = trimmed.replace(/^bearer\\s+/i, \"\").trim();\n\t\treturn token ? `Bearer ${token}` : \"\";\n\t}\n\treturn `Bearer ${trimmed}`;\n}\n\nexport function hexToRgba(\n\thex: string | null | undefined,\n): [number, number, number, number] {\n\tconst value = String(hex ?? \"\").trim();\n\tconst match = value.match(/^#?([0-9a-fA-F]{6})$/);\n\tif (!match) return [...DEFAULT_POINT_COLOR];\n\n\tconst n = Number.parseInt(match[1], 16);\n\treturn [(n >> 16) & 255, (n >> 8) & 255, n & 255, 255];\n}\n\nexport function buildTermPalette(\n\tterms:\n\t\t| Array<{ termId?: string | null; termColor?: string | null }>\n\t\t| null\n\t\t| undefined,\n): TermPalette {\n\tconst palette: Array<[number, number, number, number]> = [\n\t\t[...DEFAULT_POINT_COLOR],\n\t];\n\tconst termToPaletteIndex = new Map<string, number>();\n\n\tfor (const term of terms ?? []) {\n\t\tconst termId = String(term?.termId ?? \"\");\n\t\tif (!termId || termToPaletteIndex.has(termId)) continue;\n\n\t\ttermToPaletteIndex.set(termId, palette.length);\n\t\tpalette.push(hexToRgba(term?.termColor));\n\t}\n\n\tconst colors = new Uint8Array(palette.length * 4);\n\tfor (let i = 0; i < palette.length; i += 1) {\n\t\tcolors[i * 4] = palette[i][0];\n\t\tcolors[i * 4 + 1] = palette[i][1];\n\t\tcolors[i * 4 + 2] = palette[i][2];\n\t\tcolors[i * 4 + 3] = palette[i][3];\n\t}\n\n\treturn { colors, termToPaletteIndex };\n}\n","import type {\n RoiClipWorkerDataRequest,\n RoiClipWorkerIndexRequest,\n RoiClipWorkerIndexSuccess,\n RoiClipWorkerRequest,\n RoiClipWorkerResponse,\n RoiClipWorkerSuccess,\n} from \"../wsi/point-clip-worker-protocol\";\nimport { pointInAnyPreparedPolygon, prepareRoiPolygons } from \"../wsi/roi-geometry\";\nimport { nowMs } from \"../wsi/utils\";\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 const fillModes = msg.fillModes ? new Uint8Array(msg.fillModes) : null;\n const ids = msg.ids ? new Uint32Array(msg.ids) : null;\n\n const maxCountByPositions = Math.floor(positions.length / 2);\n const safeCount = Math.max(0, Math.min(count, maxCountByPositions, terms.length, fillModes ? fillModes.length : Number.MAX_SAFE_INTEGER));\n const hasFillModes = fillModes instanceof Uint8Array && fillModes.length >= safeCount;\n const hasIds = ids instanceof Uint32Array && ids.length >= safeCount;\n const prepared = prepareRoiPolygons(msg.polygons ?? []);\n\n if (safeCount === 0 || prepared.length === 0) {\n const empty: RoiClipWorkerSuccess = {\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 if (hasFillModes) {\n empty.fillModes = new Uint8Array(0).buffer;\n }\n if (hasIds) {\n empty.ids = new Uint32Array(0).buffer;\n }\n return empty;\n }\n\n const nextPositions = new Float32Array(safeCount * 2);\n const nextTerms = new Uint16Array(safeCount);\n const nextFillModes = hasFillModes ? new Uint8Array(safeCount) : null;\n const nextIds = hasIds ? new Uint32Array(safeCount) : null;\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 (!pointInAnyPreparedPolygon(x, y, prepared)) continue;\n nextPositions[cursor * 2] = x;\n nextPositions[cursor * 2 + 1] = y;\n nextTerms[cursor] = terms[i];\n if (nextFillModes) {\n nextFillModes[cursor] = fillModes![i];\n }\n if (nextIds) {\n nextIds[cursor] = ids![i];\n }\n cursor += 1;\n }\n\n const outPositions = nextPositions.slice(0, cursor * 2);\n const outTerms = nextTerms.slice(0, cursor);\n const outFillModes = nextFillModes ? nextFillModes.slice(0, cursor) : null;\n const outIds = nextIds ? nextIds.slice(0, cursor) : null;\n\n const success: RoiClipWorkerSuccess = {\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 if (outFillModes) {\n success.fillModes = outFillModes.buffer;\n }\n if (outIds) {\n success.ids = outIds.buffer;\n }\n return success;\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 = prepareRoiPolygons(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 (!pointInAnyPreparedPolygon(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 const transfer: Transferable[] = [response.positions, response.paletteIndices];\n if (response.fillModes) {\n transfer.push(response.fillModes);\n }\n if (response.ids) {\n transfer.push(response.ids);\n }\n workerScope.postMessage(response, transfer);\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":["isFiniteNumber","value","isCoordinatePair","isLinearRing","point","isPolygonRings","ring","isMultiPolygon","polygon","closeRoiRing","coordinates","out","x","y","prev","first","last","polygonSignedArea","sum","i","a","b","normalizePolygonRings","rings","normalized","closed","outerIndex","outerArea","area","normalizeRoiGeometry","geometry","pointInRing","inside","j","xi","yi","xj","yj","prepareRoiPolygons","geometries","prepared","multipolygon","outer","minX","minY","maxX","maxY","pointInPreparedPolygon","hole","pointInAnyPreparedPolygon","polygons","nowMs","toErrorMessage","error","workerScope","handleDataRequest","msg","start","count","positions","terms","fillModes","ids","maxCountByPositions","safeCount","hasFillModes","hasIds","empty","nextPositions","nextTerms","nextFillModes","nextIds","cursor","outPositions","outTerms","outFillModes","outIds","success","handleIndexRequest","outIndices","event","data","response","transfer","fail"],"mappings":"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"}