|
|
<script lang="ts"> |
|
|
import { onMount, onDestroy } from 'svelte'; |
|
|
import { Niivue } from '@niivue/niivue'; |
|
|
import { Block } from "@gradio/atoms"; |
|
|
import { StatusTracker } from "@gradio/statustracker"; |
|
|
import type { LoadingStatus } from "@gradio/statustracker"; |
|
|
import type { Gradio } from "@gradio/utils"; |
|
|
|
|
|
interface Props { |
|
|
value?: { background_url: string | null; overlay_url: string | null } | null; |
|
|
label?: string; |
|
|
show_label?: boolean; |
|
|
loading_status?: LoadingStatus; |
|
|
elem_id?: string; |
|
|
elem_classes?: string[]; |
|
|
visible?: boolean; |
|
|
height?: number; |
|
|
container?: boolean; |
|
|
scale?: number; |
|
|
min_width?: number; |
|
|
|
|
|
gradio: Gradio<{ change: never }>; |
|
|
} |
|
|
|
|
|
let { |
|
|
value = null, |
|
|
label, |
|
|
show_label = true, |
|
|
loading_status, |
|
|
elem_id = "", |
|
|
elem_classes = [], |
|
|
visible = true, |
|
|
height = 500, |
|
|
container = true, |
|
|
scale = null, |
|
|
min_width = undefined, |
|
|
gradio |
|
|
}: Props = $props(); |
|
|
|
|
|
let div_container: HTMLDivElement; |
|
|
let nv: Niivue | null = null; |
|
|
let canvas: HTMLCanvasElement; |
|
|
let initialized = false; |
|
|
|
|
|
onMount(async () => { |
|
|
try { |
|
|
nv = new Niivue({ |
|
|
backColor: [0, 0, 0, 1], |
|
|
show3Dcrosshair: true, |
|
|
logging: false |
|
|
}); |
|
|
|
|
|
await nv.attachToCanvas(canvas); |
|
|
|
|
|
|
|
|
initialized = true; |
|
|
} catch (error) { |
|
|
console.error('[NiiVue] Initialization failed:', error); |
|
|
} |
|
|
}); |
|
|
|
|
|
onDestroy(() => { |
|
|
|
|
|
if (nv) { |
|
|
nv.cleanup(); |
|
|
nv = null; |
|
|
} |
|
|
}); |
|
|
|
|
|
async function loadVolumes() { |
|
|
if (!nv) return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (nv.volumes.length > 0) { |
|
|
nv.removeVolume(nv.volumes[0]); |
|
|
} |
|
|
|
|
|
if (!value) { |
|
|
nv.drawScene(); |
|
|
return; |
|
|
} |
|
|
|
|
|
const volumes = []; |
|
|
if (value.background_url) { |
|
|
volumes.push({ url: value.background_url }); |
|
|
} |
|
|
if (value.overlay_url) { |
|
|
volumes.push({ |
|
|
url: value.overlay_url, |
|
|
colormap: 'red', |
|
|
opacity: 0.5, |
|
|
}); |
|
|
} |
|
|
|
|
|
if (volumes.length > 0) { |
|
|
await nv.loadVolumes(volumes); |
|
|
} else { |
|
|
nv.drawScene(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
$effect(() => { |
|
|
if (initialized && value !== undefined) { |
|
|
loadVolumes(); |
|
|
} |
|
|
}); |
|
|
|
|
|
</script> |
|
|
|
|
|
<Block |
|
|
{visible} |
|
|
variant={"solid"} |
|
|
padding={false} |
|
|
{elem_id} |
|
|
{elem_classes} |
|
|
{height} |
|
|
allow_overflow={false} |
|
|
{container} |
|
|
{scale} |
|
|
{min_width} |
|
|
> |
|
|
{#if loading_status} |
|
|
<StatusTracker |
|
|
autoscroll={gradio.autoscroll} |
|
|
i18n={gradio.i18n} |
|
|
{...loading_status} |
|
|
/> |
|
|
{/if} |
|
|
|
|
|
<div bind:this={div_container} class="niivue-container" style="height: {height}px;"> |
|
|
<canvas bind:this={canvas}></canvas> |
|
|
</div> |
|
|
</Block> |
|
|
|
|
|
<style> |
|
|
.niivue-container { |
|
|
width: 100%; |
|
|
background: #000; |
|
|
position: relative; |
|
|
border-radius: var(--radius-lg); |
|
|
overflow: hidden; |
|
|
} |
|
|
canvas { |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
outline: none; |
|
|
display: block; |
|
|
} |
|
|
</style> |
|
|
|