Layouts and slice types
A viewer arranges its canvas into tiles: 2D slices, a 3D render, or both. You pick the arrangement with a few layout properties. Like every viewer setting, they can go in the constructor or be assigned later, and the view redraws.
import NiiVue, { MULTIPLANAR_TYPE, SLICE_TYPE } from '@niivue/niivue'
const mosaic = 'A -20 0 20 40 ; C -30 0 30 S R X 0'
const demo = async (canvas, { ui, asset }) => {
const nv = new NiiVue({ backgroundColor: [0.2, 0.2, 0.2, 1] })
await nv.attachToCanvas(canvas)
await nv.loadVolumes([{ url: asset('volumes/mni152.nii.gz') }])
ui.select(
'View',
[
['Axial', String(SLICE_TYPE.AXIAL)],
['Coronal', String(SLICE_TYPE.CORONAL)],
['Sagittal', String(SLICE_TYPE.SAGITTAL)],
['Multiplanar', String(SLICE_TYPE.MULTIPLANAR)],
['Render', String(SLICE_TYPE.RENDER)],
['Mosaic', 'mosaic'],
],
String(SLICE_TYPE.MULTIPLANAR),
(v) => {
if (v === 'mosaic') {
nv.mosaicString = mosaic
return
}
nv.mosaicString = ''
nv.sliceType = Number(v)
},
)
ui.select(
'Multiplanar',
[
['Auto', String(MULTIPLANAR_TYPE.AUTO)],
['Column', String(MULTIPLANAR_TYPE.COLUMN)],
['Grid', String(MULTIPLANAR_TYPE.GRID)],
['Row', String(MULTIPLANAR_TYPE.ROW)],
],
String(MULTIPLANAR_TYPE.AUTO),
(v) => {
nv.multiplanarType = Number(v)
},
)
return nv
}
export default demo
Slice type
sliceType takes a SLICE_TYPE value:
| Value | View |
|---|---|
SLICE_TYPE.AXIAL (0) | one axial slice |
SLICE_TYPE.CORONAL (1) | one coronal slice |
SLICE_TYPE.SAGITTAL (2) | one sagittal slice |
SLICE_TYPE.MULTIPLANAR (3, default) | all three slices, plus a render when there is room |
SLICE_TYPE.RENDER (4) | the 3D render only |
SLICE_TYPE.NONE (5) | no spatial view; the whole canvas goes to the signal graph |
import NiiVue, { SLICE_TYPE } from '@niivue/niivue'
const nv = new NiiVue({ sliceType: SLICE_TYPE.AXIAL })
nv.sliceType = SLICE_TYPE.RENDER
A single 2D slice fills the canvas by default. Set isSingleViewFillCanvas
to false to letterbox it to the slice's aspect ratio instead.
Multiplanar options
In multiplanar mode, multiplanarType sets how the tiles are arranged:
MULTIPLANAR_TYPE.AUTO (picks a layout that suits the canvas shape),
COLUMN, GRID or ROW.
showRender decides whether the 3D render joins the slices.
SHOW_RENDER.ALWAYS always adds it and SHOW_RENDER.NEVER never does. The
default, SHOW_RENDER.AUTO, adds it only when that does not make the slices
smaller.
A few more properties shape the tiles:
heroFraction(0 to 1) gives one large "hero" tile that share of the canvas, andheroSliceTypepicks which view it shows (the render by default).isEqualSizemakes every tile the same size, instead of sizing each one to its slice's extent.tileMarginadds a gap, in pixels, between tiles.layoutInsetkeeps a strip of the canvas clear of tiles, colorbar, legend and graph:[left, top, right, bottom]in CSS pixels. Overlays still draw on the whole canvas, so in-canvas controls can sit there.isRadiologicalflips left and right.
nv.multiplanarType = MULTIPLANAR_TYPE.GRID
nv.showRender = SHOW_RENDER.ALWAYS
nv.heroFraction = 0.6
Mosaics
A mosaic shows many slices at once. Set mosaicString to a short list of
tokens; while it is non-empty it overrides sliceType. Set it back to ''
to return to the normal layout.
A,CandSchoose axial, coronal or sagittal for the numbers that follow.- Each number is a slice position in mm and becomes one tile.
Rmakes the next tile a 3D render seen from that orientation. A negative number (or-0) views it from the opposite side.Xdraws cross-lines on the next tile, marking where the other slices sit.Lturns slice labels on andL-turns them off.;starts a new row.
nv.mosaicString = 'A -20 0 20 40 ; C -30 0 30 S R X 0'
isMosaicCentered (on by default) centers the mosaic on the canvas.
Custom layouts
For full control, give customLayout a list of tiles. Each tile has a
sliceType and a position of [left, top, width, height], all as
fractions of the canvas, measured from the top left. A tile can also fix its
slice at sliceMM, or set fill: true to fill its pane instead of keeping
the slice's aspect ratio.
nv.customLayout = [
{ sliceType: SLICE_TYPE.SAGITTAL, position: [0, 0, 0.5, 1] },
{ sliceType: SLICE_TYPE.CORONAL, position: [0.5, 0, 0.5, 0.5] },
{ sliceType: SLICE_TYPE.AXIAL, position: [0.5, 0.5, 0.5, 0.5] },
]
nv.clearCustomLayout() // back to the built-in layouts
The setter checks the list and throws if a tile is invalid.
Several viewers in one canvas
Two or more viewers can share a canvas, each drawing into its own region.
Pass bounds to the constructor as [[x1, y1], [x2, y2]], or call
setBounds([x1, y1, x2, y2]) later. Coordinates are fractions of the canvas,
with y = 0 at the bottom (unlike customLayout). clearBounds() gives
a viewer the whole canvas again, and showBoundsBorder,
boundsBorderColor and boundsBorderThickness outline each region.
import NiiVue, { SLICE_TYPE } from '@niivue/niivue'
const demo = async (canvas, { ui, asset }) => {
const nv1 = new NiiVue({
bounds: [
[0, 0],
[0.5, 1],
],
showBoundsBorder: true,
})
const nv2 = new NiiVue({
bounds: [
[0.5, 0],
[1, 1],
],
showBoundsBorder: true,
})
await nv1.attachToCanvas(canvas)
await nv2.attachToCanvas(canvas)
await nv1.loadVolumes([{ url: asset('volumes/mni152.nii.gz') }])
await nv2.loadVolumes([
{ url: asset('volumes/mni152.nii.gz'), colormap: 'hot' },
])
nv1.sliceType = SLICE_TYPE.AXIAL
nv2.sliceType = SLICE_TYPE.RENDER
ui.select('Split', ['Side by side', 'Stacked'], 'Side by side', (v) => {
// [x1, y1, x2, y2], with y = 0 at the bottom of the canvas
const stacked = v === 'Stacked'
nv1.setBounds(stacked ? [0, 0.5, 1, 1] : [0, 0, 0.5, 1])
nv2.setBounds(stacked ? [0, 0, 1, 0.5] : [0.5, 0, 1, 1])
})
return [nv1, nv2]
}
export default demo
Each viewer has its own volumes and settings. If you move one viewer's
region, call drawScene() on the others so the shared canvas repaints. To
make the viewers follow each other, see Linking viewers.
Viewers that share a canvas also share its backend, so a shared canvas does
not fall back from WebGPU to WebGL2: if you ask for backend: 'webgpu' and
the machine has no WebGPU adapter, attachToCanvas throws.
See also
- Layout: every layout property in one page.
- Custom layout: several predefined
customLayouts. - Bounds: two linked viewers on one canvas.