Skip to content

Commit

Permalink
Merge pull request #450 from laurennlam/add_texture-map-to-sphere
Browse files Browse the repository at this point in the history
feat(TextureMapToSphere): Add the vtkTextureMapToSphere filter
  • Loading branch information
jourdain authored Dec 8, 2017
2 parents 66ed348 + 5cd1626 commit d4e30bc
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 0 deletions.
19 changes: 19 additions & 0 deletions Sources/Filters/Texture/TextureMapToSphere/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
## Introduction
Generate texture coordinates by mapping points to sphere
The TCoords DataArray is name 'Texture Coordinate'

## (Set/Get) Center
Specify a point defining the center of the sphere.

## (Set/Get) AutomaticSphereGeneration
Turn on/off automatic sphere generation.

This means it automatically finds the sphere center.

## (Set/Get) PreventSeam
Control how the texture coordinates are generated.

If PreventSeam is set, the s-coordinate ranges :

- from 0->1 and 1->0 corresponding to the theta angle variation between 0->180 and 180->0 degrees
- Otherwise, the s-coordinate ranges from 0->1 between 0->360 degrees.
164 changes: 164 additions & 0 deletions Sources/Filters/Texture/TextureMapToSphere/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import macro from 'vtk.js/Sources/macro';
import vtkDataArray from 'vtk.js/Sources/Common/Core/DataArray';
import vtkMath from 'vtk.js/Sources/Common/Core/Math';
import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData';

const { vtkErrorMacro } = macro;

// ----------------------------------------------------------------------------
// vtkTextureMapToSphere methods
// ----------------------------------------------------------------------------

function vtkTextureMapToSphere(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkTextureMapToSphere');

publicAPI.requestData = (inData, outData) => {
if (model.deleted) {
return;
}
const input = inData[0];

const nbPoints = input.getPoints().getNumberOfPoints();
if (nbPoints <= 1) {
vtkErrorMacro("Can't generate texture coordinates without points");
return;
}

const piOverTwo = Math.PI / 2;
const x = [];
const points = input.getPoints();
if (model.automaticSphereGeneration) {
model.center = [0, 0, 0];
for (let i = 0; i < nbPoints; i++) {
points.getPoint(i, x);
model.center[0] += x[0];
model.center[1] += x[1];
model.center[2] += x[2];
}
model.center[0] /= nbPoints;
model.center[1] /= nbPoints;
model.center[2] /= nbPoints;
}

let rho = 0;
let diff = 0;
let phi = 0;
const tc = [0, 0];
let r = 0;
let thetaX = 0;
let thetaY = 0;
const tcoordsData = [];
for (let i = 0; i < nbPoints; i++) {
points.getPoint(i, x);
rho = Math.sqrt(vtkMath.distance2BetweenPoints(x, model.center));
if (rho !== 0) {
diff = x[2] - model.center[2];
if (Math.abs(diff) > rho) {
phi = 0;
if (diff > 0) {
tc[1] = 0;
} else {
tc[1] = 1;
}
} else {
phi = Math.acos(diff / rho);
tc[1] = phi / Math.PI;
}
} else {
tc[1] = 0;
}

r = rho * Math.sin(phi);
if (r !== 0) {
diff = x[0] - model.center[0];
if (Math.abs(diff) > r) {
if (diff > 0) {
thetaX = 0;
} else {
thetaX = Math.PI;
}
} else {
thetaX = Math.acos(diff / r);
}

diff = x[1] - model.center[1];
if (Math.abs(diff) > r) {
if (diff > 0) {
thetaY = piOverTwo;
} else {
thetaY = -piOverTwo;
}
} else {
thetaY = Math.asin(diff / r);
}
} else {
thetaX = 0;
thetaY = 0;
}

if (model.preventSeam) {
tc[0] = thetaX / Math.PI;
} else {
tc[0] = thetaX / (2 * Math.PI);
if (thetaY < 0) {
tc[0] = 1 - tc[0];
}
}
tcoordsData.push(...tc);
}

const tCoords = vtkDataArray.newInstance({
name: 'Texture Coordinates',
numberOfComponents: 2,
size: nbPoints,
values: tcoordsData,
});

const output = vtkPolyData.newInstance();
output.getPoints().setData(new Float32Array(input.getPoints().getData()));
output.getPolys().setData(new Uint32Array(input.getPolys().getData()));
output.getPointData().setTCoords(tCoords);

// Update output
outData[0] = output;
};
}

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

