Skip to content

Commit 658e1ce

Browse files
committed
Move camera in front of volume in 2D and 3D views
getLPSDirections comes from VolView Use forwardVector and upwardVector instead of axis, orientation, viewUp Get these info from a basis (3 vec3) instead of hardcoding it Replace VIEW_ORIENTATIONS and VIEW_TYPES with variables in the store Introduce the notion of master volume in the store Closes #463
1 parent 25f4161 commit 658e1ce

File tree

6 files changed

+305
-50
lines changed

6 files changed

+305
-50
lines changed

src/components/core/Datasets/script.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export default {
112112
}
113113

114114
this.datasets = sources.map((s) => s.getProxyId());
115+
this.$store.dispatch('views/updateMasterSourceId', this.datasets);
115116
},
116117
getSourceName(sourceId) {
117118
const proxy = this.$proxyManager.getProxyById(sourceId);
Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
export const DEFAULT_VIEW_TYPE = 'View3D:default';
22

3-
export const VIEW_TYPES = [
4-
{ text: '3D', value: 'View3D:default' },
5-
{ text: 'Orientation Y', value: 'View2D_Y:y' },
6-
{ text: 'Orientation X', value: 'View2D_X:x' },
7-
{ text: 'Orientation Z', value: 'View2D_Z:z' },
8-
];
3+
export const DEFAULT_AXIS_PRESET = 'default';
94

10-
export const VIEW_TYPES_LPS = [
11-
{ text: '3D', value: 'View3D:default' },
12-
{ text: 'Sagittal', value: 'View2D_Y:y' },
13-
{ text: 'Coronal', value: 'View2D_X:x' },
14-
{ text: 'Axial', value: 'View2D_Z:z' },
15-
];
5+
export const VIEW_TYPE_VALUES = {
6+
default: 'View3D:default',
7+
x: 'View2D_X:x',
8+
y: 'View2D_Y:y',
9+
z: 'View2D_Z:z',
10+
};
1611

1712
/* eslint-disable no-template-curly-in-string */
1813
export const CURSOR_ANNOTATIONS = {
@@ -26,25 +21,22 @@ export const ANNOTATIONS = {
2621
};
2722
/* eslint-enable no-template-curly-in-string */
2823

29-
export const VIEW_ORIENTATIONS = {
30-
default: {
31-
axis: 1,
32-
orientation: -1,
33-
viewUp: [0, 0, 1],
34-
},
35-
x: {
36-
axis: 0,
37-
orientation: 1,
38-
viewUp: [0, 0, 1],
39-
},
40-
y: {
41-
axis: 1,
42-
orientation: -1,
43-
viewUp: [0, 0, 1],
44-
},
45-
z: {
46-
axis: 2,
47-
orientation: -1,
48-
viewUp: [0, -1, 0],
49-
},
24+
export const DEFAULT_VIEW_TYPES = {
25+
[VIEW_TYPE_VALUES.default]: '3D',
26+
[VIEW_TYPE_VALUES.x]: 'Orientation X',
27+
[VIEW_TYPE_VALUES.y]: 'Orientation Y',
28+
[VIEW_TYPE_VALUES.z]: 'Orientation Z',
5029
};
30+
31+
export const DEFAULT_LPS_VIEW_TYPES = {
32+
[VIEW_TYPE_VALUES.default]: '3D',
33+
[VIEW_TYPE_VALUES.x]: 'Sagittal',
34+
[VIEW_TYPE_VALUES.y]: 'Coronal',
35+
[VIEW_TYPE_VALUES.z]: 'Axial',
36+
};
37+
38+
export const DEFAULT_VIEW_ORIENTATION = [
39+
[1, 0, 0],
40+
[0, 1, 0],
41+
[0, 0, 1],
42+
];

src/components/core/VtkView/script.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,14 @@ import { Breakpoints } from 'paraview-glance/src/constants';
44
import {
55
ANNOTATIONS,
66
DEFAULT_VIEW_TYPE,
7-
VIEW_TYPES,
8-
VIEW_TYPES_LPS,
9-
VIEW_ORIENTATIONS,
107
} from 'paraview-glance/src/components/core/VtkView/constants';
118

129
import PalettePicker from 'paraview-glance/src/components/widgets/PalettePicker';
1310
import ToolbarSheet from 'paraview-glance/src/components/core/ToolbarSheet';
1411
import { BACKGROUND } from 'paraview-glance/src/components/core/VtkView/palette';
1512

13+
import { updateViewOrientationFromBasisAndAxis } from 'paraview-glance/src/utils';
14+
1615
const ROTATION_STEP = 2;
1716

1817
// ----------------------------------------------------------------------------
@@ -69,6 +68,15 @@ export default {
6968
axisPreset(state) {
7069
return state.axisPreset;
7170
},
71+
viewOrientation(state) {
72+
return state.viewOrientation;
73+
},
74+
viewTypeItems(state) {
75+
return Object.entries(state.viewTypes).map(([viewType, text]) => ({
76+
text,
77+
value: viewType,
78+
}));
79+
},
7280
}),
7381
...mapGetters(['cameraViewPoints']),
7482
type() {
@@ -80,9 +88,6 @@ export default {
8088
orientationLabels() {
8189
return this.axisPreset === 'lps' ? ['L', 'P', 'S'] : ['X', 'Y', 'Z'];
8290
},
83-
viewTypes() {
84-
return this.axisPreset === 'lps' ? VIEW_TYPES_LPS : VIEW_TYPES;
85-
},
8691
smallScreen() {
8792
return this.$vuetify.breakpoint.width < Breakpoints.md;
8893
},
@@ -250,8 +255,12 @@ export default {
250255
updateOrientation(mode) {
251256
if (this.view && !this.inAnimation) {
252257
this.inAnimation = true;
253-
const { axis, orientation, viewUp } = VIEW_ORIENTATIONS[mode];
254-
this.view.updateOrientation(axis, orientation, viewUp, 100).then(() => {
258+
updateViewOrientationFromBasisAndAxis(
259+
this.view,
260+
this.viewOrientation,
261+
mode,
262+
this.type === 'View3D' ? 100 : 0
263+
).then(() => {
255264
this.inAnimation = false;
256265
});
257266
}

src/components/core/VtkView/template.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@
136136
flat
137137
hide-details
138138
:class="$style.viewTypeSelector"
139-
:items="viewTypes"
139+
:items="viewTypeItems"
140140
:value="viewType"
141141
@change="changeViewType"
142142
/>

src/store/views.js

Lines changed: 112 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,20 @@ import WidgetManagerConstants from '@kitware/vtk.js/Widgets/Core/WidgetManager/C
44

55
import {
66
DEFAULT_VIEW_TYPE,
7-
VIEW_TYPES,
8-
VIEW_ORIENTATIONS,
7+
DEFAULT_AXIS_PRESET,
8+
VIEW_TYPE_VALUES,
9+
DEFAULT_VIEW_TYPES,
10+
DEFAULT_LPS_VIEW_TYPES,
11+
DEFAULT_VIEW_ORIENTATION,
912
} from 'paraview-glance/src/components/core/VtkView/constants';
1013
import { DEFAULT_BACKGROUND } from 'paraview-glance/src/components/core/VtkView/palette';
1114

12-
import { remapIdValues, wrapMutationAsAction } from 'paraview-glance/src/utils';
15+
import {
16+
remapIdValues,
17+
wrapMutationAsAction,
18+
getLPSDirections,
19+
updateViewOrientationFromBasisAndAxis,
20+
} from 'paraview-glance/src/utils';
1321

1422
const { CaptureOn } = WidgetManagerConstants;
1523

@@ -21,7 +29,7 @@ export default ({ proxyManager }) => ({
2129
backgroundColors: {}, // viewType -> bg
2230
globalBackgroundColor: DEFAULT_BACKGROUND,
2331
axisType: 'arrow',
24-
axisPreset: 'default',
32+
axisPreset: DEFAULT_AXIS_PRESET,
2533
axisVisible: true,
2634
annotationOpacity: 1,
2735
interactionStyle3D: '3D',
@@ -30,8 +38,14 @@ export default ({ proxyManager }) => ({
3038
// If null, it will be calculated and set on first use
3139
firstPersonMovementSpeed: null,
3240
maxTextureLODSize: 50000, // Units are in KiB
33-
viewOrder: VIEW_TYPES.map((v) => v.value),
41+
viewOrder: Object.values(VIEW_TYPE_VALUES),
3442
visibleCount: 1,
43+
// a basis in column-major order (list of 3 vectors): number[3][3]
44+
viewOrientation: DEFAULT_VIEW_ORIENTATION,
45+
// for each view type, the corresponding text to display { viewType: text }
46+
viewTypes: DEFAULT_VIEW_TYPES,
47+
masterSourceId: null, // null or string
48+
previousConfigurationPreset: null, // null or string, can only be set to a string
3549
},
3650
mutations: {
3751
setGlobalBackground(state, background) {
@@ -66,6 +80,18 @@ export default ({ proxyManager }) => ({
6680
mapViewTypeToId(state, { viewType, viewId }) {
6781
Vue.set(state.viewTypeToId, viewType, viewId);
6882
},
83+
setViewTypes(state, types) {
84+
state.viewTypes = types;
85+
},
86+
setViewOrientation(state, orientation) {
87+
state.viewOrientation = orientation;
88+
},
89+
setMasterSourceId(state, { sourceId }) {
90+
state.masterSourceId = sourceId;
91+
},
92+
setPreviousConfigurationPreset(state, preset) {
93+
state.previousConfigurationPreset = preset;
94+
},
6995
changeBackground(state, { viewType, color }) {
7096
state.backgroundColors[viewType] = color;
7197
},
@@ -97,8 +123,11 @@ export default ({ proxyManager }) => ({
97123
const view = proxyManager.createProxy('Views', type, { name });
98124

99125
// Update orientation
100-
const { axis, orientation, viewUp } = VIEW_ORIENTATIONS[name];
101-
view.updateOrientation(axis, orientation, viewUp);
126+
updateViewOrientationFromBasisAndAxis(
127+
view,
128+
state.viewOrientation,
129+
name
130+
);
102131

103132
// set background to transparent
104133
view.setBackground(0, 0, 0, 0);
@@ -167,11 +196,86 @@ export default ({ proxyManager }) => ({
167196
});
168197
commit('setAxisType', axisType);
169198
},
170-
setAxisPreset({ commit }, axisPreset) {
199+
setAxisPreset({ commit, dispatch }, axisPreset) {
171200
proxyManager.getViews().forEach((view) => {
172201
view.setPresetToOrientationAxes(axisPreset);
173202
});
174203
commit('setAxisPreset', axisPreset);
204+
dispatch('configureViewOrientationAndTypes');
205+
},
206+
setViewOrientation({ commit, state }, orientation) {
207+
commit('setViewOrientation', orientation);
208+
Object.entries(state.viewTypeToId).forEach(([viewType, viewId]) => {
209+
const view = proxyManager.getProxyById(viewId);
210+
const [type, name] = viewType.split(':');
211+
updateViewOrientationFromBasisAndAxis(
212+
view,
213+
orientation,
214+
name,
215+
type === 'View3D' ? 100 : 0
216+
);
217+
});
218+
},
219+
setViewTypes({ commit }, viewTypes) {
220+
commit('setViewTypes', viewTypes);
221+
},
222+
configureViewOrientationAndTypes({ commit, dispatch, state }) {
223+
if (state.axisPreset === 'lps') {
224+
const masterSource =
225+
state.masterSourceId &&
226+
proxyManager.getProxyById(state.masterSourceId);
227+
if (masterSource?.getDataset().isA('vtkImageData')) {
228+
// lps mode with a master volume
229+
const directionMatrix = masterSource.getDataset().getDirection();
230+
const lpsDirections = getLPSDirections(directionMatrix);
231+
const axisToXYZ = ['x', 'y', 'z'];
232+
const viewTypes = {
233+
[VIEW_TYPE_VALUES.default]: '3D',
234+
[VIEW_TYPE_VALUES[axisToXYZ[lpsDirections.l.axis]]]: 'Sagittal',
235+
[VIEW_TYPE_VALUES[axisToXYZ[lpsDirections.p.axis]]]: 'Coronal',
236+
[VIEW_TYPE_VALUES[axisToXYZ[lpsDirections.s.axis]]]: 'Axial',
237+
};
238+
const viewOrientation = [
239+
lpsDirections.l.vector,
240+
lpsDirections.p.vector,
241+
lpsDirections.s.vector,
242+
];
243+
dispatch('setViewTypes', viewTypes);
244+
dispatch('setViewOrientation', viewOrientation);
245+
} else if (state.previousConfigurationPreset !== 'lps') {
246+
// lps mode but no master volume and previous configuration wasn't lps
247+
dispatch('setViewTypes', DEFAULT_LPS_VIEW_TYPES);
248+
dispatch('setViewOrientation', DEFAULT_VIEW_ORIENTATION);
249+
}
250+
} else {
251+
// Not in lps mode
252+
dispatch('setViewTypes', DEFAULT_VIEW_TYPES);
253+
dispatch('setViewOrientation', DEFAULT_VIEW_ORIENTATION);
254+
}
255+
commit('setPreviousConfigurationPreset', state.axisPreset);
256+
},
257+
updateMasterSourceId({ dispatch, state }, datasets) {
258+
const hiddenDatasets = proxyManager
259+
.getRepresentations()
260+
.filter((r) => !r.isVisible())
261+
.map((r) => r.getInput().getProxyId());
262+
const fullyVisibleDatasets = datasets.filter(
263+
(dataset) => !hiddenDatasets.includes(dataset)
264+
);
265+
266+
if (!fullyVisibleDatasets.includes(state.masterSourceId)) {
267+
if (fullyVisibleDatasets.length === 0) {
268+
dispatch('setMasterSourceId', { sourceId: null });
269+
} else {
270+
dispatch('setMasterSourceId', { sourceId: fullyVisibleDatasets[0] });
271+
}
272+
}
273+
},
274+
setMasterSourceId({ commit, dispatch, state }, { sourceId }) {
275+
commit('setMasterSourceId', { sourceId });
276+
if (state.axisPreset === 'lps') {
277+
dispatch('configureViewOrientationAndTypes');
278+
}
175279
},
176280
setAxisVisible({ commit }, visible) {
177281
proxyManager.getViews().forEach((view) => {

0 commit comments

Comments
 (0)