Quickstart
10분 내 첫 WSI 렌더
`WsiViewerCanvas`를 기준으로 타일, 포인트, ROI를 순서대로 연결합니다. 아래 코드는 현재 라이브러리 API(`src/index.ts`)에 맞춰 작성되어 있습니다.
1. 설치
npm install
npm run dev
현재 레포는 모노레포 패키지 publish 이전 단계입니다. 로컬 라이브러리 코드(`src`)를
직접 import 하거나 workspace 의존성으로 연결해 사용하세요.
2. 이미지 정보 조회 후 정규화
import { normalizeImageInfo, toBearerToken } from "open-plant";
const IMAGE_ID = "69898975a05a9cd9ec6fe311";
const token = "<access-token>";
const res = await fetch(
`https://your-api.example.com/api/v4/images/${IMAGE_ID}/info`,
{
headers: { Authorization: toBearerToken(token) },
},
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const rawInfo = await res.json();
const TILE_BASE_URL = "https://your-s3-bucket.example.com/ims";
const source = normalizeImageInfo(rawInfo, TILE_BASE_URL);
const pointZstUrl = rawInfo?.mvtPath || "";
3. 최소 뷰어 렌더
import { useState } from "react";
import { WsiViewerCanvas } from "open-plant";
function Viewer({ source, token }) {
const [viewState, setViewState] = useState();
const [rotationResetNonce, setRotationResetNonce] = useState(0);
const [selectedRoiId, setSelectedRoiId] = useState(null);
return (
<WsiViewerCanvas
source={source}
authToken={token}
viewState={viewState}
onViewStateChange={setViewState}
ctrlDragRotate
rotationResetNonce={rotationResetNonce}
activeRegionId={selectedRoiId}
onActiveRegionChange={setSelectedRoiId}
onPointerWorldMove={(event) => {
// event.coordinate -> [x, y] | null
}}
onStats={(s) => console.log(s)}
style={{ width: "100vw", height: "100vh" }}
/>
);
}
4. 카메라 제한/전환 + 타일 색상 보정
<WsiViewerCanvas
source={source}
imageColorSettings={{
brightness: 0,
contrast: 0,
saturation: 0,
}}
minZoom={0.25}
maxZoom={1}
viewTransition={{ duration: 300 }}
autoLiftRegionLabelAtMaxZoom
/>
색상 보정은 타일 셰이더에만 적용됩니다. point/cell marker, ROI, draw overlay 색상은 변경되지 않습니다.
<WsiViewerCanvas
source={source}
zoomSnaps={[1.25, 2.5, 5, 10, 20, 40]}
zoomSnapFitAsMin
/>
zoomSnaps는 배율 단계이며, 내부적으로 source.mpp 기준 zoom 값으로 정규화됩니다.
5. 포인트 데이터 전달 + term 팔레트 매핑
포인트 로딩/파싱(ZST/MVT 디코딩 등)은 라이브러리 외부에서 처리합니다. 파싱 완료된 TypedArray를 뷰어에 전달하세요.
import { buildTermPalette } from "open-plant";
// positions: Float32Array [x0,y0,x1,y1,...] (직접 구현한 로더)
// paletteIndices: Uint16Array (term 테이블에서 매핑)
const termPalette = buildTermPalette(source.terms);
const pointData = {
count: positions.length / 2,
positions,
paletteIndices,
fillModes, // optional Uint8Array (0: ring, 1: solid)
};
<WsiViewerCanvas
source={source}
authToken={toBearerToken(token)}
pointData={pointData}
pointPalette={termPalette.colors}
/>
6. ROI 폴리곤 클리핑
<WsiViewerCanvas
source={source}
pointData={pointData}
pointPalette={termPalette.colors}
roiRegions={[
{
id: "roi-1",
label: "Tumor Core",
coordinates: [
[12000, 14000],
[18000, 14000],
[18000, 20000],
[12000, 20000],
[12000, 14000],
],
},
]}
clipPointsToRois
/>
7. ROI term 통계 콜백
<WsiViewerCanvas
source={source}
pointData={pointData}
roiRegions={regions}
roiPaletteIndexToTermId={new Map([[1, "negative"], [2, "positive"]])}
onRoiPointGroups={(stats) => {
// stats.groups -> ROI별 term count
}}
/>
8. ROI 가속 모드(worker / hybrid-webgpu)
import { getWebGpuCapabilities } from "open-plant";
const caps = await getWebGpuCapabilities();
const clipMode = caps.supported ? "hybrid-webgpu" : "worker";
<WsiViewerCanvas
source={source}
pointData={pointData}
pointPalette={termPalette.colors}
roiRegions={regions}
clipPointsToRois
clipMode={clipMode} // "sync" | "worker" | "hybrid-webgpu"
onClipStats={(stats) => {
console.log(stats.mode, stats.durationMs, stats.outputCount);
}}
/>
권장 기본값: 먼저
clipMode="worker"를 사용하세요.
hybrid-webgpu는 실데이터 벤치마크 후 선택하는 것을 권장합니다.