From 5cd1626302f2d1280b752ede9b4ed888a56c46a4 Mon Sep 17 00:00:00 2001 From: "laurenn.lam@kitware.com" Date: Thu, 7 Dec 2017 17:27:48 +0100 Subject: [PATCH] feat(TextureMapToSphere): Add the vtkTextureMapToSphere filter --- .../Filters/Texture/TextureMapToSphere/api.md | 19 ++ .../Texture/TextureMapToSphere/index.js | 164 ++++++++++++++++++ .../test/testTextureMapToSphere.js | 54 ++++++ Sources/Filters/Texture/index.js | 5 + Sources/Filters/index.js | 2 + Sources/tests.js | 1 + 6 files changed, 245 insertions(+) create mode 100644 Sources/Filters/Texture/TextureMapToSphere/api.md create mode 100644 Sources/Filters/Texture/TextureMapToSphere/index.js create mode 100644 Sources/Filters/Texture/TextureMapToSphere/test/testTextureMapToSphere.js create mode 100644 Sources/Filters/Texture/index.js diff --git a/Sources/Filters/Texture/TextureMapToSphere/api.md b/Sources/Filters/Texture/TextureMapToSphere/api.md new file mode 100644 index 00000000000..91e87b25657 --- /dev/null +++ b/Sources/Filters/Texture/TextureMapToSphere/api.md @@ -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. \ No newline at end of file diff --git a/Sources/Filters/Texture/TextureMapToSphere/index.js b/Sources/Filters/Texture/TextureMapToSphere/index.js new file mode 100644 index 00000000000..15b15675e83 --- /dev/null +++ b/Sources/Filters/Texture/TextureMapToSphere/index.js @@ -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 }; diff --git a/Sources/Filters/Texture/TextureMapToSphere/test/testTextureMapToSphere.js b/Sources/Filters/Texture/TextureMapToSphere/test/testTextureMapToSphere.js new file mode 100644 index 00000000000..bd350e5a3cf --- /dev/null +++ b/Sources/Filters/Texture/TextureMapToSphere/test/testTextureMapToSphere.js @@ -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(); +}); diff --git a/Sources/Filters/Texture/index.js b/Sources/Filters/Texture/index.js new file mode 100644 index 00000000000..a5f7634bee3 --- /dev/null +++ b/Sources/Filters/Texture/index.js @@ -0,0 +1,5 @@ +import vtkTextureMapToSphere from './TextureMapToSphere'; + +export default { + vtkTextureMapToSphere, +}; diff --git a/Sources/Filters/index.js b/Sources/Filters/index.js index 216f9d56ef7..8f115fa3589 100644 --- a/Sources/Filters/index.js +++ b/Sources/Filters/index.js @@ -1,7 +1,9 @@ import General from './General'; import Sources from './Sources'; +import Texture from './Texture'; export default { General, Sources, + Texture, }; diff --git a/Sources/tests.js b/Sources/tests.js index e1bd182a36d..14a5999b5e0 100644 --- a/Sources/tests.js +++ b/Sources/tests.js @@ -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';