|
2 | 2 | <div class="vtk-container-wrapper"> |
3 | 3 | <div class="vtk-gutter"></div> |
4 | 4 | <div class="vtk-container" :class="active ? 'active' : ''"> |
5 | | - <div class="vtk-sub-container" ref="containerParent"></div> |
| 5 | + <div class="vtk-sub-container"> |
| 6 | + <div class="vtk-view" ref="vtkContainer" /> |
| 7 | + </div> |
6 | 8 | </div> |
7 | 9 | </div> |
8 | 10 | </template> |
9 | 11 |
|
10 | 12 | <script> |
11 | | -import { mapGetters } from 'vuex'; |
| 13 | +import { |
| 14 | + ref, |
| 15 | + toRefs, |
| 16 | + watch, |
| 17 | + unref, |
| 18 | + reactive, |
| 19 | + watchEffect, |
| 20 | + computed, |
| 21 | +} from '@vue/composition-api'; |
12 | 22 |
|
13 | | -import VtkViewMixin, { attachResizeObserver } from '@/src/mixins/VtkView'; |
| 23 | +import { |
| 24 | + CommonViewProps, |
| 25 | + useVtkView, |
| 26 | + useVtkViewCameraOrientation, |
| 27 | + giveViewAnnotations, |
| 28 | +} from '@/src/composables/view/common'; |
| 29 | +import { useResizeObserver } from '@/src/composables/resizeObserver'; |
| 30 | +import { watchScene, watchColorBy } from '@/src/composables/scene'; |
| 31 | +import { useComputedState } from '@/src/composables/store'; |
| 32 | +import { useProxyManager } from '@/src/composables/proxyManager'; |
14 | 33 |
|
15 | 34 | export default { |
16 | 35 | name: 'VtkThreeView', |
| 36 | + props: CommonViewProps, |
| 37 | + setup(props) { |
| 38 | + const { viewName, viewType, viewUp, axis, orientation } = toRefs(props); |
| 39 | + const vtkContainer = ref(null); |
17 | 40 |
|
18 | | - mixins: [VtkViewMixin], |
| 41 | + const pxm = useProxyManager(); |
19 | 42 |
|
20 | | - computed: { |
21 | | - ...mapGetters('visualization', [ |
22 | | - 'boundsWithSpacing', |
23 | | - 'baseImageColorPreset', |
24 | | - ]), |
25 | | - }, |
| 43 | + const { |
| 44 | + sceneSources, |
| 45 | + worldOrientation, |
| 46 | + colorBy, |
| 47 | + boundsWithSpacing, |
| 48 | + baseImageColorPreset, |
| 49 | + baseImage, |
| 50 | + slices, |
| 51 | + windowing, |
| 52 | + } = useComputedState({ |
| 53 | + sceneSources(state, getters) { |
| 54 | + const { pipelines } = state.visualization; |
| 55 | + return getters.sceneObjectIDs |
| 56 | + .filter((id) => id in pipelines) |
| 57 | + .map((id) => pipelines[id].last); |
| 58 | + }, |
| 59 | + worldOrientation: (state) => state.visualization.worldOrientation, |
| 60 | + colorBy: (state, getters) => |
| 61 | + getters.sceneObjectIDs.map((id) => state.visualization.colorBy[id]), |
| 62 | + boundsWithSpacing: (_, getters) => |
| 63 | + getters['visualization/boundsWithSpacing'], |
| 64 | + baseImageColorPreset: (_, getters) => |
| 65 | + getters['visualization/baseImageColorPreset'], |
| 66 | + baseImage(state) { |
| 67 | + const { pipelines } = state.visualization; |
| 68 | + const { selectedBaseImage } = state; |
| 69 | + if (selectedBaseImage in pipelines) { |
| 70 | + return pipelines[selectedBaseImage].last; |
| 71 | + } |
| 72 | + return null; |
| 73 | + }, |
| 74 | + slices: (state) => state.visualization.slices, |
| 75 | + windowing: (state) => state.visualization.windowing, |
| 76 | + }); |
26 | 77 |
|
27 | | - watch: { |
28 | | - sceneSources() { |
29 | | - this.updateOrientation(); |
30 | | - this.resetCamera(); |
31 | | - }, |
32 | | - boundsWithSpacing() { |
33 | | - this.resetCamera(); |
34 | | - }, |
35 | | - baseImageColorPreset(preset) { |
36 | | - this.setColorPresetAnnotation(preset); |
37 | | - }, |
38 | | - }, |
| 78 | + const spacing = computed(() => worldOrientation.value.spacing); |
39 | 79 |
|
40 | | - mounted() { |
41 | | - this.resizeObserver = attachResizeObserver( |
42 | | - this.$refs.containerParent, |
43 | | - this.resizeLater |
44 | | - ); |
45 | | - }, |
| 80 | + const viewRef = useVtkView({ |
| 81 | + containerRef: vtkContainer, |
| 82 | + viewName, |
| 83 | + viewType, |
| 84 | + }); |
46 | 85 |
|
47 | | - beforeDestroy() { |
48 | | - this.resizeObserver.unobserve(this.$refs.containerParent); |
49 | | - }, |
| 86 | + // handle camera orientation |
| 87 | + useVtkViewCameraOrientation(viewRef, viewUp, axis, orientation); |
50 | 88 |
|
51 | | - methods: { |
52 | | - afterViewMount() { |
53 | | - this.view.setBackground(0.1, 0.2, 0.3); |
54 | | - this.view.setOrientationAxesType('cube'); |
55 | | - this.setColorPresetAnnotation(this.baseImageColorPreset); |
56 | | - }, |
57 | | - setColorPresetAnnotation(preset) { |
58 | | - if (this.view) { |
59 | | - this.view.setCornerAnnotation('nw', preset || 'No colormap'); |
| 89 | + useResizeObserver(vtkContainer, () => { |
| 90 | + const view = unref(viewRef); |
| 91 | + if (view) { |
| 92 | + view.resize(); |
60 | 93 | } |
61 | | - }, |
| 94 | + }); |
| 95 | +
|
| 96 | + // update scene sources and their colors |
| 97 | + watchScene(sceneSources, worldOrientation, viewRef); |
| 98 | + watchColorBy(colorBy, sceneSources, viewRef); |
| 99 | +
|
| 100 | + // prepare view |
| 101 | + watchEffect(() => { |
| 102 | + const view = unref(viewRef); |
| 103 | + if (view) { |
| 104 | + view.setBackground(0.1, 0.2, 0.3); |
| 105 | + view.setOrientationAxesType('cube'); |
| 106 | + } |
| 107 | + }); |
| 108 | +
|
| 109 | + // set wl and slices |
| 110 | + watchEffect(() => { |
| 111 | + if (viewRef.value && baseImage.value) { |
| 112 | + const rep = pxm.getRepresentation(baseImage.value, viewRef.value); |
| 113 | + if (rep) { |
| 114 | + rep.setXSlice(slices.value.x * spacing.value[0]); |
| 115 | + rep.setYSlice(slices.value.y * spacing.value[1]); |
| 116 | + rep.setZSlice(slices.value.z * spacing.value[2]); |
| 117 | + rep.setWindowWidth(windowing.value.width); |
| 118 | + rep.setWindowLevel(windowing.value.level); |
| 119 | + } |
| 120 | + } |
| 121 | + }); |
| 122 | +
|
| 123 | + // reset camera whenever bounds changes |
| 124 | + watch(boundsWithSpacing, () => { |
| 125 | + const view = unref(viewRef); |
| 126 | + if (view) { |
| 127 | + view.resetCamera(); |
| 128 | + } |
| 129 | + }); |
| 130 | +
|
| 131 | + giveViewAnnotations( |
| 132 | + viewRef, |
| 133 | + reactive({ |
| 134 | + nw: baseImageColorPreset, |
| 135 | + }), |
| 136 | + { |
| 137 | + nw: 'No colormap', |
| 138 | + } |
| 139 | + ); |
| 140 | +
|
| 141 | + return { |
| 142 | + vtkContainer, // dom ref |
| 143 | + active: true, |
| 144 | + }; |
62 | 145 | }, |
63 | 146 | }; |
64 | 147 | </script> |
65 | 148 |
|
66 | 149 | <style src="@/src/assets/styles/vtk-view.css"></style> |
| 150 | + |
| 151 | +<style scoped> |
| 152 | +.vtk-gutter { |
| 153 | + display: flex; |
| 154 | + flex-flow: column; |
| 155 | +} |
| 156 | +</style> |
0 commit comments