diff --git a/Sources/Common/Core/ImageHelper/index.js b/Sources/Common/Core/ImageHelper/index.js index 3d7898b5509..6394e59372b 100644 --- a/Sources/Common/Core/ImageHelper/index.js +++ b/Sources/Common/Core/ImageHelper/index.js @@ -58,7 +58,7 @@ function imageToImageData( ctx.translate(canvas.width / 2, canvas.height / 2); ctx.scale(flipX ? -1 : 1, flipY ? -1 : 1); ctx.rotate((rotate * Math.PI) / 180); - ctx.drawImage(image, -image.width / 2, -image.width / 2); + ctx.drawImage(image, -image.width / 2, -image.height / 2); return canvasToImageData(canvas); } diff --git a/Sources/IO/XML/XMLPolyDataReader/index.js b/Sources/IO/XML/XMLPolyDataReader/index.js index 1147adb85dc..0b232139c27 100644 --- a/Sources/IO/XML/XMLPolyDataReader/index.js +++ b/Sources/IO/XML/XMLPolyDataReader/index.js @@ -1,5 +1,4 @@ import macro from 'vtk.js/Sources/macro'; -import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData'; import vtkXMLReader from 'vtk.js/Sources/IO/XML/XMLReader'; @@ -60,28 +59,6 @@ function handleCells( return size; } -// ---------------------------------------------------------------------------- - -function handleFieldDataArray( - dataArrayElem, - compressor, - byteOrder, - headerType, - binaryBuffer -) { - const size = Number(dataArrayElem.getAttribute('NumberOfTuples')); - return vtkDataArray.newInstance( - vtkXMLReader.processDataArray( - size, - dataArrayElem, - compressor, - byteOrder, - headerType, - binaryBuffer - ) - ); -} - // ---------------------------------------------------------------------------- // vtkXMLPolyDataReader methods // ---------------------------------------------------------------------------- @@ -92,26 +69,9 @@ function vtkXMLPolyDataReader(publicAPI, model) { publicAPI.parseXML = (rootElem, type, compressor, byteOrder, headerType) => { const datasetElem = rootElem.getElementsByTagName(model.dataType)[0]; - const fieldDataElem = datasetElem.getElementsByTagName('FieldData')[0]; const pieces = datasetElem.getElementsByTagName('Piece'); const nbPieces = pieces.length; - // field data - let fieldDataArrays = []; - if (fieldDataElem) { - fieldDataArrays = [ - ...fieldDataElem.getElementsByTagName('DataArray'), - ].map((daElem) => - handleFieldDataArray( - daElem, - compressor, - byteOrder, - headerType, - model.binaryBuffer - ) - ); - } - for (let outputIndex = 0; outputIndex < nbPieces; outputIndex++) { // Create dataset const polydata = vtkPolyData.newInstance(); @@ -162,11 +122,6 @@ function vtkXMLPolyDataReader(publicAPI, model) { model.binaryBuffer ); - const fieldData = polydata.getFieldData(); - for (let i = 0; i < fieldDataArrays.length; i++) { - fieldData.addArray(fieldDataArrays[i]); - } - // Add new output model.output[outputIndex] = polydata; } diff --git a/Sources/IO/XML/XMLReader/index.js b/Sources/IO/XML/XMLReader/index.js index 0310ad1eb5a..1fd5073d0e8 100644 --- a/Sources/IO/XML/XMLReader/index.js +++ b/Sources/IO/XML/XMLReader/index.js @@ -320,6 +320,28 @@ function processFieldData( } } +// ---------------------------------------------------------------------------- +function handleFieldDataArrays( + fieldDataElem, + compressor, + byteOrder, + headerType, + binaryBuffer +) { + return [...fieldDataElem.getElementsByTagName('DataArray')].map((daElem) => + vtkDataArray.newInstance( + processDataArray( + Number(daElem.getAttribute('NumberOfTuples')), + daElem, + compressor, + byteOrder, + headerType, + binaryBuffer + ) + ) + ); +} + // ---------------------------------------------------------------------------- // vtkXMLReader methods // ---------------------------------------------------------------------------- @@ -513,6 +535,27 @@ function vtkXMLReader(publicAPI, model) { } publicAPI.parseXML(rootElem, type, compressor, byteOrder, headerType); + + const datasetElem = rootElem.getElementsByTagName(type)[0]; + const fieldDataElem = datasetElem.getElementsByTagName('FieldData')[0]; + + if (fieldDataElem) { + const fieldDataArrays = handleFieldDataArrays( + fieldDataElem, + compressor, + byteOrder, + headerType, + model.binaryBuffer + ); + + for (let i = 0; i < model.output.length; i++) { + const fieldData = model.output[i].getFieldData(); + for (let j = 0; j < fieldDataArrays.length; j++) { + fieldData.addArray(fieldDataArrays[j]); + } + } + } + return true; }; diff --git a/Sources/Testing/testAlgorithm.js b/Sources/Testing/testAlgorithm.js index ef83701bf58..144b347a6c8 100644 --- a/Sources/Testing/testAlgorithm.js +++ b/Sources/Testing/testAlgorithm.js @@ -63,3 +63,50 @@ test('Macro methods algo tests', (t) => { t.end(); }); + +test('Macro shouldUpdate returns true if output is deleted', (t) => { + const algo = { + publicAPI: {}, + model: {}, + }; + const input1 = { + publicAPI: {}, + model: {}, + }; + + const input2 = { + publicAPI: {}, + model: {}, + }; + + macro.obj(algo.publicAPI, algo.model); + macro.algo(algo.publicAPI, algo.model, 1, 1); + + macro.obj(input1.publicAPI, input1.model); + macro.obj(input2.publicAPI, input2.model); + + // trivial producer + algo.publicAPI.requestData = (inData, outData) => { + outData[0] = inData[0]; + }; + + algo.publicAPI.setInputData(input1.publicAPI, 0); + t.equal( + input1.publicAPI, + algo.publicAPI.getOutputData(), + 'Trivial producer outputs first input data' + ); + + // delete output data + algo.publicAPI.getOutputData().delete(); + + // set new data + algo.publicAPI.setInputData(input2.publicAPI, 0); + t.equal( + input2.publicAPI, + algo.publicAPI.getOutputData(), + 'Trivial producer outputs second input data' + ); + + t.end(); +}); diff --git a/Sources/Testing/testMacro.js b/Sources/Testing/testMacro.js index 753650b54d3..60d638c210c 100644 --- a/Sources/Testing/testMacro.js +++ b/Sources/Testing/testMacro.js @@ -428,3 +428,16 @@ test('Macro methods keystore tests', (t) => { t.end(); }); + +test('Macro debounce can be cancelled', (t) => { + const func = () => { + t.fail('Should not call cancelled debounce function'); + }; + + const debFunc = macro.debounce(func, 5); + debFunc(); + debFunc.cancel(); + + // end the test after the debounce phase + setTimeout(() => t.end(), 10); +}); diff --git a/Sources/Widgets/Representations/ResliceCursorContextRepresentation/index.js b/Sources/Widgets/Representations/ResliceCursorContextRepresentation/index.js index 5335529254e..522005f5527 100644 --- a/Sources/Widgets/Representations/ResliceCursorContextRepresentation/index.js +++ b/Sources/Widgets/Representations/ResliceCursorContextRepresentation/index.js @@ -150,8 +150,29 @@ function vtkResliceCursorContextRepresentation(publicAPI, model) { const length = vtkMath.normalize(vector); axis.line.source.setDirection(vector); axis.line.source.setHeight(length); - axis.rotation1.source.setCenter(state.getRotationPoint1()); - axis.rotation2.source.setCenter(state.getRotationPoint2()); + + // Rotation handles + const pixelWorldHeight = publicAPI.getPixelWorldHeightAtCoord(center); + const { rendererPixelDims } = model.displayScaleParams; + const minDim = Math.min(rendererPixelDims[0], rendererPixelDims[1]); + const ratio = 0.5; + const distance = + (window.devicePixelRatio * (pixelWorldHeight * ratio * minDim)) / 2; + const rotationHandlePosition = []; + vtkMath.multiplyAccumulate( + center, + vector, + distance, + rotationHandlePosition + ); + axis.rotation1.source.setCenter(rotationHandlePosition); + vtkMath.multiplyAccumulate( + center, + vector, + -distance, + rotationHandlePosition + ); + axis.rotation2.source.setCenter(rotationHandlePosition); } /** @@ -247,7 +268,6 @@ function vtkResliceCursorContextRepresentation(publicAPI, model) { const axis2State = state[getAxis2](); let activeLineState = null; - let activeRotationPointName = ''; let methodName = ''; switch (prop) { @@ -261,22 +281,18 @@ function vtkResliceCursorContextRepresentation(publicAPI, model) { break; case model.pipelines.axes[0].rotation1.actor: activeLineState = axis1State; - activeRotationPointName = 'RotationPoint1'; methodName = InteractionMethodsName.RotateLine; break; case model.pipelines.axes[0].rotation2.actor: activeLineState = axis1State; - activeRotationPointName = 'RotationPoint2'; methodName = InteractionMethodsName.RotateLine; break; case model.pipelines.axes[1].rotation1.actor: activeLineState = axis2State; - activeRotationPointName = 'RotationPoint1'; methodName = InteractionMethodsName.RotateLine; break; case model.pipelines.axes[1].rotation2.actor: activeLineState = axis2State; - activeRotationPointName = 'RotationPoint2'; methodName = InteractionMethodsName.RotateLine; break; default: @@ -285,7 +301,6 @@ function vtkResliceCursorContextRepresentation(publicAPI, model) { } state.setActiveLineState(activeLineState); - state.setActiveRotationPointName(activeRotationPointName); state.setUpdateMethodName(methodName); return state; @@ -296,33 +311,39 @@ function vtkResliceCursorContextRepresentation(publicAPI, model) { // Object factory // ---------------------------------------------------------------------------- -const DEFAULT_VALUES = { - axis1Name: '', - axis2Name: '', - coincidentTopologyParameters: { - Point: { - factor: -1.0, - offset: -1.0, - }, - Line: { - factor: -1.5, - offset: -1.5, +function defaultValues(initialValues) { + return { + axis1Name: '', + axis2Name: '', + coincidentTopologyParameters: { + Point: { + factor: -1.0, + offset: -1.0, + }, + Line: { + factor: -1.5, + offset: -1.5, + }, + Polygon: { + factor: -2.0, + offset: -2.0, + }, }, - Polygon: { - factor: -2.0, - offset: -2.0, - }, - }, - rotationEnabled: true, - scaleInPixels: true, - viewType: null, -}; + rotationEnabled: true, + rotationHandlePosition: 0.5, + scaleInPixels: true, + viewType: null, + ...initialValues, + }; +} // ---------------------------------------------------------------------------- export function extend(publicAPI, model, initialValues = {}) { + Object.assign(model, defaultValues(initialValues)); vtkWidgetRepresentation.extend(publicAPI, model, initialValues); - Object.assign(model, DEFAULT_VALUES, initialValues); + + macro.setGet(publicAPI, model, ['rotationHandlePosition']); // Object specific methods vtkResliceCursorContextRepresentation(publicAPI, model); diff --git a/Sources/Widgets/Representations/SplineContextRepresentation/index.js b/Sources/Widgets/Representations/SplineContextRepresentation/index.js index 364fe5c662c..8b652083c43 100644 --- a/Sources/Widgets/Representations/SplineContextRepresentation/index.js +++ b/Sources/Widgets/Representations/SplineContextRepresentation/index.js @@ -1,6 +1,6 @@ import macro from 'vtk.js/Sources/macro'; import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; -import vtkHandleRepresentation from 'vtk.js/Sources/Widgets/Representations/HandleRepresentation'; +import vtkContextRepresentation from 'vtk.js/Sources/Widgets/Representations/ContextRepresentation'; import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData'; import vtkSpline3D from 'vtk.js/Sources/Common/DataModel/Spline3D'; @@ -155,7 +155,7 @@ const DEFAULT_VALUES = { export function extend(publicAPI, model, initialValues = {}) { Object.assign(model, DEFAULT_VALUES, initialValues); - vtkHandleRepresentation.extend(publicAPI, model, initialValues); + vtkContextRepresentation.extend(publicAPI, model, initialValues); macro.get(publicAPI, model, ['mapper']); macro.setGet(publicAPI, model, [ 'resolution', diff --git a/Sources/Widgets/Widgets3D/ResliceCursorWidget/api.md b/Sources/Widgets/Widgets3D/ResliceCursorWidget/api.md index fd323a2588b..c1035fab3c5 100644 --- a/Sources/Widgets/Widgets3D/ResliceCursorWidget/api.md +++ b/Sources/Widgets/Widgets3D/ResliceCursorWidget/api.md @@ -24,8 +24,6 @@ These sub states contain : - activeLineState: Used in the behavior.js file in order to get the attribute of the selected line -- activeRotationPointName: Used in the behavior.js file in order to get the selected rotation point - - image: vtkImage used to place the reslice cursor - activeViewType: Used in the behavior.js file in order to get the current view diff --git a/Sources/Widgets/Widgets3D/ResliceCursorWidget/behavior.js b/Sources/Widgets/Widgets3D/ResliceCursorWidget/behavior.js index 1daeeeafe98..b2edaddd2a6 100644 --- a/Sources/Widgets/Widgets3D/ResliceCursorWidget/behavior.js +++ b/Sources/Widgets/Widgets3D/ResliceCursorWidget/behavior.js @@ -316,20 +316,19 @@ export default function widgetBehavior(publicAPI, model) { ); const center = model.widgetState.getCenter(); - const previousWorldPosition = activeLine[ - `get${model.widgetState.getActiveRotationPointName()}` - ](); - - const previousVectorToOrigin = [0, 0, 0]; - vtkMath.subtract(previousWorldPosition, center, previousVectorToOrigin); - vtkMath.normalize(previousVectorToOrigin); + const previousLineDirection = vtkMath.subtract( + activeLine.getPoint1(), + activeLine.getPoint2(), + [] + ); + vtkMath.normalize(previousLineDirection); const currentVectorToOrigin = [0, 0, 0]; vtkMath.subtract(worldCoords, center, currentVectorToOrigin); vtkMath.normalize(currentVectorToOrigin); const radianAngle = vtkMath.signedAngleBetweenVectors( - previousVectorToOrigin, + previousLineDirection, currentVectorToOrigin, planeNormal ); diff --git a/Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js b/Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js index 797a854e9ad..a4bdf0093e1 100644 --- a/Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js +++ b/Sources/Widgets/Widgets3D/ResliceCursorWidget/example/index.js @@ -7,6 +7,7 @@ import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; import vtkAnnotatedCubeActor from 'vtk.js/Sources/Rendering/Core/AnnotatedCubeActor'; import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray'; import vtkHttpDataSetReader from 'vtk.js/Sources/IO/Core/HttpDataSetReader'; +import vtkGenericRenderWindow from 'vtk.js/Sources/Rendering/Misc/GenericRenderWindow'; import vtkImageData from 'vtk.js/Sources/Common/DataModel/ImageData'; import vtkImageMapper from 'vtk.js/Sources/Rendering/Core/ImageMapper'; import vtkImageReslice from 'vtk.js/Sources/Imaging/Core/ImageReslice'; @@ -15,11 +16,7 @@ import vtkInteractorStyleImage from 'vtk.js/Sources/Interaction/Style/Interactor import vtkInteractorStyleTrackballCamera from 'vtk.js/Sources/Interaction/Style/InteractorStyleTrackballCamera'; import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; import vtkOutlineFilter from 'vtk.js/Sources/Filters/General/OutlineFilter'; -import vtkOpenGLRenderWindow from 'vtk.js/Sources/Rendering/OpenGL/RenderWindow'; import vtkOrientationMarkerWidget from 'vtk.js/Sources/Interaction/Widgets/OrientationMarkerWidget'; -import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer'; -import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow'; -import vtkRenderWindowInteractor from 'vtk.js/Sources/Rendering/Core/RenderWindowInteractor'; import vtkResliceCursorWidget from 'vtk.js/Sources/Widgets/Widgets3D/ResliceCursorWidget'; import vtkWidgetManager from 'vtk.js/Sources/Widgets/Core/WidgetManager'; @@ -59,25 +56,9 @@ const showDebugActors = true; // ---------------------------------------------------------------------------- const container = document.querySelector('body'); -const table = document.createElement('table'); -table.setAttribute('id', 'table'); -container.appendChild(table); - -// Define first line that will contains control panel -const trLine0 = document.createElement('tr'); -trLine0.setAttribute('id', 'line0'); -table.appendChild(trLine0); const controlContainer = document.createElement('div'); -trLine0.appendChild(controlContainer); controlContainer.innerHTML = controlPanel; - -const trLine1 = document.createElement('tr'); -trLine1.setAttribute('id', 'line1'); -table.appendChild(trLine1); - -const trLine2 = document.createElement('tr'); -trLine2.setAttribute('id', 'line2'); -table.appendChild(trLine2); +container.appendChild(controlContainer); // ---------------------------------------------------------------------------- // Setup rendering code @@ -125,25 +106,28 @@ function createRGBStringFromRGBValues(rgb) { } widgetState.setOpacity(0.6); +widgetState.setSphereRadius(10); const initialPlanesState = { ...widgetState.getPlanes() }; let view3D = null; for (let i = 0; i < 4; i++) { - const element = document.createElement('td'); - - if (i % 2 === 0) { - trLine2.appendChild(element); - } else { - trLine1.appendChild(element); - } - + 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); + + const grw = vtkGenericRenderWindow.newInstance(); + grw.setContainer(element); + grw.resize(); const obj = { - renderWindow: vtkRenderWindow.newInstance(), - renderer: vtkRenderer.newInstance(), - GLWindow: vtkOpenGLRenderWindow.newInstance(), - interactor: vtkRenderWindowInteractor.newInstance(), + renderWindow: grw.getRenderWindow(), + renderer: grw.getRenderer(), + GLWindow: grw.getOpenGLRenderWindow(), + interactor: grw.getInteractor(), widgetManager: vtkWidgetManager.newInstance(), }; @@ -152,7 +136,6 @@ for (let i = 0; i < 4; i++) { obj.renderWindow.addRenderer(obj.renderer); obj.renderWindow.addView(obj.GLWindow); obj.renderWindow.setInteractor(obj.interactor); - obj.GLWindow.setContainer(element); obj.interactor.setView(obj.GLWindow); obj.interactor.initialize(); obj.interactor.bindEvents(element); diff --git a/Sources/Widgets/Widgets3D/ResliceCursorWidget/helpers.js b/Sources/Widgets/Widgets3D/ResliceCursorWidget/helpers.js index 3de1a453fbd..bab2250314d 100644 --- a/Sources/Widgets/Widgets3D/ResliceCursorWidget/helpers.js +++ b/Sources/Widgets/Widgets3D/ResliceCursorWidget/helpers.js @@ -132,7 +132,7 @@ export function rotateVector(vectorToBeRotated, axis, angle) { } // Update the extremities and the rotation point coordinate of the line -function updateLine(lineState, center, axis, lineLength, rotationLength) { +function updateLine(lineState, center, axis, lineLength) { const p1 = [ center[0] - lineLength * axis[0], center[1] - lineLength * axis[1], @@ -143,21 +143,9 @@ function updateLine(lineState, center, axis, lineLength, rotationLength) { center[1] + lineLength * axis[1], center[2] + lineLength * axis[2], ]; - const rotationP1 = [ - center[0] - rotationLength * axis[0], - center[1] - rotationLength * axis[1], - center[2] - rotationLength * axis[2], - ]; - const rotationP2 = [ - center[0] + rotationLength * axis[0], - center[1] + rotationLength * axis[1], - center[2] + rotationLength * axis[2], - ]; lineState.setPoint1(p1); lineState.setPoint2(p2); - lineState.setRotationPoint1(rotationP1); - lineState.setRotationPoint2(rotationP2); } // Update the reslice cursor state according to the three planes normals and the origin @@ -173,12 +161,6 @@ export function updateState(widgetState) { const bounds = widgetState.getImage().getBounds(); const center = widgetState.getCenter(); - // Factor used to define where the rotation point will be displayed - // according to the plane size where there will be visible - const factor = 0.5 * 0.85; - const xRotationLength = (bounds[1] - bounds[0]) * factor; - const yRotationLength = (bounds[3] - bounds[2]) * factor; - const zRotationLength = (bounds[5] - bounds[4]) * factor; // Length of the principal diagonal. const pdLength = 20 * 0.5 * vtkBoundingBox.getDiagonalLength(bounds); @@ -187,45 +169,39 @@ export function updateState(widgetState) { widgetState.getAxisXinY(), center, xyIntersectionLineAxis, - pdLength, - zRotationLength + pdLength ); updateLine( widgetState.getAxisYinX(), center, xyIntersectionLineAxis, - pdLength, - zRotationLength + pdLength ); updateLine( widgetState.getAxisYinZ(), center, yzIntersectionLineAxis, - pdLength, - xRotationLength + pdLength ); updateLine( widgetState.getAxisZinY(), center, yzIntersectionLineAxis, - pdLength, - xRotationLength + pdLength ); updateLine( widgetState.getAxisXinZ(), center, xzIntersectionLineAxis, - pdLength, - yRotationLength + pdLength ); updateLine( widgetState.getAxisZinX(), center, xzIntersectionLineAxis, - pdLength, - yRotationLength + pdLength ); } diff --git a/Sources/Widgets/Widgets3D/ResliceCursorWidget/state.js b/Sources/Widgets/Widgets3D/ResliceCursorWidget/state.js index 698f3e6a0ef..8919f9382a5 100644 --- a/Sources/Widgets/Widgets3D/ResliceCursorWidget/state.js +++ b/Sources/Widgets/Widgets3D/ResliceCursorWidget/state.js @@ -3,7 +3,6 @@ import { ScrollingMethods } from 'vtk.js/Sources/Widgets/Widgets3D/ResliceCursor import { ViewTypes } from 'vtk.js/Sources/Widgets/Core/WidgetManager/Constants'; const factor = 1; -const rotationFactor = 1; const axisXColor = [1, 0, 0]; const axisYColor = [0, 1, 0]; const axisZColor = [0, 0, 1]; @@ -13,8 +12,6 @@ const generateAxisXinY = () => .createBuilder() .addField({ name: 'point1', initialValue: [0, 0, -factor] }) .addField({ name: 'point2', initialValue: [0, 0, factor] }) - .addField({ name: 'rotationPoint1', initialValue: [0, 0, -rotationFactor] }) - .addField({ name: 'rotationPoint2', initialValue: [0, 0, rotationFactor] }) .addField({ name: 'color', initialValue: axisXColor }) .addField({ name: 'name', initialValue: 'AxisXinY' }) .addField({ name: 'viewType', initialValue: ViewTypes.YZ_PLANE }) @@ -26,8 +23,6 @@ const generateAxisXinZ = () => .createBuilder() .addField({ name: 'point1', initialValue: [0, -factor, 0] }) .addField({ name: 'point2', initialValue: [0, factor, 0] }) - .addField({ name: 'rotationPoint1', initialValue: [0, -rotationFactor, 0] }) - .addField({ name: 'rotationPoint2', initialValue: [0, rotationFactor, 0] }) .addField({ name: 'color', initialValue: axisXColor }) .addField({ name: 'name', initialValue: 'AxisXinZ' }) .addField({ name: 'viewType', initialValue: ViewTypes.YZ_PLANE }) @@ -39,8 +34,6 @@ const generateAxisYinX = () => .createBuilder() .addField({ name: 'point1', initialValue: [0, 0, -factor] }) .addField({ name: 'point2', initialValue: [0, 0, factor] }) - .addField({ name: 'rotationPoint1', initialValue: [0, 0, -rotationFactor] }) - .addField({ name: 'rotationPoint2', initialValue: [0, 0, rotationFactor] }) .addField({ name: 'color', initialValue: axisYColor }) .addField({ name: 'name', initialValue: 'AxisYinX' }) .addField({ name: 'viewType', initialValue: ViewTypes.XZ_PLANE }) @@ -52,8 +45,6 @@ const generateAxisYinZ = () => .createBuilder() .addField({ name: 'point1', initialValue: [-factor, 0, 0] }) .addField({ name: 'point2', initialValue: [factor, 0, 0] }) - .addField({ name: 'rotationPoint1', initialValue: [-rotationFactor, 0, 0] }) - .addField({ name: 'rotationPoint2', initialValue: [rotationFactor, 0, 0] }) .addField({ name: 'color', initialValue: axisYColor }) .addField({ name: 'name', initialValue: 'AxisYinZ' }) .addField({ name: 'viewType', initialValue: ViewTypes.XZ_PLANE }) @@ -65,8 +56,6 @@ const generateAxisZinX = () => .createBuilder() .addField({ name: 'point1', initialValue: [0, -factor, 0] }) .addField({ name: 'point2', initialValue: [0, factor, 0] }) - .addField({ name: 'rotationPoint1', initialValue: [0, -rotationFactor, 0] }) - .addField({ name: 'rotationPoint2', initialValue: [0, rotationFactor, 0] }) .addField({ name: 'color', initialValue: axisZColor }) .addField({ name: 'name', initialValue: 'AxisZinX' }) .addField({ name: 'viewType', initialValue: ViewTypes.XY_PLANE }) @@ -78,8 +67,6 @@ const generateAxisZinY = () => .createBuilder() .addField({ name: 'point1', initialValue: [-factor, 0, 0] }) .addField({ name: 'point2', initialValue: [factor, 0, 0] }) - .addField({ name: 'rotationPoint1', initialValue: [-rotationFactor, 0, 0] }) - .addField({ name: 'rotationPoint2', initialValue: [rotationFactor, 0, 0] }) .addField({ name: 'color', initialValue: axisZColor }) .addField({ name: 'name', initialValue: 'AxisZinY' }) .addField({ name: 'viewType', initialValue: ViewTypes.XY_PLANE }) @@ -122,7 +109,6 @@ export default function generateState() { .addField({ name: 'center', initialValue: [0, 0, 0] }) .addField({ name: 'opacity', initialValue: 1 }) .addField({ name: 'activeLineState', initialValue: null }) - .addField({ name: 'activeRotationPointName', initialValue: null }) .addField({ name: 'image', initialValue: null }) .addField({ name: 'activeViewType', initialValue: null }) .addField({ name: 'lineThickness', initialValue: 2 }) diff --git a/Sources/Widgets/Widgets3D/SplineWidget/behavior.js b/Sources/Widgets/Widgets3D/SplineWidget/behavior.js index f90fbc8c3bb..055be6af384 100644 --- a/Sources/Widgets/Widgets3D/SplineWidget/behavior.js +++ b/Sources/Widgets/Widgets3D/SplineWidget/behavior.js @@ -4,12 +4,15 @@ import { vec3 } from 'gl-matrix'; export default function widgetBehavior(publicAPI, model) { model.classHierarchy.push('vtkSplineWidgetProp'); + model.keysDown = {}; + model.moveHandle = model.widgetState.getMoveHandle(); + // -------------------------------------------------------------------------- // Private methods // -------------------------------------------------------------------------- const updateHandlesSize = () => { - if (model.handleSizeInPixels !== null) { + if (model.handleSizeInPixels != null) { const scale = model.handleSizeInPixels * vec3.distance( @@ -104,21 +107,19 @@ export default function widgetBehavior(publicAPI, model) { // -------------------------------------------------------------------------- - const superSetHandleSizeInPixels = publicAPI.setHandleSizeInPixels; publicAPI.setHandleSizeInPixels = (size) => { - superSetHandleSizeInPixels(size); + model.factory.setHandleSizeInPixels(size); updateHandlesSize(); }; - publicAPI.setHandleSizeInPixels(model.handleSizeInPixels); // set initial value + publicAPI.setHandleSizeInPixels(model.factory.getHandleSizeInPixels()); // set initial value // -------------------------------------------------------------------------- - const superSetResolution = publicAPI.setResolution; publicAPI.setResolution = (resolution) => { - superSetResolution(resolution); - model.representations[1].setResolution(model.resolution); + model.factory.setResolution(resolution); + model.representations[1].setResolution(resolution); }; - publicAPI.setResolution(model.resolution); // set initial value + publicAPI.setResolution(model.factory.getResolution()); // set initial value // -------------------------------------------------------------------------- diff --git a/Sources/Widgets/Widgets3D/SplineWidget/index.js b/Sources/Widgets/Widgets3D/SplineWidget/index.js index e5a8f5214f7..290bd581a87 100644 --- a/Sources/Widgets/Widgets3D/SplineWidget/index.js +++ b/Sources/Widgets/Widgets3D/SplineWidget/index.js @@ -51,7 +51,6 @@ function vtkSplineWidget(publicAPI, model) { // initialization // -------------------------------------------------------------------------- - model.moveHandle = model.widgetState.getMoveHandle(); // Default manipulator model.manipulator = vtkPlanePointManipulator.newInstance(); } @@ -59,12 +58,11 @@ function vtkSplineWidget(publicAPI, model) { // ---------------------------------------------------------------------------- const DEFAULT_VALUES = { - keysDown: {}, freehandMinDistance: 0.1, allowFreehand: true, - resolution: 32, + resolution: 32, // propagates to SplineContextRepresentation defaultCursor: 'pointer', - handleSizeInPixels: 10, + handleSizeInPixels: 10, // propagates to SplineContextRepresentation }; // ---------------------------------------------------------------------------- diff --git a/Sources/interfaces.d.ts b/Sources/interfaces.d.ts index fed2fb1870b..30260bea9d0 100644 --- a/Sources/interfaces.d.ts +++ b/Sources/interfaces.d.ts @@ -16,6 +16,14 @@ export interface vtkRange { max: number; } +/** + * Represents a debounced function. + */ +export interface vtkDebouncedFunction { + (...args: any) : any; + cancel() : void; +} + export interface vtkOutputPort { filter: vtkAlgorithm; } diff --git a/Sources/macro.d.ts b/Sources/macro.d.ts index bc19ec36e8d..29688dad86e 100644 --- a/Sources/macro.d.ts +++ b/Sources/macro.d.ts @@ -1,4 +1,4 @@ -import { vtkSubscription, vtkProperty, vtkPropertyDomain } from "./interfaces"; +import { vtkSubscription, vtkDebouncedFunction, vtkProperty, vtkPropertyDomain } from "./interfaces"; /** * Allow user to redefine vtkXXXMacro method call. @@ -305,8 +305,10 @@ export function traverseInstanceTree( * @param func * @param wait * @param immediate (default false) + * @returns vtkDebouncedFunction A debounced function that can be called. + * Use .cancel() to clear any pending debounced call. */ -export function debounce(func: (...args: any) => any, wait: number, immediate?: boolean): (...args: any) => any; +export function debounce(func: (...args: any) => any, wait: number, immediate?: boolean): vtkDebouncedFunction; /** * Creates a throttled function that only invokes `func` at most once per diff --git a/Sources/macro.js b/Sources/macro.js index b10446e7b88..17206ac5387 100644 --- a/Sources/macro.js +++ b/Sources/macro.js @@ -1,4 +1,4 @@ -import vtk from './vtk'; +import vtk, { vtkGlobal } from './vtk'; import ClassHierarchy from './Common/Core/ClassHierarchy'; let globalMTime = 0; @@ -33,14 +33,14 @@ consoleMethods.forEach((methodName) => { fakeConsole[methodName] = noOp; }); -global.console = console.hasOwnProperty('log') ? console : fakeConsole; +vtkGlobal.console = console.hasOwnProperty('log') ? console : fakeConsole; const loggerFunctions = { debug: noOp, // Don't print debug by default - error: global.console.error || noOp, - info: global.console.info || noOp, - log: global.console.log || noOp, - warn: global.console.warn || noOp, + error: vtkGlobal.console.error || noOp, + info: vtkGlobal.console.info || noOp, + log: vtkGlobal.console.log || noOp, + warn: vtkGlobal.console.warn || noOp, }; export function setLoggerFunction(name, fn) { @@ -705,6 +705,11 @@ export function algo(publicAPI, model, numberOfInputs, numberOfOutputs) { if (!model.output[count]) { return true; } + + if (model.output[count].isDeleted()) { + return true; + } + const mt = model.output[count].getMTime(); if (mt < localMTime) { return true; @@ -985,7 +990,7 @@ export function traverseInstanceTree( export function debounce(func, wait, immediate) { let timeout; - return (...args) => { + const debounced = (...args) => { const context = this; const later = () => { timeout = null; @@ -1000,6 +1005,10 @@ export function debounce(func, wait, immediate) { func.apply(context, args); } }; + + debounced.cancel = () => clearTimeout(timeout); + + return debounced; } // ---------------------------------------------------------------------------- diff --git a/Sources/vtk.js b/Sources/vtk.js index 7cb7fb312b7..e2499df60a5 100644 --- a/Sources/vtk.js +++ b/Sources/vtk.js @@ -1,3 +1,7 @@ +import globalThisShim from 'globalthis'; + +export const vtkGlobal = globalThisShim(); // returns native globalThis if compliant + const factoryMapping = { vtkObject: () => null, }; @@ -10,15 +14,15 @@ export default function vtk(obj) { return obj; } if (!obj.vtkClass) { - if (global.console && global.console.error) { - global.console.error('Invalid VTK object'); + if (vtkGlobal.console && vtkGlobal.console.error) { + vtkGlobal.console.error('Invalid VTK object'); } return null; } const constructor = factoryMapping[obj.vtkClass]; if (!constructor) { - if (global.console && global.console.error) { - global.console.error( + if (vtkGlobal.console && vtkGlobal.console.error) { + vtkGlobal.console.error( `No vtk class found for Object of type ${obj.vtkClass}` ); } diff --git a/package-lock.json b/package-lock.json index df1e74d1e09..65ac170f7d1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7524,7 +7524,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -9873,7 +9872,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", "integrity": "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ==", - "dev": true, "requires": { "define-properties": "^1.1.3" } @@ -15642,8 +15640,7 @@ "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object-visit": { "version": "1.0.1", diff --git a/package.json b/package.json index 81e3a77a29b..3392a7b6738 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "commander": "6.2.1", "d3-scale": "3.2.4", "gl-matrix": "3.3.0", + "globalthis": "^1.0.2", "jszip": "3.2.0", "pako": "2.0.3", "seedrandom": "3.0.5",