# Enabling Slide Change in MPR Viewer via Slider Movement using vtlResliceCursorWidget

**URL:** https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975
**Category:** Web
**Created:** [July 19, 2023, 10:36am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975 "2023-07-19T10:36:57Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [July 19, 2023, 10:36am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/1 "2023-07-19T10:36:57Z")

</div>

I would like to know how to implement a functionality where the MPR viewer updates its displayed slides as the user interacts with a slider. Specifically, I am using the vtlResliceCursorWidget and seeking guidance on how to synchronize the slider with the MPR viewer, ensuring that the selected slide changes correspondingly as the slider is moved. Any insights or code examples regarding this integration would be greatly appreciated. Thank you in advance for your assistance!  
@finetjul @Sebastien_Jourdain @Forrest

---

<div class="post-metadata">

### Author: ![Thibault\_Bruyere](https://discourse.vtk.org/user_avatar/discourse.vtk.org/thibault_bruyere/32/6585_2.png) [@Thibault\_Bruyere](https://discourse.vtk.org/u/Thibault_Bruyere)
#### Post date: [July 19, 2023, 11:11am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/2 "2023-07-19T11:11:33Z")

</div>

The CPR example uses a reslice cursor widget and a slider to control the angle of the reslicing.  
The angle can be changed using the green line too.  
Here is the [live demo](https://kitware.github.io/vtk-js/examples/ImageCPRMapper/index.html).  
The source code is in `Sources\Rendering\Core\ImageCPRMapper\example\index.js`.

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [July 20, 2023, 10:23am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/3 "2023-07-20T10:23:43Z")

</div>

Thank you so much for your response. I am using the example provided at [vtk.js](https://kitware.github.io/vtk-js/examples/ResliceCursorWidget.html) as a reference.

My goal is to display axial, coronal, and sagittal views. I would like to be able to change the slice using the mouse scroll for all MPR scenes and also use the UI scroll bar available on all MPR scenes for this purpose. Additionally, I want to implement a crosshair functionality. Moreover, if I adjust the strength and level on one view, it should affect the other views as well.

I have already managed to change the slice using the mouse scroll, but I am unsure of how to achieve the same functionality using the UI scroll bar.

Could you please help me with implementing the desired features?

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [July 20, 2023, 10:47am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/4 "2023-07-20T10:47:31Z")

</div>

@cory.quammen @pieper @will.schroeder do any of you have a solution for this issue?

---

<div class="post-metadata">

### Author: ![pieper](https://discourse.vtk.org/user_avatar/discourse.vtk.org/pieper/32/17_2.png) [@pieper](https://discourse.vtk.org/u/pieper)
#### Post date: [July 20, 2023, 12:36pm UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/5 "2023-07-20T12:36:46Z")

</div>

I suggest you use [cornerstone3D](https://github.com/cornerstonejs/cornerstone3D) rather than reimplementing this from scratch.

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [July 26, 2023, 8:16am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/6 "2023-07-26T08:16:23Z")

</div>

Thank you for recommending cornerstone3D, which I did consider. However, I have specific requirements that I need to meet, and I believe it would be more beneficial for me to stick with a single framework. After reviewing the cornerstone3D documentation, I noticed a lack of available resources, which concerns me. If I were to encounter any issues while combining vtk.js with cornerstone3D, it might be challenging to find solutions due to the limited community support. As a beginner, I find it difficult to understand how to proceed in this situation. Despite this, I truly appreciate your input! @pieper

---

<div class="post-metadata">

### Author: ![finetjul](https://discourse.vtk.org/user_avatar/discourse.vtk.org/finetjul/32/154_2.png) [@finetjul](https://discourse.vtk.org/u/finetjul)
#### Post date: [August 3, 2023, 4:42pm UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/7 "2023-08-03T16:42:50Z")

</div>

Hi Raja, to scroll within the vtkRelisceCursorWidget, all you have to do is modify the “center” of the widget. Your slider would modify one coordinate of the center (the one of the specific axis).  
e.g. `widget.setCenter(center[0], center[1] + step, center[2])` if you scroll along the Y axis

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [August 3, 2023, 4:51pm UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/8 "2023-08-03T16:51:37Z")

</div>

@finetjul, I truly appreciate your response. Could you kindly provide additional context for better understanding?

---

<div class="post-metadata">

### Author: ![finetjul](https://discourse.vtk.org/user_avatar/discourse.vtk.org/finetjul/32/154_2.png) [@finetjul](https://discourse.vtk.org/u/finetjul)
#### Post date: [August 3, 2023, 5:05pm UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/9 "2023-08-03T17:05:42Z")

</div>

```auto
resliceCursorWidget = vtlResliceCursorWidget.newInstance(); // see ResliceCursorWidget example for more context
...
bounds = widget.getWidgetState().getImage().getBounds();
const ySlider= document.getElementById('ySlider');
ySlider.min = bounds[2];
ySlider.max =bounds[3];
ySlider.addEventListener('change', (ev) => {
  center = resliceCursorWidget.getWidgetState().getCenter();
  resliceCursorWidget.setCenter(center[0], ev.target.value, center[1]);
}

```

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [August 3, 2023, 5:54pm UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/10 "2023-08-03T17:54:10Z")

</div>

```auto
const bounds = resliceCursorWidget.getWidgetState().getImage().getBounds();
const ySlider = document.getElementById('slicingScale');
ySlider.min = bounds[2];
ySlider.max = bounds[3];
ySlider.addEventListener('input', (ev) => {
    let center = resliceCursorWidget.getWidgetState().getCenter();
    resliceCursorWidget.setCenter([center[0], ev.target.value, center[2]]);
});

```

Based on your suggestion, I have implemented the code you provided. However, while adjusting the slider, the crosshair’s position changes horizontally in the sagittal view. The image slice remains unchanged.  
@finetjul

---

<div class="post-metadata">

### Author: ![finetjul](https://discourse.vtk.org/user_avatar/discourse.vtk.org/finetjul/32/154_2.png) [@finetjul](https://discourse.vtk.org/u/finetjul)
#### Post date: [August 4, 2023, 5:37am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/11 "2023-08-04T05:37:37Z")

</div>

Interaction events are not triggered when changing the center programmatically.  
You might have to call `updateViews()` (from the example) manually…

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [August 4, 2023, 11:38am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/12 "2023-08-04T11:38:48Z")

</div>

[/uploads/default/original/2X/1/1a923e00cb1cd58d4c9f1415e0bc9894291bd4b6.mp4](https://discourse.vtk.org/uploads/default/original/2X/1/1a923e00cb1cd58d4c9f1415e0bc9894291bd4b6.mp4)
  
I want to change the slices, but here crosshair is moving

---

<div class="post-metadata">

### Author: ![finetjul](https://discourse.vtk.org/user_avatar/discourse.vtk.org/finetjul/32/154_2.png) [@finetjul](https://discourse.vtk.org/u/finetjul)
#### Post date: [August 4, 2023, 11:51am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/13 "2023-08-04T11:51:12Z")

</div>

please share your code…

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [August 4, 2023, 11:55am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/14 "2023-08-04T11:55:41Z")

</div>

```auto
import '@kitware/vtk.js/favicon';
import '@kitware/vtk.js/Rendering/Profiles/All';

import vtkGenericRenderWindow from '@kitware/vtk.js/Rendering/Misc/GenericRenderWindow';
import vtkResourceLoader from '@kitware/vtk.js/IO/Core/ResourceLoader';
import vtkITKHelper from '@kitware/vtk.js/Common/DataModel/ITKHelper';

import vtkInteractorStyleImage from '@kitware/vtk.js/Interaction/Style/InteractorStyleImage';
import vtkImageMapper from '@kitware/vtk.js/Rendering/Core/ImageMapper';
import vtkImageSlice from '@kitware/vtk.js/Rendering/Core/ImageSlice';
import ImageConstants from '@kitware/vtk.js/Rendering/Core/ImageMapper/Constants';
import vtkWidgetManager from '@kitware/vtk.js/Widgets/Core/WidgetManager';
import { xyzToViewType } from '@kitware/vtk.js/Widgets/Widgets3D/ResliceCursorWidget/Constants';
import vtkImageReslice from '@kitware/vtk.js/Imaging/Core/ImageReslice';
import { SlabMode } from '@kitware/vtk.js/Imaging/Core/ImageReslice/Constants';
import vtkResliceCursorWidget from '@kitware/vtk.js/Widgets/Widgets3D/ResliceCursorWidget';
import vtkSphereSource from '@kitware/vtk.js/Filters/Sources/SphereSource';
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import { CaptureOn } from '@kitware/vtk.js/Widgets/Core/WidgetManager/Constants';

import vtkHttpDataSetReader from '@kitware/vtk.js/IO/Core/HttpDataSetReader';
import httpDataAccessHelper from '@kitware/vtk.js/IO/Core/DataAccessHelper/HttpDataAccessHelper';

const viewEleX = document.querySelector('#xView');
const viewEleY = document.querySelector('#yView');
const viewEleZ = document.querySelector('#zView');
const mprContainer = [viewEleX, viewEleY, viewEleZ];

const viewAttributes = [];

const resliceCursorWidget = vtkResliceCursorWidget.newInstance();
const widgetState = resliceCursorWidget.getWidgetState();

widgetState
    .getStatesWithLabel('rotation')
    .forEach((handle) => {
        handle.setVisible(false);
    });

for (let i = 0; i < 3; i++) {
    const obj = {};

    const genericRenderWindow = vtkGenericRenderWindow.newInstance();
    genericRenderWindow.setContainer(mprContainer[i]);
    genericRenderWindow.getOpenGLRenderWindow().setSize(mprContainer[i].clientWidth, mprContainer[i].clientHeight);
    genericRenderWindow.resize();
    obj.genericRenderWindow = genericRenderWindow;

    const renderer = genericRenderWindow.getRenderer();
    renderer.getActiveCamera().setParallelProjection(true);
    obj.renderer = renderer;

    const renderWindow = genericRenderWindow.getRenderWindow();
    const interactor = vtkInteractorStyleImage.newInstance();
    renderWindow.getInteractor().setInteractorStyle(interactor);
    obj.renderWindow = renderWindow;

    const widgetManager = vtkWidgetManager.newInstance();
    widgetManager.setRenderer(renderer);
    widgetManager.enablePicking();
    widgetManager.setCaptureOn(CaptureOn.MOUSE_MOVE);
    obj.widgetManager = widgetManager;

    const widgetInstance = widgetManager.addWidget(resliceCursorWidget, xyzToViewType[i]);
    widgetInstance.setScaleInPixels(false);
    widgetInstance.setKeepOrthogonality(true);
    obj.widgetInstance = widgetInstance;

    const reslice = vtkImageReslice.newInstance();
    reslice.setSlabNumberOfSlices(1);
    reslice.setTransformInputSampling(false);
    reslice.setAutoCropOutput(true);
    reslice.setOutputDimensionality(2);
    obj.reslice = reslice;

    const resliceMapper = vtkImageMapper.newInstance();
    resliceMapper.setInputConnection(reslice.getOutputPort());
    obj.resliceMapper = resliceMapper;

    const resliceActor = vtkImageSlice.newInstance();
    resliceActor.setMapper(resliceMapper);
    obj.resliceActor = resliceActor;

    viewAttributes.push(obj);
}

function updateReslice(
    interactionContext = {
        viewType: '',
        reslice: null,
        actor: null,
        renderer: null,
        resetFocalPoint: false, 
        keepFocalPointPosition: false, 
        computeFocalPointOffset: false, 
        spheres: null,
    }
) {
    const modified = resliceCursorWidget.updateReslicePlane(
        interactionContext.reslice,
        interactionContext.viewType
    );

    if (modified) {
        const resliceAxes = interactionContext.reslice.getResliceAxes();
        interactionContext.actor.setUserMatrix(resliceAxes);
    }
    resliceCursorWidget.updateCameraPoints(
        interactionContext.renderer,
        interactionContext.viewType,
        interactionContext.resetFocalPoint,
        interactionContext.keepFocalPointPosition,
        interactionContext.computeFocalPointOffset
    );

    return modified;
}

function setupResliceViews(vtkImage) {
    resliceCursorWidget.setImage(vtkImage);

    const bounds = resliceCursorWidget.getWidgetState().getImage().getBounds();
    const ySlider = document.getElementById('slicingScale');
    ySlider.min = bounds[2];
    ySlider.max = bounds[3];
    ySlider.addEventListener('input', (ev) => {
        let center = resliceCursorWidget.getWidgetState().getCenter();
        resliceCursorWidget.setCenter([center[0], ev.target.value, center[2]]);
        updateReslice({
            viewType: xyzToViewType[0],
            reslice: viewAttributes[0].reslice,
            actor: viewAttributes[0].resliceActor,
            renderer: viewAttributes[0].renderer,
            resetFocalPoint: true, 
            keepFocalPointPosition: false,
            computeFocalPointOffset: true,
        });
        viewAttributes[0].renderWindow.render();
    });

    for (let i = 0; i < 3; i++) {
        const obj = viewAttributes[i];

        obj.reslice.setInputData(vtkImage);
        obj.renderer.addActor(obj.resliceActor);
        const viewType = xyzToViewType[i];

        obj.widgetInstance.onInteractionEvent(
            ({ computeFocalPointOffset, canUpdateFocalPoint }) => {
                const activeViewType = resliceCursorWidget.getWidgetState().getActiveViewType();
                const keepFocalPointPosition = activeViewType !== viewType && canUpdateFocalPoint;
                updateReslice({
                    viewType: viewType,
                    reslice: obj.reslice,
                    actor: obj.resliceActor,
                    renderer: obj.renderer,
                    resetFocalPoint: false,
                    keepFocalPointPosition,
                    computeFocalPointOffset,
                });
            });

        updateReslice({
            viewType: viewType,
            reslice: obj.reslice,
            actor: obj.resliceActor,
            renderer: obj.renderer,
            resetFocalPoint: true, 
            keepFocalPointPosition: false,
            computeFocalPointOffset: true,
        });
        obj.renderWindow.getInteractor().render();
    }
}

const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true });
reader
    .setUrl('https://kitware.github.io/vtk-js/data/volume/LIDC2.vti')
    .then(() => reader.loadData())
    .then(() => {
        const image = reader.getOutputData();
        setupResliceViews(image);
    });

```

---

<div class="post-metadata">

### Author: ![finetjul](https://discourse.vtk.org/user_avatar/discourse.vtk.org/finetjul/32/154_2.png) [@finetjul](https://discourse.vtk.org/u/finetjul)
#### Post date: [August 4, 2023, 12:18pm UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/15 "2023-08-04T12:18:48Z")

</div>

In ySlider.addEventListener(‘input’, (ev) =\> {

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [August 4, 2023, 12:24pm UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/16 "2023-08-04T12:24:01Z")

</div>

I have already added it inside `setupResliceViews` function in my code.

---

<div class="post-metadata">

### Author: ![finetjul](https://discourse.vtk.org/user_avatar/discourse.vtk.org/finetjul/32/154_2.png) [@finetjul](https://discourse.vtk.org/u/finetjul)
#### Post date: [August 4, 2023, 12:30pm UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/17 "2023-08-04T12:30:56Z")

</div>

My email got truncated, let me re-post it here properly:  
In `ySlider.addEventListener('input', (ev) => {`, you should call `updateView()` for the Y (i.e.1) view and not the X (i.e. 0) view.

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [August 4, 2023, 12:43pm UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/18 "2023-08-04T12:43:41Z")

</div>

It’s working. Thank you so much. However, I’m encountering another issue. Let me address it.  
Here is the revised code for changing the image slice using the slider.

```auto
const bounds = resliceCursorWidget.getWidgetState().getImage().getBounds();
const ySlider = document.getElementById('slicingScale');
ySlider.min = bounds[2];
ySlider.max = bounds[3];
ySlider.addEventListener('input', (ev) => {
	let center = resliceCursorWidget.getWidgetState().getCenter();
	resliceCursorWidget.setCenter([center[0], ev.target.value, center[2]]);
	updateReslice({
		viewType: xyzToViewType[1],
		reslice: viewAttributes[1].reslice,
		actor: viewAttributes[1].resliceActor,
		renderer: viewAttributes[1].renderer,
		resetFocalPoint: true,
		keepFocalPointPosition: false, 
		computeFocalPointOffset: true,
	});
	viewAttributes[1].renderWindow.render();
});

```

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [August 14, 2023, 10:48am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/19 "2023-08-14T10:48:16Z")

</div>

Hi @finetjul, while using the above code, when I change the image slice using the slider and then scroll, the images in all three viewports disappear.

[/uploads/default/original/2X/c/cdb81a5acfcf3701d09ffa59e9d5950da90b48b3.mp4](https://discourse.vtk.org/uploads/default/original/2X/c/cdb81a5acfcf3701d09ffa59e9d5950da90b48b3.mp4)

---

<div class="post-metadata">

### Author: ![rajajms](https://discourse.vtk.org/user_avatar/discourse.vtk.org/rajajms/32/7476_2.png) [@rajajms](https://discourse.vtk.org/u/rajajms)
#### Post date: [August 14, 2023, 11:00am UTC](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975/20 "2023-08-14T11:00:15Z")

</div>

![image](https://discourse.vtk.org/uploads/default/original/2X/6/6c19402946ad6cdbdd382ddfdb3f29760c093fae.png)  
 ![image](https://discourse.vtk.org/uploads/default/original/2X/d/d51c2f3e5aa7aa8497a52477c7eb61f4aa99ce67.png)

I am printing the value of getCenter when scrolling, and I’ve noticed something unusual in the displayed output. Please refer to the image. @finetjul

[Next page](https://discourse.vtk.org/t/enabling-slide-change-in-mpr-viewer-via-slider-movement-using-vtlreslicecursorwidget/11975.md?page=2)
