@@ -33,13 +33,14 @@ import {
3333import {
3434 CommonViewProps ,
3535 useVtkView ,
36- useVtkViewCameraOrientation ,
37- giveViewAnnotations ,
36+ applyViewAnnotations ,
3837} from ' @/src/composables/view/common' ;
3938import {
4039 useOrientationLabels ,
4140 use2DMouseControls ,
4241 usePixelProbe ,
42+ apply2DCameraPlacement ,
43+ useIJKAxisCamera ,
4344} from ' @/src/composables/view/view2D' ;
4445import { useResizeObserver } from ' @/src/composables/resizeObserver' ;
4546import { watchScene , watchColorBy } from ' @/src/composables/scene' ;
@@ -48,24 +49,84 @@ import { useSubscription } from '@/src/composables/vtk';
4849import { useWidgetProvider } from ' @/src/composables/widgetProvider' ;
4950import { useProxyManager } from ' @/src/composables/proxyManager' ;
5051
51- import { resize2DCameraToFit } from ' @/src/vtk/proxyUtils' ;
5252import { DataTypes } from ' @/src/constants' ;
5353
5454import SliceSlider from ' @/src/components/SliceSlider.vue' ;
5555
56+ /**
57+ * Sets parallel scale of 2D view camera to fit a given bounds.
58+ *
59+ * Assumes the camera is reset, i.e. focused correctly.
60+ *
61+ * Bounds is specified as width/height of orthographic view.
62+ * Renders must be triggered manually.
63+ */
64+ function resize2DCameraToFit (view , lookAxis , viewUpAxis , bounds ) {
65+ const camera = view .getCamera ();
66+ const lengths = [
67+ bounds[1 ] - bounds[0 ],
68+ bounds[3 ] - bounds[2 ],
69+ bounds[5 ] - bounds[4 ],
70+ ];
71+ const [w , h ] = view .getOpenglRenderWindow ().getSize ();
72+ let bw;
73+ let bh;
74+ /* eslint-disable prefer-destructuring */
75+ if (lookAxis === 0 && viewUpAxis === 1 ) {
76+ bw = lengths[2 ];
77+ bh = lengths[1 ];
78+ } else if (lookAxis === 0 && viewUpAxis === 2 ) {
79+ bw = lengths[1 ];
80+ bh = lengths[2 ];
81+ } else if (lookAxis === 1 && viewUpAxis === 0 ) {
82+ bw = lengths[2 ];
83+ bh = lengths[0 ];
84+ } else if (lookAxis === 1 && viewUpAxis === 2 ) {
85+ bw = lengths[0 ];
86+ bh = lengths[2 ];
87+ } else if (lookAxis === 2 && viewUpAxis === 0 ) {
88+ bw = lengths[1 ];
89+ bh = lengths[0 ];
90+ } else if (lookAxis === 2 && viewUpAxis === 1 ) {
91+ bw = lengths[0 ];
92+ bh = lengths[1 ];
93+ }
94+ /* eslint-enable prefer-destructuring */
95+ const viewAspect = w / h;
96+ const boundsAspect = bw / bh;
97+
98+ let scale = 0 ;
99+ if (viewAspect >= boundsAspect) {
100+ scale = bh / 2 ;
101+ } else {
102+ scale = bw / 2 / viewAspect;
103+ }
104+
105+ camera .setParallelScale (scale);
106+ }
107+
56108/**
57109 * This differs from view.resetCamera() in that we reset the view
58110 * to the specified bounds.
59111 */
60- function resetCamera (viewRef , boundsWithSpacing , resizeToFit ) {
112+ function resetCamera (viewRef , lookAxis , viewUpAxis , imageParams , resizeToFit ) {
61113 const view = unref (viewRef);
62114 if (view) {
63115 const renderer = view .getRenderer ();
64116 renderer .computeVisiblePropBounds ();
65- renderer .resetCamera (unref (boundsWithSpacing) );
117+ renderer .resetCamera (imageParams . value . bounds );
66118
67119 if (unref (resizeToFit)) {
68- resize2DCameraToFit (view, unref (boundsWithSpacing));
120+ const { extent , spacing } = imageParams .value ;
121+ const extentWithSpacing = extent .map (
122+ (e , i ) => e * spacing[Math .floor (i / 2 )]
123+ );
124+ resize2DCameraToFit (
125+ view,
126+ unref (lookAxis),
127+ unref (viewUpAxis),
128+ unref (extentWithSpacing)
129+ );
69130 }
70131 }
71132}
@@ -80,20 +141,26 @@ export default {
80141 },
81142
82143 setup (props ) {
83- const { viewName , viewType , viewUp , axis , orientation } = toRefs (props);
144+ const { viewName , viewType } = toRefs (props);
84145 const vtkContainer = ref (null );
85146 const resizeToFit = ref (true );
147+
148+ const { axis , orientation , viewUp , viewUpAxis } = useIJKAxisCamera (
149+ viewType
150+ );
86151 const axisLabel = computed (() => ' xyz' [axis .value ]);
87152
88153 const store = useStore ();
89154 const widgetProvider = useWidgetProvider ();
90155 const pxm = useProxyManager ();
91156
157+ // currentSlice: VtkTwoView concerns itself only with IJK coords, so
158+ // currentSlice is expected to be in image coords.
92159 const {
93160 sceneSources ,
94- worldOrientation ,
161+ imageParams ,
95162 colorBy ,
96- boundsWithSpacing ,
163+ extentWithSpacing ,
97164 baseImage ,
98165 currentSlice ,
99166 windowing ,
@@ -105,11 +172,11 @@ export default {
105172 .filter ((id ) => id in pipelines)
106173 .map ((id ) => pipelines[id].last );
107174 },
108- worldOrientation : (state ) => state .visualization .worldOrientation ,
175+ imageParams : (state ) => state .visualization .imageParams ,
109176 colorBy : (state , getters ) =>
110177 getters .sceneObjectIDs .map ((id ) => state .visualization .colorBy [id]),
111- boundsWithSpacing : (_ , getters ) =>
112- getters[' visualization/boundsWithSpacing ' ],
178+ extentWithSpacing : (_ , getters ) =>
179+ getters[' visualization/extentWithSpacing ' ],
113180 baseImage (state ) {
114181 const { pipelines } = state .visualization ;
115182 const { selectedBaseImage } = state;
@@ -137,18 +204,21 @@ export default {
137204 },
138205 });
139206
140- const currentSliceSpacing = computed (
141- () => worldOrientation .value .spacing [axis .value ]
142- );
143-
144207 const viewRef = useVtkView ({
145208 containerRef: vtkContainer,
146209 viewName,
147210 viewType,
148211 });
149212
150213 // configure camera orientation
151- useVtkViewCameraOrientation (viewRef, viewUp, axis, orientation);
214+ apply2DCameraPlacement (
215+ viewRef,
216+ imageParams,
217+ viewUp,
218+ orientation,
219+ axis,
220+ ' image'
221+ );
152222
153223 useResizeObserver (vtkContainer, () => {
154224 const view = unref (viewRef);
@@ -157,17 +227,19 @@ export default {
157227 }
158228 });
159229
160- watchScene (sceneSources, worldOrientation, viewRef);
230+ watchScene (sceneSources, viewRef);
161231 watchColorBy (colorBy, sceneSources, viewRef);
162232
163233 // reset camera conditions
164234 watch (
165- [baseImage, boundsWithSpacing ],
166- () => resetCamera (viewRef, boundsWithSpacing , resizeToFit),
235+ [baseImage, extentWithSpacing ],
236+ () => resetCamera (viewRef, axis, viewUpAxis, imageParams , resizeToFit),
167237 { immediate: true }
168238 );
169239 useSubscription (viewRef, (view ) =>
170- view .onResize (() => resetCamera (viewRef, boundsWithSpacing, resizeToFit))
240+ view .onResize (() =>
241+ resetCamera (viewRef, axis, viewUpAxis, imageParams, resizeToFit)
242+ )
171243 );
172244
173245 // setup view
@@ -193,10 +265,10 @@ export default {
193265 default: windowing .value .level ,
194266 }));
195267 const sliceRange = computed (() => {
196- const { bounds } = unref (worldOrientation );
268+ const { extent } = unref (imageParams );
197269 return {
198- min: bounds [axis .value * 2 ],
199- max: bounds [axis .value * 2 + 1 ],
270+ min: extent [axis .value * 2 ],
271+ max: extent [axis .value * 2 + 1 ],
200272 step: 1 ,
201273 default: currentSlice .value ,
202274 };
@@ -231,8 +303,7 @@ export default {
231303 if (viewRef .value && baseImage .value ) {
232304 const rep = pxm .getRepresentation (baseImage .value , viewRef .value );
233305 if (rep) {
234- if (rep .setSlice )
235- rep .setSlice (currentSlice .value * currentSliceSpacing .value );
306+ if (rep .setSlice ) rep .setSlice (currentSlice .value );
236307 if (rep .setWindowWidth ) rep .setWindowWidth (windowing .value .width );
237308 if (rep .setWindowLevel ) rep .setWindowLevel (windowing .value .level );
238309 }
@@ -243,10 +314,7 @@ export default {
243314 const { pixelProbe } = usePixelProbe (viewRef, baseImage);
244315
245316 // orientation labels
246- const { leftLabel , upLabel } = useOrientationLabels (
247- viewRef,
248- worldOrientation
249- );
317+ const { left: leftLabel , top: upLabel } = useOrientationLabels (viewRef);
250318
251319 // pixel probe annotation
252320 const pixelAnnotation = computed (() => {
@@ -285,7 +353,7 @@ export default {
285353 : ' '
286354 );
287355
288- giveViewAnnotations (
356+ applyViewAnnotations (
289357 viewRef,
290358 reactive ({
291359 n: upLabel,
0 commit comments