From dd32dfc41b79273a309b88f4634e22d8be5d832a Mon Sep 17 00:00:00 2001 From: milebril Date: Wed, 25 May 2022 15:07:37 +0200 Subject: [PATCH] feat(polydatacellnormals): programatically choose to compute cell normals closes #2434 --- .../Core/PolyDataNormals/example/index.js | 94 +++++++++++++++++++ Sources/Filters/Core/PolyDataNormals/index.js | 57 +++++++++++ 2 files changed, 151 insertions(+) create mode 100644 Sources/Filters/Core/PolyDataNormals/example/index.js diff --git a/Sources/Filters/Core/PolyDataNormals/example/index.js b/Sources/Filters/Core/PolyDataNormals/example/index.js new file mode 100644 index 00000000000..23ce4a6cd2c --- /dev/null +++ b/Sources/Filters/Core/PolyDataNormals/example/index.js @@ -0,0 +1,94 @@ +import 'vtk.js/Sources/favicon'; + +// Load the rendering pieces we want to use (for both WebGL and WebGPU) +import 'vtk.js/Sources/Rendering/Profiles/Geometry'; + +import vtkFullScreenRenderWindow from 'vtk.js/Sources/Rendering/Misc/FullScreenRenderWindow'; +import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; +import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; +import vtkLineSource from 'vtk.js/Sources/Filters/Sources/LineSource'; +import vtkSphereSource from 'vtk.js/Sources/Filters/Sources/SphereSource'; + +import vtkPolyDataNormals from 'vtk.js/Sources/Filters/Core/PolyDataNormals'; + +// ---------------------------------------------------------------------------- +// Standard rendering code setup +// ---------------------------------------------------------------------------- + +const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance(); +const renderer = fullScreenRenderer.getRenderer(); +const renderWindow = fullScreenRenderer.getRenderWindow(); + +const computeNormals = vtkPolyDataNormals.newInstance(); + +const sphere = vtkSphereSource.newInstance({ + thetaResolution: 4, + phiResolution: 4, +}); +const polydata = sphere.getOutputData(); + +const mapper = vtkMapper.newInstance(); +const actor = vtkActor.newInstance(); + +actor.setMapper(mapper); +mapper.setInputData(polydata); + +renderer.addActor(actor); + +computeNormals.setInputData(polydata); +computeNormals.setComputeCellNormals(true); +computeNormals.update(); + +const cellNormals = computeNormals.getOutputData().getCellData().getNormals(); + +const pointsData = polydata.getPoints().getData(); +const polysData = polydata.getPolys().getData(); + +let numberOfPoints = 0; +const cellPointIds = [0, 0, 0]; +let normalId = 0; + +for (let c = 0; c < polysData.length; c += numberOfPoints + 1) { + numberOfPoints = polysData[c]; + + if (numberOfPoints < 3) { + continue; // eslint-disable-line + } + + for (let i = 1; i <= 3; ++i) { + cellPointIds[i - 1] = 3 * polysData[c + i]; + } + + const v1 = pointsData.slice(cellPointIds[0], cellPointIds[0] + 3); + const v2 = pointsData.slice(cellPointIds[1], cellPointIds[1] + 3); + const v3 = pointsData.slice(cellPointIds[2], cellPointIds[2] + 3); + + const center = [ + (v1[0] + v2[0] + v3[0]) / 3, + (v1[1] + v2[1] + v3[1]) / 3, + (v1[2] + v2[2] + v3[2]) / 3, + ]; + + const line = vtkLineSource.newInstance({ + point1: center, + point2: [ + center[0] + cellNormals[normalId++], + center[1] + cellNormals[normalId++], + center[2] + cellNormals[normalId++], + ], + }); + + const lineMapper = vtkMapper.newInstance(); + const lineActor = vtkActor.newInstance(); + + lineActor.setMapper(lineMapper); + lineMapper.setInputData(line.getOutputData()); + renderer.addActor(lineActor); +} + +// Display the resulting STL +renderer.resetCamera(); +renderWindow.render(); + +global.renderer = renderer; +global.renderWindow = renderWindow; diff --git a/Sources/Filters/Core/PolyDataNormals/index.js b/Sources/Filters/Core/PolyDataNormals/index.js index 11edb794470..3f70526a1af 100644 --- a/Sources/Filters/Core/PolyDataNormals/index.js +++ b/Sources/Filters/Core/PolyDataNormals/index.js @@ -5,6 +5,11 @@ import vtkMath from 'vtk.js/Sources/Common/Core/Math/index'; import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData'; import vtkTriangle from 'vtk.js/Sources/Common/DataModel/Triangle'; +export const NormalType = { + CELL: 'cell', + POINT: ' point', +}; + // ---------------------------------------------------------------------------- // vtkPolyDataNormals methods // ---------------------------------------------------------------------------- @@ -69,6 +74,49 @@ function vtkPolyDataNormals(publicAPI, model) { return normalsData; }; + publicAPI.vtkPolyDataCellNormalsExecute = (polyData) => { + const pointsData = polyData.getPoints().getData(); + const polysData = polyData.getPolys().getData(); + const numPolys = polyData.getPolys().getNumberOfCells(); + + if (!pointsData || !polysData) { + return null; + } + + const normalsData = new Float32Array(numPolys * 3); + + let normalsIndex = 0; + let numberOfPoints = 0; + const cellPointIds = [0, 0, 0]; + + for (let c = 0; c < polysData.length; c += numberOfPoints + 1) { + numberOfPoints = polysData[c]; + + if (numberOfPoints < 3) { + continue; // eslint-disable-line + } + + for (let i = 1; i <= 3; ++i) { + cellPointIds[i - 1] = 3 * polysData[c + i]; + } + + const cellNormal = []; + + vtkTriangle.computeNormal( + pointsData.slice(cellPointIds[0], cellPointIds[0] + 3), + pointsData.slice(cellPointIds[1], cellPointIds[1] + 3), + pointsData.slice(cellPointIds[2], cellPointIds[2] + 3), + cellNormal + ); + + normalsData[normalsIndex++] = cellNormal[0]; + normalsData[normalsIndex++] = cellNormal[1]; + normalsData[normalsIndex++] = cellNormal[2]; + } + + return normalsData; + }; + publicAPI.requestData = (inData, outData) => { const numberOfInputs = publicAPI.getNumberOfInputPorts(); @@ -105,6 +153,13 @@ function vtkPolyDataNormals(publicAPI, model) { output.getPointData().setNormals(outputNormals); + if (model.computeCellNormals) { + const outputCellNormalsData = + publicAPI.vtkPolyDataCellNormalsExecute(input); + + output.getCellData().setNormals(outputCellNormalsData); + } + outData[0] = output; }; } @@ -130,6 +185,8 @@ export function extend(publicAPI, model, initialValues = {}) { macro.algo(publicAPI, model, 1, 1); + macro.setGet(publicAPI, model, ['computeCellNormals']); + /* Object specific methods */ vtkPolyDataNormals(publicAPI, model);