diff --git a/Sources/Interaction/Widgets/ImageCroppingRegionsRepresentation/index.js b/Sources/Interaction/Widgets/ImageCroppingRegionsRepresentation/index.js index bd93d2e0a32..fe07489bd6c 100644 --- a/Sources/Interaction/Widgets/ImageCroppingRegionsRepresentation/index.js +++ b/Sources/Interaction/Widgets/ImageCroppingRegionsRepresentation/index.js @@ -1,6 +1,5 @@ import macro from 'vtk.js/Sources/macro'; import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; -import vtkCellPicker from 'vtk.js/Sources/Rendering/Core/CellPicker'; import vtkWidgetRepresentation from 'vtk.js/Sources/Interaction/Widgets/WidgetRepresentation'; import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData'; @@ -15,93 +14,45 @@ const { Orientation } = WidgetConstants; // vtkImageCroppingRegionsRepresentation methods // ---------------------------------------------------------------------------- +// Reorders a bounds array such that each (a,b) pairing is a +// (min,max) pairing. +function reorderBounds(bounds) { + for (let i = 0; i < 6; i += 2) { + if (bounds[i] > bounds[i + 1]) { + const tmp = bounds[i + 1]; + bounds[i + 1] = bounds[i]; + bounds[i] = tmp; + } + } +} + function vtkImageCroppingRegionsRepresentation(publicAPI, model) { // Set our className model.classHierarchy.push('vtkImageCroppingRegionsRepresentation'); - // this is called at the end of the enclosing function. - function constructor() { - // set fields from parent classes - model.placeFactor = 1; - - model.initialBounds = [0, 1, 0, 1, 0, 1]; - // Format: - // [xmin, xmax, ymin, ymax, zmin, zmax] - model.planePositions = [0, 0, 0, 0, 0, 0]; - model.sliceOrientation = Orientation.XY; - model.slice = 0; - - // construct region polydata - model.regionPolyData = vtkPolyData.newInstance(); - model.regionPolyData.getPoints().setData(new Float32Array(16 * 3), 3); - - // This is a separate vtkPolyData for the center region - model.centerRegionPolyData = vtkPolyData.newInstance(); - model.centerRegionPolyData.getPoints().setData(new Float32Array(4 * 3), 3); - - /* - 15-14-----11-10 - | | - 12 13-----8 9 - | | | | - 3 2------7 6 - | | - 0--1------4--5 - */ - // set polys - - // prettier-ignore - model.regionPolyData - .getPolys() - .setData( - new Uint32Array([ - 4, 0, 1, 2, 3, - 4, 1, 4, 7, 2, - 4, 4, 5, 6, 7, - 4, 7, 6, 9, 8, - 4, 8, 9, 10, 11, - 4, 13, 8, 11, 14, - 4, 12, 13, 14, 15, - 4, 3, 2, 13, 12, - ]), - 1, - ); - model.centerRegionPolyData - .getPolys() - .setData(new Uint32Array([4, 0, 1, 2, 3]), 1); - - model.mapper = vtkMapper.newInstance(); - model.actor = vtkActor.newInstance(); - model.centerMapper = vtkMapper.newInstance(); - model.centerActor = vtkActor.newInstance(); - - model.mapper.setInputData(model.regionPolyData); - model.actor.setMapper(model.mapper); - model.actor.getProperty().setEdgeVisibility(true); - publicAPI.setEdgeColor(...model.edgeColor); - publicAPI.setOpacity(model.opacity); - - model.centerMapper.setInputData(model.centerRegionPolyData); - model.centerActor.setMapper(model.centerMapper); - - // set picker - model.cursorPicker = vtkCellPicker.newInstance(); - } + // set fields from parent classes + model.placeFactor = 1; - publicAPI.getActors = () => [model.actor, model.centerActor]; - publicAPI.getNestedProps = () => publicAPI.getActors(); + model.initialBounds = [0, 1, 0, 1, 0, 1]; + // Format: + // [xmin, xmax, ymin, ymax, zmin, zmax] + model.planePositions = [0, 0, 0, 0, 0, 0]; + model.sliceOrientation = Orientation.XY; + model.slice = 0; - // Reorders a bounds array such that each (a,b) pairing is a - // (min,max) pairing. - function reorderBounds(bounds) { - for (let i = 0; i < 6; i += 2) { - if (bounds[i] > bounds[i + 1]) { - const tmp = bounds[i + 1]; - bounds[i + 1] = bounds[i]; - bounds[i] = tmp; - } - } - } + model.regionPolyData = vtkPolyData.newInstance(); + model.regionPolyData.getPoints().setData(new Float32Array(16 * 3), 3); + + // This is a separate vtkPolyData for the center region + model.centerRegionPolyData = vtkPolyData.newInstance(); + model.centerRegionPolyData.getPoints().setData(new Float32Array(4 * 3), 3); + + model.mapper = vtkMapper.newInstance(); + model.actor = vtkActor.newInstance(); + model.centerMapper = vtkMapper.newInstance(); + model.centerActor = vtkActor.newInstance(); + + // methods function updateCenterOpacity() { const slicePos = model.slice; @@ -134,6 +85,9 @@ function vtkImageCroppingRegionsRepresentation(publicAPI, model) { .setOpacity(centerInvisible ? 0 : model.opacity); } + publicAPI.getActors = () => [model.actor, model.centerActor]; + publicAPI.getNestedProps = () => publicAPI.getActors(); + publicAPI.placeWidget = (...bounds) => { const boundsArray = []; @@ -232,6 +186,7 @@ function vtkImageCroppingRegionsRepresentation(publicAPI, model) { publicAPI.updateGeometry(); }; + // Force update the geometry publicAPI.updateGeometry = () => { const slicePos = model.slice; const verts = model.regionPolyData.getPoints().getData(); @@ -340,6 +295,48 @@ function vtkImageCroppingRegionsRepresentation(publicAPI, model) { model.validPick = 1; model.placed = 1; } + + /* + 15-14-----11-10 + | | + 12 13-----8 9 + | | | | + 3 2------7 6 + | | + 0--1------4--5 + */ + // set polys + + // prettier-ignore + model.regionPolyData + .getPolys() + .setData( + new Uint32Array([ + 4, 0, 1, 2, 3, + 4, 1, 4, 7, 2, + 4, 4, 5, 6, 7, + 4, 7, 6, 9, 8, + 4, 8, 9, 10, 11, + 4, 13, 8, 11, 14, + 4, 12, 13, 14, 15, + 4, 3, 2, 13, 12, + ]), + 1, + ); + model.centerRegionPolyData + .getPolys() + .setData(new Uint32Array([4, 0, 1, 2, 3]), 1); + + model.mapper.setInputData(model.regionPolyData); + model.actor.setMapper(model.mapper); + model.actor.getProperty().setEdgeVisibility(true); + + model.centerMapper.setInputData(model.centerRegionPolyData); + model.centerActor.setMapper(model.centerMapper); + + publicAPI.setEdgeColor(...model.edgeColor); + publicAPI.setOpacity(model.opacity); + publicAPI.modified(); } }; @@ -347,9 +344,6 @@ function vtkImageCroppingRegionsRepresentation(publicAPI, model) { publicAPI.setProperty = (property) => { model.actor.setProperty(property); }; - - // invoke the constructor after setting up public methods - constructor(); } // ---------------------------------------------------------------------------- diff --git a/Sources/Interaction/Widgets/ImageCroppingRegionsWidget/index.js b/Sources/Interaction/Widgets/ImageCroppingRegionsWidget/index.js index 84f52dceaa1..59973ee3673 100644 --- a/Sources/Interaction/Widgets/ImageCroppingRegionsWidget/index.js +++ b/Sources/Interaction/Widgets/ImageCroppingRegionsWidget/index.js @@ -20,6 +20,29 @@ const events = [ // vtkImageCroppingRegionsWidget methods // ---------------------------------------------------------------------------- +// Returns cursor name based on widget state +function getCursorState(state) { + switch (state) { + case WidgetState.MOVE_LEFT: + case WidgetState.MOVE_RIGHT: + return 'ew-resize'; + + case WidgetState.MOVE_BOTTOM: + case WidgetState.MOVE_TOP: + return 'ns-resize'; + + case WidgetState.MOVE_LEFT_BOTTOM: + case WidgetState.MOVE_LEFT_TOP: + case WidgetState.MOVE_RIGHT_BOTTOM: + case WidgetState.MOVE_RIGHT_TOP: + return 'all-scroll'; + + case WidgetState.IDLE: + default: + return 'default'; + } +} + function vtkImageCroppingRegionsWidget(publicAPI, model) { // Set our className model.classHierarchy.push('vtkImageCroppingRegionsWidget'); @@ -28,45 +51,6 @@ function vtkImageCroppingRegionsWidget(publicAPI, model) { let widgetState = WidgetState.IDLE; let isCropMoving = false; - // sets cursor based on widget state - function setCursor(state) { - switch (state) { - case WidgetState.MOVE_LEFT: - case WidgetState.MOVE_RIGHT: - model.interactor.getView().setCursor('ew-resize'); - break; - - case WidgetState.MOVE_BOTTOM: - case WidgetState.MOVE_TOP: - model.interactor.getView().setCursor('ns-resize'); - break; - - case WidgetState.MOVE_LEFT_BOTTOM: - case WidgetState.MOVE_LEFT_TOP: - case WidgetState.MOVE_RIGHT_BOTTOM: - case WidgetState.MOVE_RIGHT_TOP: - model.interactor.getView().setCursor('all-scroll'); - break; - - case WidgetState.IDLE: - default: - model.interactor.getView().setCursor('default'); - } - } - - function updateWidget() { - const data = model.volumeMapper.getInputData(); - const origin = data.getOrigin(); - const spacing = data.getSpacing(); - const slice = - origin[model.sliceOrientation] + - spacing[model.sliceOrientation] * model.slice; - - model.widgetRep.setSliceOrientation(model.sliceOrientation); - // set the widget slice + 1 to prevent z fighting - model.widgetRep.setSlice(slice + 1); - } - // Overriden method publicAPI.createDefaultRepresentation = () => { if (!model.widgetRep) { @@ -115,12 +99,16 @@ function vtkImageCroppingRegionsWidget(publicAPI, model) { if (i) { publicAPI.listenEvents(); } + + publicAPI.modified(); }; publicAPI.setVolumeMapper = (volumeMapper) => { - model.volumeMapper = volumeMapper; - if (model.enabled) { - publicAPI.updateRepresentation(); + if (volumeMapper !== model.volumeMapper) { + model.volumeMapper = volumeMapper; + if (model.enabled) { + publicAPI.updateRepresentation(); + } } }; @@ -137,20 +125,40 @@ function vtkImageCroppingRegionsWidget(publicAPI, model) { const bounds = model.volumeMapper.getBounds(); model.widgetRep.placeWidget(...bounds); - updateWidget(); + publicAPI.updateWidget(); + }; + + // Force a widget update + publicAPI.updateWidget = () => { + const data = model.volumeMapper.getInputData(); + const origin = data.getOrigin(); + const spacing = data.getSpacing(); + const slice = + origin[model.sliceOrientation] + + spacing[model.sliceOrientation] * model.slice; + + model.widgetRep.setSliceOrientation(model.sliceOrientation); + // set the widget representation slice + 1 to prevent z fighting + model.widgetRep.setSlice(slice + 1); + + publicAPI.modified(); }; publicAPI.setSlice = (slice) => { - model.slice = slice; - if (model.enabled) { - updateWidget(); + if (slice !== model.slice) { + model.slice = slice; + if (model.enabled) { + publicAPI.updateWidget(); + } } }; publicAPI.setSliceOrientation = (sliceOrientation) => { - model.sliceOrientation = sliceOrientation; - if (model.enabled) { - updateWidget(); + if (sliceOrientation !== model.sliceOrientation) { + model.sliceOrientation = sliceOrientation; + if (model.enabled) { + publicAPI.updateWidget(); + } } }; @@ -332,7 +340,7 @@ function vtkImageCroppingRegionsWidget(publicAPI, model) { widgetState = WidgetState.IDLE; } - setCursor(widgetState); + model.interactor.getView().setCursor(getCursorState(widgetState)); return VOID; };