Skip to content

Commit

Permalink
feat(reslicecursor): expose a convenient getPlaneExtremities function
Browse files Browse the repository at this point in the history
This is useful to control the center using sliders.
  • Loading branch information
finetjul committed Jan 6, 2024
1 parent 3cb0efd commit 6fa6d80
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 10 deletions.
68 changes: 64 additions & 4 deletions Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import vtkImageReslice from '@kitware/vtk.js/Imaging/Core/ImageReslice';
import vtkImageSlice from '@kitware/vtk.js/Rendering/Core/ImageSlice';
import vtkInteractorStyleImage from '@kitware/vtk.js/Interaction/Style/InteractorStyleImage';
import vtkInteractorStyleTrackballCamera from '@kitware/vtk.js/Interaction/Style/InteractorStyleTrackballCamera';
import vtkMath from '@kitware/vtk.js/Common/Core/Math';
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
import vtkOutlineFilter from '@kitware/vtk.js/Filters/General/OutlineFilter';
import vtkOrientationMarkerWidget from '@kitware/vtk.js/Interaction/Widgets/OrientationMarkerWidget';
Expand Down Expand Up @@ -122,12 +123,19 @@ const initialPlanesState = { ...widgetState.getPlanes() };
let view3D = null;

for (let i = 0; i < 4; i++) {
const elementParent = document.createElement('div');
elementParent.setAttribute('class', 'view');
elementParent.style.width = '50%';
elementParent.style.height = '300px';
elementParent.style.display = 'inline-block';

const element = document.createElement('div');
element.setAttribute('class', 'view');
element.style.width = '50%';
element.style.height = '300px';
element.style.display = 'inline-block';
container.appendChild(element);
element.style.width = '100%';
element.style.height = '100%';
elementParent.appendChild(element);

container.appendChild(elementParent);

const grw = vtkGenericRenderWindow.newInstance();
grw.setContainer(element);
Expand Down Expand Up @@ -252,6 +260,37 @@ for (let i = 0; i < 4; i++) {
obj.orientationWidget.setViewportSize(0.15);
obj.orientationWidget.setMinPixelSize(100);
obj.orientationWidget.setMaxPixelSize(300);

// create sliders
if (i < 3) {
const slider = document.createElement('input');
slider.type = 'range';
slider.min = 0;
slider.max = 200;
slider.style.bottom = '0px';
slider.style.width = '100%';
elementParent.appendChild(slider);
obj.slider = slider;

slider.addEventListener('change', (ev) => {
const newDistanceToP1 = ev.target.value;
const dirProj = widget.getWidgetState().getPlanes()[
xyzToViewType[i]
].normal;
const planeExtremities = widget.getPlaneExtremities(xyzToViewType[i]);
const newCenter = vtkMath.multiplyAccumulate(
planeExtremities[0],
dirProj,
Number(newDistanceToP1),
[]
);
widget.setCenter(newCenter);
obj.widgetInstance.invokeInternalInteractionEvent();
viewAttributes.forEach((obj2) => {
obj2.interactor.render();
});
});
}
}

// ----------------------------------------------------------------------------
Expand All @@ -269,6 +308,7 @@ function updateReslice(
computeFocalPointOffset: false, // Defines if the display offset between reslice center and focal point has to be
// computed. If so, then this offset will be used to keep the focal point position during rotation.
spheres: null,
slider: null,
}
) {
const modified = widget.updateReslicePlane(
Expand All @@ -283,6 +323,24 @@ function updateReslice(
interactionContext.sphereSources[0].setCenter(planeSource.getOrigin());
interactionContext.sphereSources[1].setCenter(planeSource.getPoint1());
interactionContext.sphereSources[2].setCenter(planeSource.getPoint2());

if (interactionContext.slider) {
const planeExtremities = widget.getPlaneExtremities(
interactionContext.viewType
);
const length = Math.sqrt(
vtkMath.distance2BetweenPoints(planeExtremities[0], planeExtremities[1])
);
const dist = Math.sqrt(
vtkMath.distance2BetweenPoints(
planeExtremities[0],
widgetState.getCenter()
)
);
interactionContext.slider.min = 0;
interactionContext.slider.max = length;
interactionContext.slider.value = dist;
}
}
widget.updateCameraPoints(
interactionContext.renderer,
Expand Down Expand Up @@ -349,6 +407,7 @@ reader.setUrl(`${__BASE_PATH__}/data/volume/LIDC2.vti`).then(() => {
keepFocalPointPosition,
computeFocalPointOffset,
sphereSources: obj.sphereSources,
slider: obj.slider,
});
}
);
Expand All @@ -363,6 +422,7 @@ reader.setUrl(`${__BASE_PATH__}/data/volume/LIDC2.vti`).then(() => {
keepFocalPointPosition: false, // Don't update the focal point as we already set it to the center of the image
computeFocalPointOffset: true, // Allow to compute the current offset between display reslice center and display focal point
sphereSources: obj.sphereSources,
slider: obj.slider,
});
obj.interactor.render();
});
Expand Down
8 changes: 8 additions & 0 deletions Sources/Widgets/Widgets3D/ResliceCursorWidget/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ export interface vtkResliceCursorWidget extends vtkAbstractWidgetFactory {

getManipulator(): vtkPlaneManipulator;

/**
* Return an array of the first and the last possible points of the plane
* along its normal.
* @param {ViewTypes} viewType
* @returns {[Vector3, Vector3]} first and last points
*/
getPlaneExtremities(viewType: ViewTypes): Array<Vector3>;

}

export interface IResliceCursorWidgetInitialValues {}
Expand Down
31 changes: 25 additions & 6 deletions Sources/Widgets/Widgets3D/ResliceCursorWidget/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import macro from 'vtk.js/Sources/macros';
import vtkAbstractWidgetFactory from 'vtk.js/Sources/Widgets/Core/AbstractWidgetFactory';
import vtkBox from '@kitware/vtk.js/Common/DataModel/Box';
import vtkPlane from 'vtk.js/Sources/Common/DataModel/Plane';
import vtkPlaneSource from 'vtk.js/Sources/Filters/Sources/PlaneSource';
import vtkPlaneManipulator from 'vtk.js/Sources/Widgets/Manipulators/PlaneManipulator';
Expand Down Expand Up @@ -319,12 +320,7 @@ function vtkResliceCursorWidget(publicAPI, model) {
publicAPI.setImage = (image) => {
model.widgetState.setImage(image);
const center = image.getCenter();
model.widgetState.setCenter(center);
updateState(
model.widgetState,
model.scaleInPixels,
model.rotationHandlePosition
);
publicAPI.setCenter(center);
};

publicAPI.setCenter = (center) => {
Expand Down Expand Up @@ -647,6 +643,29 @@ function vtkResliceCursorWidget(publicAPI, model) {
);
}
);

publicAPI.getPlaneExtremities = (viewType) => {
const dirProj = publicAPI.getWidgetState().getPlanes()[viewType].normal;
const p1 = vtkMath.multiplyAccumulate(
publicAPI.getWidgetState().getCenter(),
dirProj,
-10000,
[]
);
const p2 = vtkMath.multiplyAccumulate(
publicAPI.getWidgetState().getCenter(),
dirProj,
10000,
[]
);
// FIXME: support oriented bounds
const intersectionPoints = vtkBox.intersectWithLine(
publicAPI.getWidgetState().getImage().getBounds(),
p1,
p2
);
return [intersectionPoints.x1, intersectionPoints.x2];
};
}

// ----------------------------------------------------------------------------
Expand Down

0 comments on commit 6fa6d80

Please sign in to comment.