Skip to content
Open
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
Binary file added Documentation/public/gallery/ReverseSense.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
100 changes: 100 additions & 0 deletions Sources/Filters/Core/ReverseSense/example/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import '@kitware/vtk.js/favicon';

import '@kitware/vtk.js/Rendering/Profiles/Geometry';
import '@kitware/vtk.js/Rendering/Profiles/Glyph';

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 vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow';
import vtkGlyph3DMapper from '@kitware/vtk.js/Rendering/Core/Glyph3DMapper';
import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper';
import vtkReverseSense from '@kitware/vtk.js/Filters/Core/ReverseSense';

import GUI from 'lil-gui';

const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance();
const renderer = fullScreenRenderer.getRenderer();
const renderWindow = fullScreenRenderer.getRenderWindow();

const COLORS = {
source: [0.2, 0.55, 0.86],
reversed: [0.94, 0.52, 0.25],
};

function rgb(color) {
return `rgb(${color.map((value) => Math.round(value * 255)).join(', ')})`;
}

function styleCube(actor, color) {
actor.getProperty().setColor(...color);
actor.getProperty().setDiffuse(0.75);
actor.getProperty().setAmbient(0.2);
actor.getProperty().setSpecular(0.18);
actor.getProperty().setSpecularPower(22);
}

function addLegend() {
const gui = new GUI({ title: 'Controls' });
const details = {
source: rgb(COLORS.source),
reversed: rgb(COLORS.reversed),
};

gui.addColor(details, 'source').name('Source').disable();
gui.addColor(details, 'reversed').name('Reversed').disable();
}

const cubeSource1 = vtkCubeSource.newInstance();
const cubeActor1 = vtkActor.newInstance();
const cubeMapper1 = vtkMapper.newInstance();
cubeActor1.setMapper(cubeMapper1);
cubeMapper1.setInputConnection(cubeSource1.getOutputPort());
styleCube(cubeActor1, COLORS.source);
renderer.addActor(cubeActor1);

const arrowSource1 = vtkArrowSource.newInstance();
const glyphMapper1 = vtkGlyph3DMapper.newInstance();
glyphMapper1.setInputConnection(cubeSource1.getOutputPort());
glyphMapper1.setSourceConnection(arrowSource1.getOutputPort());
glyphMapper1.setOrientationModeToDirection();
glyphMapper1.setOrientationArray('Normals');
glyphMapper1.setScaleModeToScaleByMagnitude();
glyphMapper1.setScaleArray('Normals');
glyphMapper1.setScaleFactor(0.1);

const glyphActor1 = vtkActor.newInstance();
glyphActor1.setMapper(glyphMapper1);
renderer.addActor(glyphActor1);

const cubeSource2 = vtkCubeSource.newInstance();
const cubeActor2 = vtkActor.newInstance();
const cubeMapper2 = vtkMapper.newInstance();
cubeActor2.setMapper(cubeMapper2);
cubeMapper2.setInputConnection(cubeSource2.getOutputPort());
cubeActor2.setPosition(2, 0, 0);
styleCube(cubeActor2, COLORS.reversed);
renderer.addActor(cubeActor2);

const reverseSense = vtkReverseSense.newInstance({ reverseNormals: true });
reverseSense.setInputConnection(cubeSource2.getOutputPort());

const arrowSource2 = vtkArrowSource.newInstance();
const glyphMapper2 = vtkGlyph3DMapper.newInstance();
glyphMapper2.setInputConnection(reverseSense.getOutputPort());
glyphMapper2.setSourceConnection(arrowSource2.getOutputPort());
glyphMapper2.setOrientationModeToDirection();
glyphMapper2.setOrientationArray('Normals');
glyphMapper2.setScaleModeToScaleByMagnitude();
glyphMapper2.setScaleArray('Normals');
glyphMapper2.setScaleFactor(0.1);

const glyphActor2 = vtkActor.newInstance();
glyphActor2.setMapper(glyphMapper2);
glyphActor2.setPosition(2, 0, 0);
renderer.addActor(glyphActor2);

addLegend();

renderer.resetCamera();
renderWindow.render();
72 changes: 72 additions & 0 deletions Sources/Filters/Core/ReverseSense/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { vtkAlgorithm, vtkObject } from '../../../interfaces';

export interface IReverseSenseInitialValues {
reverseCells?: boolean;
reverseNormals?: boolean;
}

type vtkReverseSenseBase = vtkObject & vtkAlgorithm;

export interface vtkReverseSense extends vtkReverseSenseBase {
/**
* Get whether the order of polygonal cells is reversed.
*/
getReverseCells(): boolean;

/**
* Get whether the direction of point and cell normals is reversed.
*/
getReverseNormals(): boolean;

/**
* Request data from the input and produce output.
* @param inData The input data.
* @param outData The output data.
*/
requestData(inData: any, outData: any): void;

/**
* Controls whether the order of polygonal cells is reversed.
* @param {Boolean} reverseCells The new state of the `reverseCells` flag.
*/
setReverseCells(reverseCells: boolean): boolean;

/**
* Controls whether the direction of point and cell normals is reversed.
* @param {Boolean} reverseNormals The new state of the `reverseNormals` flag.
*/
setReverseNormals(reverseNormals: boolean): boolean;
}

