Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2434 vtk poly data normals add compute cell normals #2912

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions Sources/Filters/Core/PolyDataNormals/example/controlPanel.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<table>
<tr>
<td>Compute point normals</td>
<td>
<input class="computePointNormals" type="checkbox" checked />
</td>
</tr>
<tr>
<td>Compute cell normals</td>
<td>
<input class="computeCellNormals" type="checkbox" />
</td>
</tr>
</table>
112 changes: 112 additions & 0 deletions Sources/Filters/Core/PolyDataNormals/example/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import '@kitware/vtk.js/favicon';

// Load the rendering pieces we want to use (for both WebGL and WebGPU)
import '@kitware/vtk.js/Rendering/Profiles/Geometry';
import '@kitware/vtk.js/Rendering/Profiles/Glyph';

import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow';

import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor';
import vtkArrowSource from '@kitware/vtk.js/Filters/Sources/ArrowSource';
import vtkCubeSource from '@kitware/vtk.js/Filters/Sources/CubeSource';
import vtkLookupTable from '@kitware/vtk.js/Common/Core/LookupTable';
import vtkGlyph3DMapper from '@kitware/vtk.js/Rendering/Core/Glyph3DMapper';
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
import vtkPolyDataNormals from '@kitware/vtk.js/Filters/Core/PolyDataNormals';

import controlPanel from './controlPanel.html';

const { ColorMode, ScalarMode } = vtkMapper;

// ----------------------------------------------------------------------------
// Standard rendering code setup
// ----------------------------------------------------------------------------

const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({
background: [0.9, 0.9, 0.9],
});
const renderer = fullScreenRenderer.getRenderer();
const renderWindow = fullScreenRenderer.getRenderWindow();

// ----------------------------------------------------------------------------
// Example code
// ----------------------------------------------------------------------------

const lookupTable = vtkLookupTable.newInstance({ hueRange: [0.666, 0] });

const source = vtkCubeSource.newInstance();
const inputPolyData = source.getOutputData();
inputPolyData.getPointData().setNormals(null);

const mapper = vtkMapper.newInstance({
interpolateScalarsBeforeMapping: true,
colorMode: ColorMode.DEFAULT,
scalarMode: ScalarMode.DEFAULT,
useLookupTableScalarRange: true,
lookupTable,
});
const actor = vtkActor.newInstance();
actor.getProperty().setEdgeVisibility(true);

const polyDataNormals = vtkPolyDataNormals.newInstance();

// The generated 'z' array will become the default scalars, so the plane mapper will color by 'z':
polyDataNormals.setInputData(inputPolyData);

mapper.setInputConnection(polyDataNormals.getOutputPort());
actor.setMapper(mapper);

renderer.addActor(actor);

const arrowSource = vtkArrowSource.newInstance();

const glyphMapper = vtkGlyph3DMapper.newInstance();
glyphMapper.setInputConnection(polyDataNormals.getOutputPort());
glyphMapper.setSourceConnection(arrowSource.getOutputPort());
glyphMapper.setOrientationModeToDirection();
glyphMapper.setOrientationArray('Normals');
glyphMapper.setScaleModeToScaleByMagnitude();
glyphMapper.setScaleArray('Normals');
glyphMapper.setScaleFactor(0.1);

const glyphActor = vtkActor.newInstance();
glyphActor.setMapper(glyphMapper);
renderer.addActor(glyphActor);

renderer.resetCamera();
renderWindow.render();

// ----------------------------------------------------------------------------
// UI control handling
// ----------------------------------------------------------------------------

fullScreenRenderer.addController(controlPanel);

// Checkbox
document
.querySelector('.computePointNormals')
.addEventListener('change', (e) => {
polyDataNormals.setComputePointNormals(!!e.target.checked);
renderWindow.render();
});

document
.querySelector('.computeCellNormals')
.addEventListener('change', (e) => {
polyDataNormals.setComputeCellNormals(!!e.target.checked);
renderWindow.render();
});

// -----------------------------------------------------------
// Make some variables global so that you can inspect and
// modify objects in your browser's developer console:
// -----------------------------------------------------------

global.mapper = mapper;
global.actor = actor;
global.source = source;
global.renderer = renderer;
global.renderWindow = renderWindow;
global.lookupTable = lookupTable;
global.polyDataNormals = polyDataNormals;
global.glyphMapper = glyphMapper;
90 changes: 61 additions & 29 deletions Sources/Filters/Core/PolyDataNormals/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -36,37 +43,46 @@ 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),
pointsData.slice(cellPointIds[2], cellPointIds[2] + 3),
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) => {
Expand All @@ -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());
Expand All @@ -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;
};
Expand All @@ -115,6 +143,8 @@ function vtkPolyDataNormals(publicAPI, model) {
// ----------------------------------------------------------------------------
function defaultValues(initialValues) {
return {
computeCellNormals: false,
computePointNormals: true,
...initialValues,
};
}
Expand All @@ -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);
Expand Down
71 changes: 71 additions & 0 deletions Sources/Filters/Core/PolyDataNormals/test/testPolyDataNormals.js
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -24,3 +28,70 @@ test('Test vtkPolyDataNormals passData', (t) => {

t.end();
});

test('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();
});
Loading
Loading