import { nrrdSliceType, storeExportPaintImageType, loadingBarType } from "../../types/types"; import { GUI } from "dat.gui"; import { IMaskData, IDragOpts, IKeyBoardSettings, ToolMode, IGuiMeta } from "./core/types"; import { DragOperator } from "./DragOperator"; import type { ChannelValue, RGBAColor, ChannelColorMap } from "./core"; import type { SphereType } from "./tools/SphereTool"; import type { AiPromptTool, AiPromptPayload, AiMaskResult } from "./tools/AiAssistTool"; /** * Core NRRD annotation tool for medical image segmentation. * * Acts as a **Facade** that delegates to composed modules: * - {@link CanvasState} — pure state container * - {@link DrawToolCore} — tool orchestration, event routing * - {@link LayerChannelManager} — layer/channel/sphere-type management * - {@link SliceRenderPipeline} — slice rendering, canvas flip, mask reload * - {@link DataLoader} — NRRD slice loading, legacy mask loading, NIfTI loading * * No longer extends DrawToolCore — uses composition instead. * * @example * ```ts * // Default 3 layers: layer1, layer2, layer3 * const tools = new NrrdTools(container); * * // Custom layers * const tools = new NrrdTools(container, { layers: ["layer1", "layer2"] }); * ``` */ export declare class NrrdTools { container: HTMLDivElement; /** Shared state container. */ private state; /** Core drawing tool orchestrator. */ private drawCore; /** Drag operator for slice navigation. */ dragOperator: DragOperator; private preTimer; private guiParameterSettings; private _sliceRAFId; private _pendingSliceStep; /** Whether calculator mode is active (not part of gui_states interface) */ private _calculatorActive; /** Layer/channel management (extracted module) */ private layerChannelManager; /** Slice rendering pipeline (extracted module) */ private sliceRenderPipeline; /** Data loading (extracted module) */ private dataLoader; /** Stored closure callbacks from gui.ts setupGui() */ private guiCallbacks; constructor(container: HTMLDivElement, options?: { layers?: string[]; }); /** * Wire DrawToolCore's overridable methods to NrrdTools implementations. * This replaces the old inheritance approach. */ private wireDrawCoreMethods; /** * A initialise function for nrrd_tools */ private init; private initNrrdToolsModules; /** * Set up GUI for drawing panel * @param gui GUI */ setupGUI(gui: GUI): void; /** * Sync guiParameterSettings with current volume metadata. */ private syncGuiParameterSettings; /** * Set the current tool mode. */ setMode(mode: ToolMode): void; getMode(): ToolMode; isCalculatorActive(): boolean; setOpacity(value: number): void; getOpacity(): number; setBrushSize(size: number): void; getBrushSize(): number; setSphereBrushRadius(radius: number): void; getSphereBrushRadius(): number; setWindowHigh(value: number): void; setWindowLow(value: number): void; finishWindowAdjustment(): void; adjustContrast(type: "windowHigh" | "windowLow", delta: number): void; getSliderMeta(key: string): IGuiMeta | null; setPencilColor(hex: string): void; getPencilColor(): string; executeAction(action: "undo" | "redo" | "clearActiveSliceMask" | "clearActiveLayerMask" | "resetZoom" | "downloadCurrentMask" | "gaussianSmooth", opts?: { sigma?: number; }): void; undo(): void; redo(): void; enterKeyboardConfig(): void; exitKeyboardConfig(): void; setContrastShortcutEnabled(enabled: boolean): void; isContrastShortcutEnabled(): boolean; setKeyboardSettings(settings: Partial): void; getKeyboardSettings(): IKeyBoardSettings; drag(opts?: IDragOpts): void; /** * Core drawing entry point. */ draw(opts?: any): void; /** * Set the Draw Display Canvas base size */ setBaseDrawDisplayCanvasesSize(size: number): void; setDisplaySliceIndexPanel(panel: HTMLDivElement): void; enableContrastDragEvents(callback: (step: number, towards: "horizental" | "vertical") => void): void; setPencilIconUrls(urls: string[]): void; /** * Switch all contrast slices' orientation */ setSliceOrientation(axisTo: "x" | "y" | "z"): void; addSkip(index: number): void; removeSkip(index: number): void; setSliceMoving(step: number): void; setMainAreaSize(factor: number): void; switchAllSlicesArrayData(allSlices: Array): void; /** * Swap slice data while preserving current slice index, zoom, and pan position. * Used for register/origin image switching where the view state should persist. */ switchSlicesPreservingView(allSlices: Array): void; appendLoadingbar(loadingbar: HTMLDivElement): void; getCurrentImageDimension(): number[]; getVoxelSpacing(): number[]; getSpaceOrigin(): number[]; getMaskData(): IMaskData; getContainer(): HTMLElement; getDrawingCanvas(): HTMLCanvasElement; getNrrdToolsSettings(): import("./coreTools/NrrdState").NrrdState; getMaxSliceNum(): number[]; getCurrentSlicesNumAndContrastNum(): { currentSliceIndex: number; contrastIndex: number; }; getCurrentSliceIndex(): number; getIsShowContrastState(): boolean; setActiveLayer(layerId: string): void; getActiveLayer(): string; setActiveChannel(channel: ChannelValue): void; getActiveChannel(): number; setActiveSphereType(type: SphereType): void; getActiveSphereType(): SphereType; setLayerVisible(layerId: string, visible: boolean): void; isLayerVisible(layerId: string): boolean; setChannelVisible(layerId: string, channel: ChannelValue, visible: boolean): void; isChannelVisible(layerId: string, channel: ChannelValue): boolean; getLayerVisibility(): Record; getChannelVisibility(): Record>; hasLayerData(layerId: string): boolean; setChannelColor(layerId: string, channel: number, color: RGBAColor): void; getChannelColor(layerId: string, channel: number): RGBAColor; getChannelHexColor(layerId: string, channel: number): string; getChannelCssColor(layerId: string, channel: number): string; setChannelColors(layerId: string, colorMap: Partial): void; setAllLayersChannelColor(channel: number, color: RGBAColor): void; resetChannelColors(layerId?: string, channel?: number): void; setLayerOpacity(layerId: string, opacity: number): void; getLayerOpacity(layerId: string): number; getLayerOpacityMap(): Record; updateOriginAndChangedWH(): void; setSyncsliceNum(): void; resetPaintAreaUIPosition(l?: number, t?: number): void; resetLayerCanvas(): void; redrawMianPreOnDisplayCanvas(): void; resizePaintArea(factor: number): void; flipDisplayImageByAxis(): void; setEmptyCanvasSize(axis?: "x" | "y" | "z"): void; redrawDisplayCanvas(): void; private reloadMasksFromVolume; setAllSlices(allSlices: Array): void; setMasksData(masksData: storeExportPaintImageType, loadingBar?: loadingBarType): void; setMasksFromNIfTI(layerVoxels: Map, loadingBar?: loadingBarType): void; setCalculateDistanceSphere(x: number, y: number, sliceIndex: number, cal_position: "tumour" | "skin" | "nipple" | "ribcage"): void; /** * Refresh sphere canvas from sphereMaskVolume for the current slice/axis. */ private refreshSphereOverlay; /** * Enter sphere mode. */ enterSphereMode(): void; /** * Exit sphere mode. */ exitSphereMode(): void; /** Whether AI-assist mode is currently active. */ private _aiAssistActive; /** Layer visibility snapshot taken on enter, restored on exit. */ private _aiPrevVisibility; isAiAssistActive(): boolean; /** * Enter AI-assist mode (sandbox): hides the existing layer masks so ONLY the * AI overlay is shown, takes canvas ownership (left-click = prompt), and creates * the scratch volume. Right-drag still pans; wheel/slider still scrub slices. * The hidden masks are restored on exit (merge writes into them first if asked). */ enterAiAssistMode(): void; /** * Exit AI-assist mode: drop the scratch volume, restore normal tooling AND the * layer-mask visibility hidden on enter. If the caller merged first, the merged * result is now part of the layer volume and reappears with the restored masks. */ exitAiAssistMode(): void; aiSetPromptTool(tool: AiPromptTool): void; aiSetPolarity(label: number): void; /** Select the active Segmentation by its label value (the AI paints into it). */ aiSetActiveSegment(label: number): void; /** Set a Segmentation's colour (its label's colorMap entry; 2D overlay repaints). */ aiSetSegmentColor(label: number, color: { r: number; g: number; b: number; a: number; }): void; /** "New segmentation": freeze current regions + switch to a new label. */ aiNewSegment(label: number): void; /** Delete a Segmentation: erase all its label's voxels from scratch + committed. */ aiClearSegment(label: number): void; /** Set the scribble brush radius (px). */ aiSetScribbleSize(size: number): void; /** Register the callback invoked when a prompt gesture completes (app → backend). */ aiOnPrompt(cb: (payload: AiPromptPayload) => void): void; /** Register the callback fired when the lasso vertex set changes (drives the Finish button). */ aiOnLassoChange(cb: (count: number, editing: boolean) => void): void; /** Apply a backend mask result into the scratch volume (overlay repaints next frame). */ aiApplyMask(result: AiMaskResult): void; /** Clear the in-progress prompt set (e.g. slice change). */ aiClearPrompts(): void; /** Close + send the in-progress lasso to the backend (needs ≥3 vertices). */ aiFinishLasso(): void; /** Undo the last lasso vertex add/delete. */ aiLassoUndo(): void; /** Redo the last undone lasso vertex change. */ aiLassoRedo(): void; /** Abandon the in-progress lasso (Esc). */ aiCancelLasso(): void; /** True while the user is placing lasso vertices. */ aiIsLassoEditing(): boolean; /** Number of placed lasso vertices (drives the Finish button). */ aiLassoVertCount(): number; /** "New region": freeze current regions (they persist) + start a fresh prompt set. */ aiCommitRegion(): void; /** Discard all AI scratch painting since enter (sandbox discard). */ aiDiscard(): void; /** True if the scratch volume holds any predicted voxels. */ aiHasData(): boolean; /** Serialize the AI scratch as per-slice RLE for persisting to ai_generated_nii_LPS. */ aiGetScratchSlices(): { axis: "z"; width: number; height: number; slices: { sliceIndex: number; rle: number[]; }[]; } | null; /** Per-segmentation serialization (one binary RLE per label per z-slice) for the * multi-label / per-colour 3D build. */ aiGetScratchSegments(): { axis: "z"; width: number; height: number; segments: { label: number; slices: { sliceIndex: number; rle: number[]; }[]; }[]; } | null; /** * Merge the AI scratch into a target layer with an explicit per-segmentation * mapping, as a single undoable group (sandbox merge — non-destructive + one * Ctrl+Z). `mapping` is `{ AI scratch label -> target channel(1-8) }`; any label * absent from the mapping (or mapped to 0) is DISCARDED (not merged). Several AI * labels MAY map to the SAME channel — after merge the channel is the identity * (CLAUDE.md decision M2), so the merged voxels take that channel's fixed colour. * * UNION semantics (M4): an AI voxel only fills a target voxel that is currently * EMPTY (0) — existing manual annotation is NEVER erased. Where two AI labels map * to different channels and overlap the same voxel, the first one written wins * (iteration order), an accepted edge case for a single-value-per-voxel mask. * Scans all z-slices, so voxels painted from any view are caught. */ aiCommitToLayerMapped(targetLayer: string, mapping: Record): void; /** * Back-compat shim: merge with an IDENTITY mapping (each AI label → the same * channel number) via the mapped path above. Newer callers should use * `aiCommitToLayerMapped` with an explicit per-segmentation mapping. */ aiCommitToLayer(targetLayer?: string): void; clearActiveLayer(): void; reset(): void; configMouseSliceWheel(): (e: WheelEvent) => void; updateMouseWheelEvent(): void; setIsDrawFalse(target: number): void; private setShowInMainArea; private clearDictionary; /** * Get the DrawToolCore's start() render loop callback. * This is called by DragSliceTool's requestAnimationFrame loop. */ get start(): () => void; /** * Expose drawCalculatorSphereOnEachViews for external use. */ drawCalculatorSphereOnEachViews(axis: "x" | "y" | "z"): void; /** * Copy all voxel data from one layer's MaskVolume to another. * * After the copy the target layer's display is refreshed so the UI * shows the updated data immediately. The target layer's color map * is preserved — only the raw voxel buffer is overwritten. * * Use case: when editing layer2 cascades to layer3 on the backend, * call this to keep the frontend in sync without a network round-trip. * * @param sourceLayerId Layer to copy from (e.g. "layer2"). * @param targetLayerId Layer to copy into (e.g. "layer3"). * * @example * ```ts * nrrdTools.copyLayerData("layer2", "layer3"); * ``` */ copyLayerData(sourceLayerId: string, targetLayerId: string): void; }