/**
* Method used to decorate a given object (publicAPI+model) with vtkReverseSense characteristics.
*
* @param publicAPI object on which methods will be bounds (public)
* @param model object on which data structure will be bounds (protected)
* @param {IReverseSenseInitialValues} [initialValues] (default: {})
*/
export function extend(
publicAPI: object,
model: object,
initialValues?: IReverseSenseInitialValues
): void;

/**
* Method used to create a new instance of vtkReverseSense.
* @param {IReverseSenseInitialValues} [initialValues] for pre-setting some of its content
*/
export function newInstance(
initialValues?: IReverseSenseInitialValues
): vtkReverseSense;

/**
* vtkReverseSense is a filter that reverses the order of polygonal cells and/or reverses the direction of point and cell normals.
* Two flags are used to control these operations: `reverseCells` and `reverseNormals`.
* Cell reversal means reversing the order of indices in the cell connectivity list.
* Normal reversal means multiplying the normal vector by -1 (both point and cell normals, if present).
*/
export declare const vtkReverseSense: {
newInstance: typeof newInstance;
extend: typeof extend;
};
export default vtkReverseSense;
142 changes: 142 additions & 0 deletions Sources/Filters/Core/ReverseSense/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import macro from 'vtk.js/Sources/macros';

import vtkCellArray from 'vtk.js/Sources/Common/Core/CellArray';
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';

const { vtkErrorMacro } = macro;

function reverseCellArray(inputCells) {
if (!inputCells) {
return null;
}

const inputData = inputCells.getData();
const outputData = inputData.slice();

for (let offset = 0, len = inputData.length; offset < len; ) {
const cellSize = inputData[offset];
let left = offset + 1;
let right = offset + cellSize;
// reverse segment
while (left < right) {
const l = inputData[left];
const r = inputData[right];

outputData[left++] = r;
outputData[right--] = l;
}
offset += cellSize + 1;
}

return vtkCellArray.newInstance({
values: outputData,
numberOfComponents: 1,
});
}

function reverseNormals(inputNormals) {
if (!inputNormals) {
return null;
}

const values = inputNormals.getData().slice();
for (let i = 0; i < values.length; i++) {
const v = -values[i];
values[i] = v === 0 ? 0 : v; // avoids -0
}

return vtkDataArray.newInstance({
name: inputNormals.getName(),
values,
numberOfComponents: inputNormals.getNumberOfComponents(),
});
}

// ----------------------------------------------------------------------------
// vtkReverseSense methods
// ----------------------------------------------------------------------------

function vtkReverseSense(publicAPI, model) {
model.classHierarchy.push('vtkReverseSense');

publicAPI.requestData = (inData, outData) => {
const input = inData[0];

if (!input) {
vtkErrorMacro('No input!');
return;
}

const output = outData[0]?.initialize() || vtkPolyData.newInstance();

output.setPoints(input.getPoints());

output.setVerts(
model.reverseCells ? reverseCellArray(input.getVerts()) : input.getVerts()
);
output.setLines(
model.reverseCells ? reverseCellArray(input.getLines()) : input.getLines()
);
output.setPolys(
model.reverseCells ? reverseCellArray(input.getPolys()) : input.getPolys()
);
output.setStrips(
model.reverseCells
? reverseCellArray(input.getStrips())
: input.getStrips()
);

const outPointData = output.getPointData();
const outCellData = output.getCellData();

outPointData.passData(input.getPointData());
outCellData.passData(input.getCellData());
output.getFieldData().passData(input.getFieldData());

if (model.reverseNormals) {
const pointNormals = reverseNormals(input.getPointData().getNormals());
const cellNormals = reverseNormals(input.getCellData().getNormals());

if (pointNormals) {
outPointData.setNormals(pointNormals);
}

if (cellNormals) {
outCellData.setNormals(cellNormals);
}
}

outData[0] = output;
};
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
reverseCells: true,
reverseNormals: false,
};

// ----------------------------------------------------------------------------

export function extend(publicAPI, model, initialValues = {}) {
Object.assign(model, DEFAULT_VALUES, initialValues);

macro.obj(publicAPI, model);
macro.algo(publicAPI, model, 1, 1);

macro.setGet(publicAPI, model, ['reverseCells', 'reverseNormals']);

vtkReverseSense(publicAPI, model);
}

// ----------------------------------------------------------------------------

export const newInstance = macro.newInstance(extend, 'vtkReverseSense');

// ----------------------------------------------------------------------------

export default { newInstance, extend };
Loading
Loading