UNPKG

@itk-viewer/element

Version:
168 lines 7.91 kB
import { SelectorController } from 'xstate-lit'; import { TransferFunctionEditor } from '@itk-viewer/transfer-function-editor/TransferFunctionEditor.js'; import { ColorTransferFunction } from '@itk-viewer/transfer-function-editor/ColorTransferFunction.js'; const equalFloats = (a, b) => Math.abs(a - b) < 1e-6; export class ViewControls { constructor(host) { this.selectedComponent = 0; this.view = '2d'; this.colorTransferFunctions = new Map(); // component -> colorTransferFunction this.onSlice = (event) => { const target = event.target; const slice = Number(target.value); this.actor.send({ type: 'setSlice', slice, }); }; this.onAxis = (event) => { const target = event.target; const axis = target.value; this.actor.send({ type: 'setAxis', axis, }); }; this.onScale = (event) => { const target = event.target; const scale = Number(target.value); this.actor.send({ type: 'setScale', scale }); }; this.onSelectedComponent = (component) => { this.selectedComponent = component; this.updateTransferFunctionEditor(); }; this.onColorMap = (colorMap) => { this.imageActor?.send({ type: 'colorMap', component: this.selectedComponent, colorMap, }); }; this.setTransferFunctionContainer = (container) => { if (container) { this.transferFunctionEditor = new TransferFunctionEditor(container); this.transferFunctionEditor.setColorTransferFunction(new ColorTransferFunction()); this.updateTransferFunctionEditor(); this.transferFunctionEditor.eventTarget.addEventListener('colorRange', (e) => { const range = e.detail; this.imageActor?.send({ type: 'normalizedColorRange', range, component: this.selectedComponent, }); }); this.transferFunctionEditor.eventTarget.addEventListener('updated', (e) => { const points = e.detail; this.imageActor?.send({ type: 'normalizedOpacityPoints', points, component: this.selectedComponent, }); }); } else { this.transferFunctionEditor?.remove(); this.transferFunctionEditor = undefined; } }; this.onViewSnapshot = (snapshot) => { const { imageActor, spawned } = snapshot.context; if (this.imageActor !== imageActor) { this.imageSubscription?.unsubscribe(); this.imageSubscription = undefined; } this.imageActor = imageActor; // If imageActor exists and there's no subscription, subscribe to it. if (this.imageActor && !this.imageSubscription) { this.imageSubscription = this.imageActor.subscribe(this.onImageActorSnapshot.bind(this)); this.onImageActorSnapshot(this.imageActor.getSnapshot()); this.imageDimension = new SelectorController(this.host, this.imageActor, (state) => state.context.image?.imageType.dimension ?? 0); this.colorMaps = new SelectorController(this.host, this.imageActor, (state) => state.context.colorMaps); this.componentCount = new SelectorController(this.host, this.imageActor, (state) => state.context.image?.imageType.components ?? 1); } const renderer = Object.values(spawned)?.[0]; if (this.renderer !== renderer) { this.rendererSubscription?.unsubscribe(); this.rendererSubscription = undefined; } this.renderer = renderer; if (this.renderer && !this.rendererSubscription) { this.colorMapsOptions = new SelectorController(this.host, renderer, (state) => state.context.colorMapOptions ?? {}); this.rendererSubscription = renderer.on('colorTransferFunctionApplied', this.onColorTransferFunction); } }; this.onImageActorSnapshot = (snapshot) => { if (!this.transferFunctionEditor) return; const component = this.selectedComponent; const { dataRanges, normalizedColorRanges, normalizedOpacityPoints } = snapshot.context; const componentRange = dataRanges[component]; if (componentRange) { this.transferFunctionEditor.setRange(componentRange); } if (normalizedColorRanges.length === 0) return; const currentColorRange = this.transferFunctionEditor.getColorRange(); // avoid infinite loop const colorRange = normalizedColorRanges[component]; const changed = currentColorRange?.some((v, i) => !equalFloats(v, colorRange[i])); if (changed) { this.transferFunctionEditor.setColorRange(colorRange); } const currentPoints = this.transferFunctionEditor.getPoints(); const opacityPoints = normalizedOpacityPoints[component]; const pointsChanged = currentPoints?.some((point, i) => !equalFloats(point[0], opacityPoints[i][0]) || !equalFloats(point[1], opacityPoints[i][1])); if (pointsChanged) { this.transferFunctionEditor.setPoints(opacityPoints); } this.updateColorTransferFunction(); }; this.setView = (view) => { this.view = view; this.updateTransferFunctionEditor(); }; this.updateTransferFunctionEditor = () => { const rangeViewOnly = this.view === '2d'; this.transferFunctionEditor?.setRangeViewOnly(rangeViewOnly); if (this.imageActor) { this.onImageActorSnapshot(this.imageActor.getSnapshot()); } }; this.updateColorTransferFunction = () => { const ct = this.colorTransferFunctions.get(this.selectedComponent); if (!ct) return; this.transferFunctionEditor?.setColorTransferFunction(ct); }; this.onColorTransferFunction = ({ colorTransferFunction, component, }) => { this.colorTransferFunctions.set(component, colorTransferFunction); this.updateColorTransferFunction(); }; this.host = host; host.addController(this); } hostConnected() { // no-op } setActor(actor) { this.actor = actor; this.scale = new SelectorController(this.host, this.actor, (state) => state.context.scale); this.scaleCount = new SelectorController(this.host, this.actor, (state) => { const image = state.context.image; if (!image) return 1; return image.coarsestScale + 1; }); this.slice = new SelectorController(this.host, this.actor, (state) => state.context.slice); this.axis = new SelectorController(this.host, this.actor, (state) => state.context.axis); this.host.requestUpdate(); // trigger render with selected state // wire up Transfer Function Editor if (this.viewSubscription) this.viewSubscription.unsubscribe(); this.viewSubscription = this.actor.subscribe(this.onViewSnapshot.bind(this)); this.onViewSnapshot(this.actor.getSnapshot()); } } //# sourceMappingURL=view-controls-controller.js.map