const DEFAULT_VALUES = {
center: [0, 0, 0],
automaticSphereGeneration: 1,
preventSeam: 1,
};

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

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

// Build VTK API
macro.obj(publicAPI, model);

macro.setGetArray(publicAPI, model, [
'center',
]);
macro.setGet(publicAPI, model, [
'automaticSphereGeneration',
'preventSeam',
]);

macro.algo(publicAPI, model, 1, 1);
vtkTextureMapToSphere(publicAPI, model);
}

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

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

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

export default { newInstance, extend };
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import test from 'tape-catch';
import vtkCubeSource from 'vtk.js/Sources/Filters/Sources/CubeSource';
import vtkTextureMapToSphere from 'vtk.js/Sources/Filters/Texture/TextureMapToSphere';

test('Test vtkTextureMapToSphere instance', (t) => {
t.ok(vtkTextureMapToSphere, 'Make sure the class definition exists');
const instance = vtkTextureMapToSphere.newInstance();
t.ok(instance);
t.end();
});

test('Test vtkTextureMapToSphere TCoords generation', (t) => {
const cubeSource = vtkCubeSource.newInstance();
const cube = cubeSource.getOutputData();
cube.getPointData().setTCoords(null);
const sphereTextureFilter = vtkTextureMapToSphere.newInstance();
sphereTextureFilter.setInputData(cube);
sphereTextureFilter.update();

const generatedTCoords = sphereTextureFilter.getOutputData().getPointData().getTCoords().getData();
const expectedData = [
0.75, 0.695913,
0.75, 0.304087,
0.75, 0.695913,
0.75, 0.304087,
0.25, 0.695913,
0.25, 0.304087,
0.25, 0.695913,
0.25, 0.304087,
0.75, 0.695913,
0.75, 0.304087,
0.25, 0.695913,
0.25, 0.304087,
0.75, 0.695913,
0.75, 0.304087,
0.25, 0.695913,
0.25, 0.304087,
0.75, 0.695913,
0.25, 0.695913,
0.75, 0.695913,
0.25, 0.695913,
0.75, 0.304087,
0.25, 0.304087,
0.75, 0.304087,
0.25, 0.304087,
];

for (let i = 0; i < generatedTCoords.length; i++) {
const val = Math.round(generatedTCoords[i] * 1000000) / 1000000;
t.equal(val, expectedData[i]);
}

t.end();
});
5 changes: 5 additions & 0 deletions Sources/Filters/Texture/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import vtkTextureMapToSphere from './TextureMapToSphere';

export default {
vtkTextureMapToSphere,
};
2 changes: 2 additions & 0 deletions Sources/Filters/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import General from './General';
import Sources from './Sources';
import Texture from './Texture';

export default {
General,
Sources,
Texture,
};
1 change: 1 addition & 0 deletions Sources/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import './Filters/Sources/CubeSource/test/testCube';
import './Filters/Sources/LineSource/test/testLine';
import './Filters/Sources/PlaneSource/test/testPlane';
import './Filters/Sources/PointSource/test/testPointSource';
import './Filters/Texture/TextureMapToSphere/test/testTextureMapToSphere';
import './IO/Misc/PDBReader/test/testMolecule';
import './Rendering/Core/AbstractMapper/test/testAbstractMapper';
import './Rendering/Core/ColorTransferFunction/test/testColorTransferFunction';
Expand Down

0 comments on commit d4e30bc

Please sign in to comment.