diff --git a/Sources/Filters/Core/PolyDataNormals/index.js b/Sources/Filters/Core/PolyDataNormals/index.js index 4086b567bf8..3e5a4f0b8df 100644 --- a/Sources/Filters/Core/PolyDataNormals/index.js +++ b/Sources/Filters/Core/PolyDataNormals/index.js @@ -13,17 +13,24 @@ function vtkPolyDataNormals(publicAPI, model) { // Set our className model.classHierarchy.push('vtkPolyDataNormals'); - publicAPI.vtkPolyDataNormalsExecute = (pointsData, polysData) => { + publicAPI.vtkPolyDataNormalsExecute = ( + numberOfPolys, + polysData, + pointsData + ) => { if (!pointsData) { return null; } - const normalsData = new Float32Array(pointsData.length); + const pointNormals = new Float32Array(pointsData.length); + const cellNormals = new Float32Array(3 * numberOfPolys); + let cellNormalComponent = 0; let numberOfPoints = 0; const polysDataLength = polysData.length; const cellPointIds = [0, 0, 0]; + const cellNormal = [0, 0, 0]; for (let c = 0; c < polysDataLength; c += numberOfPoints + 1) { numberOfPoints = polysData[c]; @@ -36,8 +43,6 @@ function vtkPolyDataNormals(publicAPI, model) { 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), @@ -45,28 +50,39 @@ function vtkPolyDataNormals(publicAPI, model) { cellNormal ); - for (let i = 1; i <= numberOfPoints; ++i) { - let pointId = 3 * polysData[c + i]; + cellNormals[cellNormalComponent++] = cellNormal[0]; + cellNormals[cellNormalComponent++] = cellNormal[1]; + cellNormals[cellNormalComponent++] = cellNormal[2]; + + if (model.computePointNormals) { + for (let i = 1; i <= numberOfPoints; ++i) { + let pointId = 3 * polysData[c + i]; - normalsData[pointId] += cellNormal[0]; - normalsData[++pointId] += cellNormal[1]; - normalsData[++pointId] += cellNormal[2]; + pointNormals[pointId] += cellNormal[0]; + pointNormals[++pointId] += cellNormal[1]; + pointNormals[++pointId] += cellNormal[2]; + } } } - /* Normalize normals */ + // Normalize point normals. + // A point normal is the sum of all the cell normals the point belongs to + if (model.computePointNormals) { + const pointNormal = [0, 0, 0]; + for (let i = 0; i < pointsData.length; ) { + pointNormal[0] = pointNormals[i]; + pointNormal[1] = pointNormals[i + 1]; + pointNormal[2] = pointNormals[i + 2]; - for (let i = 0; i < pointsData.length; ) { - const pointNormal = normalsData.slice(i, i + 3); + vtkMath.normalize(pointNormal); - vtkMath.normalize(pointNormal); - - normalsData[i++] = pointNormal[0]; - normalsData[i++] = pointNormal[1]; - normalsData[i++] = pointNormal[2]; + pointNormals[i++] = pointNormal[0]; + pointNormals[i++] = pointNormal[1]; + pointNormals[i++] = pointNormal[2]; + } } - return normalsData; + return [cellNormals, pointNormals]; }; publicAPI.requestData = (inData, outData) => { @@ -82,18 +98,8 @@ function vtkPolyDataNormals(publicAPI, model) { return; } - const outputNormalsData = publicAPI.vtkPolyDataNormalsExecute( - input.getPoints().getData(), - input.getPolys().getData() - ); - const output = vtkPolyData.newInstance(); - const outputNormals = vtkDataArray.newInstance({ - numberOfComponents: 3, - values: outputNormalsData, - }); - output.setPoints(input.getPoints()); output.setVerts(input.getVerts()); output.setLines(input.getLines()); @@ -104,7 +110,29 @@ function vtkPolyDataNormals(publicAPI, model) { output.getCellData().passData(input.getCellData()); output.getFieldData().passData(input.getFieldData()); - output.getPointData().setNormals(outputNormals); + const [cellNormals, pointNormals] = publicAPI.vtkPolyDataNormalsExecute( + input.getNumberOfPolys(), + input.getPolys().getData(), + input.getPoints().getData() + ); + + if (model.computePointNormals) { + const outputPointNormals = vtkDataArray.newInstance({ + numberOfComponents: 3, + name: 'Normals', + values: pointNormals, + }); + output.getPointData().setNormals(outputPointNormals); + } + + if (model.computeCellNormals) { + const outputCellNormals = vtkDataArray.newInstance({ + numberOfComponents: 3, + name: 'Normals', + values: cellNormals, + }); + output.getCellData().setNormals(outputCellNormals); + } outData[0] = output; }; @@ -115,6 +143,8 @@ function vtkPolyDataNormals(publicAPI, model) { // ---------------------------------------------------------------------------- function defaultValues(initialValues) { return { + computeCellNormals: false, + computePointNormals: true, ...initialValues, }; } @@ -131,6 +161,8 @@ export function extend(publicAPI, model, initialValues = {}) { macro.algo(publicAPI, model, 1, 1); + macro.setGet(publicAPI, model, ['computeCellNormals', 'computePointNormals']); + /* Object specific methods */ vtkPolyDataNormals(publicAPI, model); diff --git a/Sources/Filters/Core/PolyDataNormals/test/testPolyDataNormals.js b/Sources/Filters/Core/PolyDataNormals/test/testPolyDataNormals.js index 9106e88fd50..1d289fda32c 100644 --- a/Sources/Filters/Core/PolyDataNormals/test/testPolyDataNormals.js +++ b/Sources/Filters/Core/PolyDataNormals/test/testPolyDataNormals.js @@ -1,7 +1,11 @@ import test from 'tape-catch'; import vtkCubeSource from 'vtk.js/Sources/Filters/Sources/CubeSource'; +import vtkMath from 'vtk.js/Sources/Common/Core/Math'; import vtkPolyDataNormals from 'vtk.js/Sources/Filters/Core/PolyDataNormals'; +import vtkTriangle from 'vtk.js/Sources/Common/DataModel/Triangle'; + +const PRECISION = 4; test('Test vtkPolyDataNormals passData', (t) => { const cube = vtkCubeSource.newInstance(); @@ -24,3 +28,70 @@ test('Test vtkPolyDataNormals passData', (t) => { t.end(); }); + +test.only('Test vtkPolyDataNormals normals', (t) => { + const cube = vtkCubeSource.newInstance(); + const input = cube.getOutputData(); + const pointNormalsData = input.getPointData().getNormals().getData(); + // const cellNormalsData = input.getCellData().getNormals().getData(); + input.getPointData().setNormals(null); + input.getCellData().setNormals(null); + + const normals = vtkPolyDataNormals.newInstance(); + normals.setInputData(input); + normals.setComputeCellNormals(true); + normals.update(); + const output = normals.getOutputData(); + + console.log(pointNormalsData); + console.log(output.getPointData().getNormals().getData()); + t.deepEqual( + vtkMath.roundVector(pointNormalsData, [], PRECISION), + vtkMath.roundVector( + output.getPointData().getNormals().getData(), + [], + PRECISION + ), + 'Same point normals' + ); + + const pointsData = output.getPoints().getData(); + const polysData = output.getPolys().getData(); + const polysDataLength = polysData.length; + const cellPointIds = [0, 0, 0]; + let numberOfPoints = 0; + let polysId = 0; + for (let c = 0; c < polysDataLength; c += numberOfPoints + 1) { + numberOfPoints = polysData[c]; + + 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 + ); + + t.deepEqual( + vtkMath.roundVector(cellNormal, [], PRECISION), + vtkMath.roundVector( + output + .getCellData() + .getNormals() + .getData() + .slice(3 * polysId, 3 * polysId + 3), + [], + PRECISION + ), + `Same cell normal #${polysId}` + ); + ++polysId; + } + + t.end(); +});