From c3eafb97ebe3db3f004558892ca8c3756a7f3ba0 Mon Sep 17 00:00:00 2001 From: Adnane Belmadiaf Date: Mon, 11 Aug 2025 21:41:43 +0200 Subject: [PATCH] feat(PlatonicSolidSource): add vtkPlatonicSolidSource --- .../docs/gallery/PlatonicSolidSource.jpg | Bin 0 -> 4222 bytes Documentation/content/examples/index.md | 2 + .../PlatonicSolidSource/Constants.d.ts | 12 + .../Sources/PlatonicSolidSource/Constants.js | 11 + .../example/controlPanel.html | 14 + .../PlatonicSolidSource/example/index.js | 67 +++++ .../Sources/PlatonicSolidSource/index.d.ts | 109 +++++++ .../Sources/PlatonicSolidSource/index.js | 276 +++++++++++++++++ .../PlatonicSolidSource/test/testCube.png | Bin 0 -> 6154 bytes .../test/testDodecahedron.png | Bin 0 -> 7464 bytes .../test/testIcosahedron.png | Bin 0 -> 8043 bytes .../test/testOctahedron.png | Bin 0 -> 6267 bytes .../test/testPlatonicSolid.js | 284 ++++++++++++++++++ .../test/testTetrahedron.png | Bin 0 -> 7091 bytes Sources/Filters/Sources/index.js | 2 + 15 files changed, 777 insertions(+) create mode 100644 Documentation/content/docs/gallery/PlatonicSolidSource.jpg create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/Constants.d.ts create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/Constants.js create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/example/controlPanel.html create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/example/index.js create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/index.d.ts create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/index.js create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/test/testCube.png create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/test/testDodecahedron.png create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/test/testIcosahedron.png create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/test/testOctahedron.png create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/test/testPlatonicSolid.js create mode 100644 Sources/Filters/Sources/PlatonicSolidSource/test/testTetrahedron.png diff --git a/Documentation/content/docs/gallery/PlatonicSolidSource.jpg b/Documentation/content/docs/gallery/PlatonicSolidSource.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3082388d1acd2535ebe904fca32e213278d74f59 GIT binary patch literal 4222 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 literal 0 HcmV?d00001 diff --git a/Documentation/content/examples/index.md b/Documentation/content/examples/index.md index 4e81c621d59..433bca0e13b 100644 --- a/Documentation/content/examples/index.md +++ b/Documentation/content/examples/index.md @@ -144,6 +144,7 @@ This will allow you to see the some live code running in your browser. Just pick [![LineSource Example][LineSource]](./LineSource.html "LineSource") [![PlaneSource Example][PlaneSource]](./PlaneSource.html "PlaneSource") [![PointSource Example][PointSource]](./PointSource.html "PointSource") +[![PlatonicSolidSource Example][PlatonicSolidSource]](./PlatonicSolidSource.html "PlatonicSolidSource") [![SLICSource Example][SLICSource]](./SLICSource.html "SLICSource") [![SphereSource Example][SphereSource]](./SphereSource.html "SphereSource") [![WarpScalar Example][WarpScalargif]](./WarpScalar.html "WarpScalar") @@ -162,6 +163,7 @@ This will allow you to see the some live code running in your browser. Just pick [LineSource]: ../docs/gallery/LineSource.jpg [PlaneSource]: ../docs/gallery/PlaneSource.jpg [PointSource]: ../docs/gallery/PointSource.jpg +[PlatonicSolidSource]: ../docs/gallery/PlatonicSolidSource.jpg [SLICSource]: ../docs/gallery/SLICSource.jpg [SphereSource]: ../docs/gallery/SphereSource.gif [WarpScalargif]: ../docs/gallery/WarpScalar.gif diff --git a/Sources/Filters/Sources/PlatonicSolidSource/Constants.d.ts b/Sources/Filters/Sources/PlatonicSolidSource/Constants.d.ts new file mode 100644 index 00000000000..9b69e18a3d9 --- /dev/null +++ b/Sources/Filters/Sources/PlatonicSolidSource/Constants.d.ts @@ -0,0 +1,12 @@ +export declare enum SolidType { + VTK_SOLID_TETRAHEDRON = 0, + VTK_SOLID_CUBE = 1, + VTK_SOLID_OCTAHEDRON = 2, + VTK_SOLID_ICOSAHEDRON = 3, + VTK_SOLID_DODECAHEDRON = 4, +} + +declare const _default: { + SolidType: typeof SolidType; +}; +export default _default; diff --git a/Sources/Filters/Sources/PlatonicSolidSource/Constants.js b/Sources/Filters/Sources/PlatonicSolidSource/Constants.js new file mode 100644 index 00000000000..d2e82518246 --- /dev/null +++ b/Sources/Filters/Sources/PlatonicSolidSource/Constants.js @@ -0,0 +1,11 @@ +export const SolidType = { + VTK_SOLID_TETRAHEDRON: 0, + VTK_SOLID_CUBE: 1, + VTK_SOLID_OCTAHEDRON: 2, + VTK_SOLID_ICOSAHEDRON: 3, + VTK_SOLID_DODECAHEDRON: 4, +}; + +export default { + SolidType, +}; diff --git a/Sources/Filters/Sources/PlatonicSolidSource/example/controlPanel.html b/Sources/Filters/Sources/PlatonicSolidSource/example/controlPanel.html new file mode 100644 index 00000000000..0506bcb182e --- /dev/null +++ b/Sources/Filters/Sources/PlatonicSolidSource/example/controlPanel.html @@ -0,0 +1,14 @@ + + + + + +
Solid Type + +
diff --git a/Sources/Filters/Sources/PlatonicSolidSource/example/index.js b/Sources/Filters/Sources/PlatonicSolidSource/example/index.js new file mode 100644 index 00000000000..88bcb618a0d --- /dev/null +++ b/Sources/Filters/Sources/PlatonicSolidSource/example/index.js @@ -0,0 +1,67 @@ +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 vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow'; +import vtkActor from '@kitware/vtk.js/Rendering/Core/Actor'; +import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper'; +import vtkPlatonicSolidSource from '@kitware/vtk.js/Filters/Sources/PlatonicSolidSource'; +import { SolidType } from '@kitware/vtk.js/Filters/Sources/PlatonicSolidSource/Constants'; + +import controlPanel from './controlPanel.html'; + +// ---------------------------------------------------------------------------- +// Standard rendering code setup +// ---------------------------------------------------------------------------- + +const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance(); +const renderer = fullScreenRenderer.getRenderer(); +const renderWindow = fullScreenRenderer.getRenderWindow(); + +// ---------------------------------------------------------------------------- +// Example code +// ---------------------------------------------------------------------------- +const platonicSolidSource = vtkPlatonicSolidSource.newInstance({ + solidType: SolidType.VTK_SOLID_DODECAHEDRON, +}); + +const mapper = vtkMapper.newInstance(); +const actor = vtkActor.newInstance(); + +mapper.setInputConnection(platonicSolidSource.getOutputPort()); +actor.setMapper(mapper); + +renderer.addActor(actor); +renderer.resetCamera(); +renderWindow.render(); + +// ----------------------------------------------------------- +// UI control handling +// ----------------------------------------------------------- + +fullScreenRenderer.addController(controlPanel); + +const solidTypeSelect = document.querySelector('select[name="solidType"]'); +solidTypeSelect.addEventListener('change', (event) => { + const solidType = event.target.value; + platonicSolidSource.setSolidType(SolidType[solidType.toUpperCase()]); + renderWindow.render(); +}); + +// Set the initial value of the select element +solidTypeSelect.value = 'VTK_SOLID_DODECAHEDRON'; + +// Render the initial state +renderWindow.render(); + +// ----------------------------------------------------------- +// Make some variables global so that you can inspect and +// modify objects in your browser's developer console: +// ----------------------------------------------------------- + +global.platonicSolidSource = platonicSolidSource; +global.mapper = mapper; +global.actor = actor; +global.renderer = renderer; +global.renderWindow = renderWindow; diff --git a/Sources/Filters/Sources/PlatonicSolidSource/index.d.ts b/Sources/Filters/Sources/PlatonicSolidSource/index.d.ts new file mode 100644 index 00000000000..659e4de6e84 --- /dev/null +++ b/Sources/Filters/Sources/PlatonicSolidSource/index.d.ts @@ -0,0 +1,109 @@ +import { vtkAlgorithm, vtkObject } from '../../../interfaces'; +import { DesiredOutputPrecision } from '../../../Common/DataModel/DataSetAttributes'; +import { SolidType } from './Constants'; + +/** + * + */ +export interface IPlatonicSolidSourceInitialValues { + solidType?: SolidType; + outputPointsPrecision?: DesiredOutputPrecision; + scale?: number; +} + +type vtkPlatonicSolidSourceBase = vtkObject & + Omit< + vtkAlgorithm, + | 'getInputData' + | 'setInputData' + | 'setInputConnection' + | 'getInputConnection' + | 'addInputConnection' + | 'addInputData' + >; + +export interface vtkPlatonicSolidSource extends vtkPlatonicSolidSourceBase { + /** + * Get the desired output precision. + * @returns {DesiredOutputPrecision} + */ + getOutputPointsPrecision(): DesiredOutputPrecision; + + /** + * Get the scale factor of the source. + */ + getScale(): number; + + /** + * Get the solid type of the source. + * @returns {SolidType} + */ + getSolidType(): SolidType; + + /** + * Request data for the source. + * @param inData + * @param outData + */ + requestData(inData: any, outData: any): void; + + /** + * Set the desired output precision. + * @param {DesiredOutputPrecision} outputPointsPrecision + */ + setOutputPointsPrecision( + outputPointsPrecision: DesiredOutputPrecision + ): boolean; + + /** + * Set the scale factor of the source. + * @param {Number} scale The scale factor. + */ + setScale(scale: number): boolean; + + /** + * Set the solid type of the source. + * @param {SolidType} solidType + */ + setSolidType(solidType: SolidType): boolean; +} + +/** + * Method used to decorate a given object (publicAPI+model) with vtkPlatonicSolidSource characteristics. + * + * @param publicAPI object on which methods will be bounds (public) + * @param model object on which data structure will be bounds (protected) + * @param {IPlatonicSolidSourceInitialValues} [initialValues] (default: {}) + */ +export function extend( + publicAPI: object, + model: object, + initialValues?: IPlatonicSolidSourceInitialValues +): void; + +/** + * Method used to create a new instance of vtkPlatonicSolidSource. + * @param {IPlatonicSolidSourceInitialValues} [initialValues] for pre-setting some of its content + */ +export function newInstance( + initialValues?: IPlatonicSolidSourceInitialValues +): vtkPlatonicSolidSource; + +/** + * vtkPlatonicSolidSource can generate each of the five Platonic solids: + * tetrahedron, cube, octahedron, icosahedron, and dodecahedron. Each of the + * solids is placed inside a sphere centered at the origin with radius 1.0. + * + * @example + * ```js + * import vtkPlatonicSolidSource from '@kitware/vtk.js/Filters/Sources/RegularPolygonSource'; + * + * const regularPolygonSource = vtkPlatonicSolidSource.newInstance(); + * const polydata = regularPolygonSource.getOutputData(); + * ``` + */ +export declare const vtkPlatonicSolidSource: { + newInstance: typeof newInstance; + extend: typeof extend; +}; +export default vtkPlatonicSolidSource; diff --git a/Sources/Filters/Sources/PlatonicSolidSource/index.js b/Sources/Filters/Sources/PlatonicSolidSource/index.js new file mode 100644 index 00000000000..6eb7c485c24 --- /dev/null +++ b/Sources/Filters/Sources/PlatonicSolidSource/index.js @@ -0,0 +1,276 @@ +import macro from 'vtk.js/Sources/macros'; +import vtkPolyData from 'vtk.js/Sources/Common/DataModel/PolyData'; +import vtkPoints from 'vtk.js/Sources/Common/Core/Points'; +import vtkCellArray from 'vtk.js/Sources/Common/Core/CellArray'; +import { SolidType } from 'vtk.js/Sources/Filters/Sources/PlatonicSolidSource/Constants'; +import { DesiredOutputPrecision } from 'vtk.js/Sources/Common/DataModel/DataSetAttributes/Constants'; +import { VtkDataTypes } from 'vtk.js/Sources/Common/Core/DataArray/Constants'; + +const a = 0.61803398875; +const b = 0.38196601125; +const c = 0.5; +const d = 0.30901699; +const e = Math.sqrt(2); +const f = Math.sqrt(3.0); + +const geometries = { + tetrahedron: { + points: [1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1], + cells: [0, 2, 1, 1, 2, 3, 0, 3, 2, 0, 1, 3], + numPoints: 4, + cellSize: 3, + numCells: 4, + scale: 1.0 / f, + }, + + cube: { + points: [ + -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, 1, 1, + -1, 1, 1, + ], + cells: [ + 0, 1, 5, 4, 0, 4, 7, 3, 4, 5, 6, 7, 3, 7, 6, 2, 1, 2, 6, 5, 0, 3, 2, 1, + ], + numPoints: 8, + cellSize: 4, + numCells: 6, + scale: 1.0 / f, + }, + + octahedron: { + points: [-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0, 0, 0, -e, 0, 0, e], + cells: [ + 4, 1, 0, 4, 2, 1, 4, 3, 2, 4, 0, 3, 0, 1, 5, 1, 2, 5, 2, 3, 5, 3, 0, 5, + ], + numPoints: 6, + cellSize: 3, + numCells: 8, + scale: 1.0 / e, + }, + + icosahedron: { + points: [ + 0, + d, + -c, + 0, + d, + c, + 0, + -d, + c, + -d, + c, + 0, + -d, + -c, + 0, + d, + c, + 0, + d, + -c, + 0, + 0, + -d, + -c, + c, + 0, + d, + -c, + 0, + d, + -c, + 0, + -d, + c, + 0, + -d, + ], + cells: [ + 0, 3, 5, 1, 5, 3, 1, 9, 2, 1, 2, 8, 0, 11, 7, 0, 7, 10, 2, 4, 6, 7, 6, 4, + 3, 10, 9, 4, 9, 10, 5, 8, 11, 6, 11, 8, 1, 3, 9, 1, 8, 5, 0, 10, 3, 0, 5, + 11, 7, 4, 10, 7, 11, 6, 2, 9, 4, 2, 6, 8, + ], + numPoints: 12, + cellSize: 3, + numCells: 20, + scale: 1.0 / 0.58778524999243, + }, + + dodecahedron: { + points: [ + b, + 0, + 1, + -b, + 0, + 1, + b, + 0, + -1, + -b, + 0, + -1, + 0, + 1, + -b, + 0, + 1, + b, + 0, + -1, + -b, + 0, + -1, + b, + 1, + b, + 0, + 1, + -b, + 0, + -1, + b, + 0, + -1, + -b, + 0, + -a, + a, + a, + a, + -a, + a, + -a, + -a, + -a, + a, + a, + -a, + a, + a, + a, + -a, + a, + -a, + -a, + -a, + a, + a, + -a, + -a, + ], + cells: [ + 0, 16, 5, 12, 1, 1, 18, 7, 13, 0, 2, 19, 6, 14, 3, 3, 17, 4, 15, 2, 4, 5, + 16, 8, 15, 5, 4, 17, 10, 12, 6, 7, 18, 11, 14, 7, 6, 19, 9, 13, 8, 16, 0, + 13, 9, 9, 19, 2, 15, 8, 10, 17, 3, 14, 11, 11, 18, 1, 12, 10, + ], + numPoints: 20, + cellSize: 5, + numCells: 12, + scale: 1.0 / 1.070466269319, + }, +}; + +// ---------------------------------------------------------------------------- +// vtkPlatonicSolidSource methods +// ---------------------------------------------------------------------------- + +function vtkPlatonicSolidSource(publicAPI, model) { + // Set our className + model.classHierarchy.push('vtkPlatonicSolidSource'); + + publicAPI.requestData = (inData, outData) => { + const output = outData[0] || vtkPolyData.newInstance(); + + let solidData; + switch (model.solidType) { + case SolidType.VTK_SOLID_TETRAHEDRON: + solidData = geometries.tetrahedron; + break; + case SolidType.VTK_SOLID_CUBE: + solidData = geometries.cube; + break; + case SolidType.VTK_SOLID_OCTAHEDRON: + solidData = geometries.octahedron; + break; + case SolidType.VTK_SOLID_ICOSAHEDRON: + solidData = geometries.icosahedron; + break; + case SolidType.VTK_SOLID_DODECAHEDRON: + solidData = geometries.dodecahedron; + break; + default: + solidData = geometries.tetrahedron; + break; + } + + let pointType; + if (model.outputPointsPrecision === DesiredOutputPrecision.SINGLE) { + pointType = VtkDataTypes.FLOAT; + } else if (model.outputPointsPrecision === DesiredOutputPrecision.DOUBLE) { + pointType = VtkDataTypes.DOUBLE; + } + + const points = vtkPoints.newInstance({ + dataType: pointType, + numberOfPoints: solidData.numPoints, + }); + + for (let i = 0; i < solidData.points.length; i += 3) { + points.insertNextPoint( + solidData.scale * solidData.points[i] * model.scale, + solidData.scale * solidData.points[i + 1] * model.scale, + solidData.scale * solidData.points[i + 2] * model.scale + ); + } + + const polys = vtkCellArray.newInstance(); + for (let i = 0; i < solidData.cells.length; i += solidData.cellSize) { + const cell = solidData.cells.slice(i, i + solidData.cellSize); + polys.insertNextCell(cell); + } + + output.setPoints(points); + output.setPolys(polys); + + outData[0] = output; + }; +} + +// ---------------------------------------------------------------------------- +// Object factory +// ---------------------------------------------------------------------------- + +const DEFAULT_VALUES = { + solidType: SolidType.VTK_SOLID_TETRAHEDRON, + outputPointsPrecision: DesiredOutputPrecision.DEFAULT, + scale: 1, +}; + +// ---------------------------------------------------------------------------- + +export function extend(publicAPI, model, initialValues = {}) { + Object.assign(model, DEFAULT_VALUES, initialValues); + + // Build VTK API + macro.obj(publicAPI, model); + macro.setGet(publicAPI, model, [ + 'solidType', + 'outputPointsPrecision', + 'scale', + ]); + macro.algo(publicAPI, model, 0, 1); + + // Object methods + vtkPlatonicSolidSource(publicAPI, model); +} + +// ---------------------------------------------------------------------------- + +export const newInstance = macro.newInstance(extend, 'vtkPlatonicSolidSource'); + +// ---------------------------------------------------------------------------- + +export default { newInstance, extend, SolidType }; diff --git a/Sources/Filters/Sources/PlatonicSolidSource/test/testCube.png b/Sources/Filters/Sources/PlatonicSolidSource/test/testCube.png new file mode 100644 index 0000000000000000000000000000000000000000..b7f1fec3618f122f346a7cf2e5e4b1e753dd85dd GIT binary patch literal 6154 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 VR!6_P2*Z6j`?U@>*R0)R{tYj{uY~{r literal 0 HcmV?d00001 diff --git a/Sources/Filters/Sources/PlatonicSolidSource/test/testDodecahedron.png b/Sources/Filters/Sources/PlatonicSolidSource/test/testDodecahedron.png new file mode 100644 index 0000000000000000000000000000000000000000..fe44ba34f24ccfa98e6f8569807912ec09a822b8 GIT binary patch literal 7464 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 literal 0 HcmV?d00001 diff --git a/Sources/Filters/Sources/PlatonicSolidSource/test/testIcosahedron.png b/Sources/Filters/Sources/PlatonicSolidSource/test/testIcosahedron.png new file mode 100644 index 0000000000000000000000000000000000000000..104e7e53fba1ace262938ed92f4e54ac569bee8a GIT binary patch literal 8043 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 literal 0 HcmV?d00001 diff --git a/Sources/Filters/Sources/PlatonicSolidSource/test/testOctahedron.png b/Sources/Filters/Sources/PlatonicSolidSource/test/testOctahedron.png new file mode 100644 index 0000000000000000000000000000000000000000..fe9ed35b7afd0a501f4f79b23c60e19b4a5b0a10 GIT binary patch literal 6267 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 V>YJeRz`qHQ=K(L*a+hCz{}0medRYJf literal 0 HcmV?d00001 diff --git a/Sources/Filters/Sources/PlatonicSolidSource/test/testPlatonicSolid.js b/Sources/Filters/Sources/PlatonicSolidSource/test/testPlatonicSolid.js new file mode 100644 index 00000000000..3e90dc18123 --- /dev/null +++ b/Sources/Filters/Sources/PlatonicSolidSource/test/testPlatonicSolid.js @@ -0,0 +1,284 @@ +import test from 'tape'; +import testUtils from 'vtk.js/Sources/Testing/testUtils'; + +import 'vtk.js/Sources/Rendering/Misc/RenderingAPIs'; +import vtkRenderWindow from 'vtk.js/Sources/Rendering/Core/RenderWindow'; +import vtkRenderer from 'vtk.js/Sources/Rendering/Core/Renderer'; +import vtkPlatonicSolidSource from 'vtk.js/Sources/Filters/Sources/PlatonicSolidSource'; +import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; +import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; +import { SolidType } from 'vtk.js/Sources/Filters/Sources/PlatonicSolidSource/Constants'; + +import baseline1 from './testTetrahedron.png'; +import baseline2 from './testCube.png'; +import baseline3 from './testOctahedron.png'; +import baseline4 from './testIcosahedron.png'; +import baseline5 from './testDodecahedron.png'; + +test.onlyIfWebGL('Test vtkPlatonicSolidSource Tetrahedron Rendering', (t) => { + const gc = testUtils.createGarbageCollector(); + t.ok('rendering', 'vtkPlatonicSolidSource Rendering'); + + // Create some control UI + const container = document.querySelector('body'); + const renderWindowContainer = gc.registerDOMElement( + document.createElement('div') + ); + container.appendChild(renderWindowContainer); + + // create what we will view + const renderWindow = gc.registerResource(vtkRenderWindow.newInstance()); + const renderer = gc.registerResource(vtkRenderer.newInstance()); + renderWindow.addRenderer(renderer); + renderer.setBackground(0.32, 0.34, 0.43); + + const actor = gc.registerResource(vtkActor.newInstance()); + + renderer.addActor(actor); + + const mapper = gc.registerResource(vtkMapper.newInstance()); + actor.setMapper(mapper); + + const regularPolygonSource = gc.registerResource( + vtkPlatonicSolidSource.newInstance() + ); + mapper.setInputConnection(regularPolygonSource.getOutputPort()); + actor.rotateX(-30); + + // now create something to view it, in this case webgl + const glwindow = gc.registerResource(renderWindow.newAPISpecificView()); + glwindow.setContainer(renderWindowContainer); + renderWindow.addView(glwindow); + glwindow.setSize(400, 400); + + const promise = glwindow + .captureNextImage() + .then((image) => + testUtils.compareImages( + image, + [baseline1], + 'Filters/Sources/platonicSolidSource/testTetrahedron', + t, + 2.5 + ) + ) + .finally(gc.releaseResources); + renderWindow.render(); + return promise; +}); + +test.onlyIfWebGL('Test vtkPlatonicSolidSource Cube Rendering', (t) => { + const gc = testUtils.createGarbageCollector(); + t.ok('rendering', 'vtkPlatonicSolidSource Rendering'); + + // Create some control UI + const container = document.querySelector('body'); + const renderWindowContainer = gc.registerDOMElement( + document.createElement('div') + ); + container.appendChild(renderWindowContainer); + + // create what we will view + const renderWindow = gc.registerResource(vtkRenderWindow.newInstance()); + const renderer = gc.registerResource(vtkRenderer.newInstance()); + renderWindow.addRenderer(renderer); + renderer.setBackground(0.32, 0.34, 0.43); + + const actor = gc.registerResource(vtkActor.newInstance()); + + renderer.addActor(actor); + + const mapper = gc.registerResource(vtkMapper.newInstance()); + actor.setMapper(mapper); + + const regularPolygonSource = gc.registerResource( + vtkPlatonicSolidSource.newInstance({ + solidType: SolidType.VTK_SOLID_CUBE, + }) + ); + mapper.setInputConnection(regularPolygonSource.getOutputPort()); + actor.rotateX(-30); + + // now create something to view it, in this case webgl + const glwindow = gc.registerResource(renderWindow.newAPISpecificView()); + glwindow.setContainer(renderWindowContainer); + renderWindow.addView(glwindow); + glwindow.setSize(400, 400); + + const promise = glwindow + .captureNextImage() + .then((image) => + testUtils.compareImages( + image, + [baseline2], + 'Filters/Sources/platonicSolidSource/testCube', + t, + 2.5 + ) + ) + .finally(gc.releaseResources); + renderWindow.render(); + return promise; +}); + +test.onlyIfWebGL('Test vtkPlatonicSolidSource Octahedron Rendering', (t) => { + const gc = testUtils.createGarbageCollector(); + t.ok('rendering', 'vtkPlatonicSolidSource Rendering'); + + // Create some control UI + const container = document.querySelector('body'); + const renderWindowContainer = gc.registerDOMElement( + document.createElement('div') + ); + container.appendChild(renderWindowContainer); + + // create what we will view + const renderWindow = gc.registerResource(vtkRenderWindow.newInstance()); + const renderer = gc.registerResource(vtkRenderer.newInstance()); + renderWindow.addRenderer(renderer); + renderer.setBackground(0.32, 0.34, 0.43); + + const actor = gc.registerResource(vtkActor.newInstance()); + + renderer.addActor(actor); + + const mapper = gc.registerResource(vtkMapper.newInstance()); + actor.setMapper(mapper); + + const regularPolygonSource = gc.registerResource( + vtkPlatonicSolidSource.newInstance({ + solidType: SolidType.VTK_SOLID_OCTAHEDRON, + }) + ); + mapper.setInputConnection(regularPolygonSource.getOutputPort()); + actor.rotateX(-30); + + // now create something to view it, in this case webgl + const glwindow = gc.registerResource(renderWindow.newAPISpecificView()); + glwindow.setContainer(renderWindowContainer); + renderWindow.addView(glwindow); + glwindow.setSize(400, 400); + + const promise = glwindow + .captureNextImage() + .then((image) => + testUtils.compareImages( + image, + [baseline3], + 'Filters/Sources/platonicSolidSource/testOctahedron', + t, + 2.5 + ) + ) + .finally(gc.releaseResources); + renderWindow.render(); + return promise; +}); + +test.onlyIfWebGL('Test vtkPlatonicSolidSource Icosahedron Rendering', (t) => { + const gc = testUtils.createGarbageCollector(); + t.ok('rendering', 'vtkPlatonicSolidSource Rendering'); + + // Create some control UI + const container = document.querySelector('body'); + const renderWindowContainer = gc.registerDOMElement( + document.createElement('div') + ); + container.appendChild(renderWindowContainer); + + // create what we will view + const renderWindow = gc.registerResource(vtkRenderWindow.newInstance()); + const renderer = gc.registerResource(vtkRenderer.newInstance()); + renderWindow.addRenderer(renderer); + renderer.setBackground(0.32, 0.34, 0.43); + + const actor = gc.registerResource(vtkActor.newInstance()); + + renderer.addActor(actor); + + const mapper = gc.registerResource(vtkMapper.newInstance()); + actor.setMapper(mapper); + + const regularPolygonSource = gc.registerResource( + vtkPlatonicSolidSource.newInstance({ + solidType: SolidType.VTK_SOLID_ICOSAHEDRON, + }) + ); + mapper.setInputConnection(regularPolygonSource.getOutputPort()); + actor.rotateX(-30); + + // now create something to view it, in this case webgl + const glwindow = gc.registerResource(renderWindow.newAPISpecificView()); + glwindow.setContainer(renderWindowContainer); + renderWindow.addView(glwindow); + glwindow.setSize(400, 400); + + const promise = glwindow + .captureNextImage() + .then((image) => + testUtils.compareImages( + image, + [baseline4], + 'Filters/Sources/platonicSolidSource/testIcosahedron', + t, + 2.5 + ) + ) + .finally(gc.releaseResources); + renderWindow.render(); + return promise; +}); + +test.onlyIfWebGL('Test vtkPlatonicSolidSource Dodecahedron Rendering', (t) => { + const gc = testUtils.createGarbageCollector(); + t.ok('rendering', 'vtkPlatonicSolidSource Rendering'); + + // Create some control UI + const container = document.querySelector('body'); + const renderWindowContainer = gc.registerDOMElement( + document.createElement('div') + ); + container.appendChild(renderWindowContainer); + + // create what we will view + const renderWindow = gc.registerResource(vtkRenderWindow.newInstance()); + const renderer = gc.registerResource(vtkRenderer.newInstance()); + renderWindow.addRenderer(renderer); + renderer.setBackground(0.32, 0.34, 0.43); + + const actor = gc.registerResource(vtkActor.newInstance()); + + renderer.addActor(actor); + + const mapper = gc.registerResource(vtkMapper.newInstance()); + actor.setMapper(mapper); + + const regularPolygonSource = gc.registerResource( + vtkPlatonicSolidSource.newInstance({ + solidType: SolidType.VTK_SOLID_DODECAHEDRON, + }) + ); + mapper.setInputConnection(regularPolygonSource.getOutputPort()); + actor.rotateX(-30); + + // now create something to view it, in this case webgl + const glwindow = gc.registerResource(renderWindow.newAPISpecificView()); + glwindow.setContainer(renderWindowContainer); + renderWindow.addView(glwindow); + glwindow.setSize(400, 400); + + const promise = glwindow + .captureNextImage() + .then((image) => + testUtils.compareImages( + image, + [baseline5], + 'Filters/Sources/platonicSolidSource/testDodecahedron', + t, + 2.5 + ) + ) + .finally(gc.releaseResources); + renderWindow.render(); + return promise; +}); diff --git a/Sources/Filters/Sources/PlatonicSolidSource/test/testTetrahedron.png b/Sources/Filters/Sources/PlatonicSolidSource/test/testTetrahedron.png new file mode 100644 index 0000000000000000000000000000000000000000..794325d8264f311776f5a8a72f1688f582ca6a02 GIT binary patch literal 7091 zcmeHs`#V&7^#9m)+0!Y;=q8Mf?H0O_63XTbW{yM`w+LmBa=%|<>{3IDK`0z5imomx z*P^7EL03m9Q7&DOOD-Yd^PW1-^Zot-pXd4fa(;L}dt1-!wccyJU+eW=Ye}%)X})a9 znk6I>X&IA2-%TQkaD@w53?#92n_Iv`Bxtv}3F+2T*?tm9#)nBa-W%>b)LEJ&XC8U7 zyX*NXR}BY2!a0YwnvC{rW*%nDJ7)>tP(`;YMx$r=Aof5C}pThrQ_t6QMA+Osq5`5y$nm&wcEj`14S5X}NxVK|wEx=Q2=0xHxYvfW5Nfnatn zFBK^#>(v8fq1;*fVHw7`HFk>>xHi*E&0N`V%zS_A#JG#m+z+>^DRRr4{Z7;^M+TD4 zH?P4jkO`izTw%;sf98hbl8omA3!22l2oAvyrNVc}UVd*tdpps1-C`jpYPxmQ5r>$2 zGm4OM75|;Gx*r41CF1+G)cS6N9C*(T!6%hBnA0xHH(MDiah-SxzeATTbxtYij(jtj zHNGZW6GtsKIKsp<{3$;FyT*05wZ7=LXM(x7-vKS!`Jq(4d7JZ%$5#8c-4%hZI*(kD zqVlhVTPES&P>`RT<9owlHA%+T@xYv<-|B4;O-bH?2XEz@qeZXocAH`%1yPG2^m$VK z?f~z}(x3k%<<*715kHx3D}{2~5X)Fx9J2UWIysfZDUxI)-44!7{jDgGthvJc`iLA{ zR@vk)ieOI4kb*S_Ptc2O>Mgk|!}xpH@rem~li?^vn86u8?S}&jY=uU>f>&>LK8bH$ z>>Ua3qpIBtDW3=7)v{^~JtD~u^?q_1*MTnge3@$+Sk1Mwf0DE#{RC35#4HVc&Z#$+ ztth?`^JDMw3q0WP%Cq>X!(TVY26T9;KI%k(##}5YYf#8?R zD;g>GR<1Yo#L1CtO?Es)+%S^4>4|b*J-W%k6^J!G@!y*Uwl9Iy4Oh!?07$1Z%v{9@ zU89UB_B?-VbjgvY%=oFL#G^sFA{?V^-TO-NTNKC6-c2^}7a~@l#4Db834=!+J5Av!mSnmInFvxOVwOgc&4weg4sLYJ2a^-k5Yab;MPQasMes zJB?cgup-?-`WiE|ZUgbQhp(CZXDCEIYHvkX;vW5*X-E@FojfAN>1pNMQa+_$P;}A?Qm+bPJ$G*-9+|{i^I&~)KOI4RE7If?ya#a zqziTyp8bM-`>2Gw38=FAJR+0-|8hh(4BR0kPTn!+5YbC$9A7&U#MX>kp zC4Tu!Al_O@DJ~9juNf#I{=G#F)|NWUqf}a5IHC6)lX#B@&d(`wbM7%a6Y4WS)N3zq ztG!!Y=edQulM~rk2A4s(7iSfp54_s=gBK|Of;SWOC)ykAr79zNNIB`=y|S@otbEZ9 z|9y}b(y_Y=DPPX(`WVo@FurPikQ~KJ6$(Ho;jyHMa-)^-%iNi_E66M@<6e&9^L?_u zv^qJ!XH#|w_*jU_x5aQ|yjwBI$2N0RM)vEOLX3MxBzya2K#t;UK#!RVK9in)MwbjY z8br&*<%7~tLK|n(GV(-aNVt1QwJYQmKCPi;il*aPDiVZq51Pz-h7f%-2EmzUg$4o? zh+H5Xzbwg*Fnf`Uau1#1-zF2eW22=%k6wN)?|UWjb>?UJ3A!2tQ?H31I4t!=UA!V` z#)ry3YMYXaaTz}R-DhyW04}d=Etw@hRfDXh*c)^)% zOtd-NG$ISXv`K+?7?Sjr^QF1#QW??}UHM2snCL4POZ1f9QO)(msd&?yNm(Ykt=}i)EAgkD<} zx#ixyNEbg79bJWAbj8vl4gojsoQc_>&g)2iaviULc3x#r(&l4>gGZte@yfT zPahh#l7ryg9JfISq(C=d@QKQYR~uz)eu$AV&l7O!?TU+g%$IZQY&&RMp*I;LwzsAD zwyY;L=kUEC0zGS2Nub4Zf8}mvq7BI(X`Csy>Tlu`0NA}JQom@5 zKwA^}qJ;>nCS=;4Hl-=Wc;`#AFas1cn5CbsS6em^>Exlc6TQ9RL{vQf(1yCjIzzsB zvT5zGw0`<&0LV~=kgty9>i6Dkc#=P}QQet~pL!7@$3XXgTf; z%)kS`-;Ee(=@~6OI^Mf08r(KX<$^f)B^-3iRJ*w$`O7E$Bb@SoQ9N_Z;;T%&&ZrCl zK4@Ay!c^hbc2*I6L$~&geFfk+Y<{ts4!>L!m2OHdSn@b0jWt*L&h4$U<3~ew*i|y- z$A(iYYHS4NmB6?A14x|o4)nk^d)YXArBd@YS2!TtJ$-g5tx%O)8%xp1ic04A~6_%LB{`t&Eizekmil851Mbm`feht8#c(gafJUqmd zc1FT7|07Z$`DIy#0(aR3vmEX5K@It4rQD*(^IcB&vb&t`6>qtCJ_{859!0ZS!PCjx z<>1tB$&TiaUuC$faNTPaw^^boZ`;)@UUi5?cn71$EG^InOB^U1d}V#p^dX^?W$5F6 zo?LL+F>V)3y?A1o4y!iWFzUN^WJ{CQg5@j9`a0>Kv5hFJ(B$3&s+U_oYn6wF#oX4Fp!t zhwt&!zP(8qBxOK_qh|AZ@l^8~9mx=7DB*3(j1wG~yQ@i6yY~KAso}>L*JxSzH6@?T z3dE*AJAE&Hk$)Dg=@+(A`vx7A-WRE-1deW)HO8-yD88}S&|jm1j%tX6%I-wvy8TLn z`-CpP2%p~yg3S>goLHLqy=X4aRqSR@C{m!<@_3IV!|j9Px>b|kyWM6oZhp?o#>YY# zXhhAdVnlfd@KaRo*0PGRpN-ASJhx0{fV$P9`poB~iNxd14!Y}%z6AoUhLE76(^H@{ z*MmkZ4J=yYa~xkobh%1HZ%Bue)$?obD<;#1K0Bx3W8ck%nsp=2CV$?ZA>n^c-}b=qN`29VJQC5jZHds8 zj=pVv@_i{kN8to3)cBNIyfM1y`K6`(5wmErS|J*iG$^s9wQ;cdU3PmwB*b)zDHghu zAYobZ1*YEMo(Mo#3MGu>iup!kfTue~W-B2LSl?zJ)NZJ z{JW6&dWh%dla3#oau^4Ih!`ykTb+ytX6{5E%wGTHMYzBq$sDa}R^`^uwN*xkyRWEp zeW3ygq7el1XCDD4B3bG50<`gr;o~?;%sP$(=u1}GzlZ^FuaXcY;32;!>&1WA)5`aj z-Upq1mBb<-EAE?2PyT4!ynGM?EF7(eV6JRs`Dgdame?0CJjoQk1O1{r%8i}W1n@Pl zr6AZFQ30%FwSMBc&^W0vzC>LFdb8Xw zO+YRne`-xM3ou=dn`Ry6_u;YE1lu09TuVI&3D4-G#G5LW9Z zo#DGP7Hp*WK1%F0*JnSh5kh|ad_h9A-K)gzl?~X(UakR~x;1@Wb0=!E zVO?I|XyS49^n6W!e`{g0m<&-*g8!vgc~{lhPKXE|n0J})A1P$dzBUTo*I9;40Op%B zu+NUgUs0Vl&S$@8heZ7`^J%cyw&kE2v=t!WY1eR}3YR2DWyVtJhyK5vRr{J~gGTF% zAuPNK3R9_!=i&)~FAv%%;b@z6XtuC*$g3DN#$4EQVXfWlXpQr{mOWcRQ=F7bvtXhL z`oS8){_Qt1x&bmqS42xczlk02Qe+!{_+4BqKK}^l4~XD=@l_W5@h;#fByiad0yY{d zM%QMf7`$Cf^sNVU7dG7}&S%x{7JEkpf^;AV4+OvPuM+h>gO6o0&>4fAJEGd>PYC{8 z2E6*ST?UkrGQ=5utrWVC2fGTv*q=dk8+zop%#He@8WfbfgkoT2jQ;&ce2HNS{m=on zbZ%XERh^~8T{lSj#`8NJ3Tb!XGkF%V_@~rUv$Djap({tU75h)IS0zd@=CjBco0B~f zyJNF?ZSx|aeMqR?!vIb7C<-g35Pikx!5-V)#VQumofjS*UG+U*^DlHJ8v~t0XeZ&w zwEKB=l`8k2$gPPQo^EL{_+76n`-NNwn|H0Y_0fW_NXgQjk>@mX^g-`v>Y9SC`y zd~3pk`K&t6WBB7QxnOOaTo;dpM4NV#3lPUjW0-m?HCjwDEqcNVb(`1$mfpK{@vO?f zj5;X9=@;w4>U~~=rhOZT4x03hjGBz8Ju~EO4wv12sNsI&Rd(#BFrN;9y3~(%mCQO#8#Rt2OOQKsmD%c~PNpzt-y#Lu#Ln-r`dLZig`(afa%UeZ9A4q&{3IE;w z6&cqXeVYEm6uKIa_2Zc_NO7)fA^iQQh5~n^__kkPVJamRj)F-}c7M;Ld(UHf9mv|J z5JVtfyKPNCC;6^P4KAZz=tjJaU0Ep<{z}y=&vM|GoEjU+bC#}~&Qthb2O5kRXtxHH zLbxEO6{(Q)XSx($Z~t3C62i(L6Q_f$$*i1pLo5!geJT!rytN3{-kBL%76du4OweB0 zy^wUFg@G~JSdqHLZsbaZ-N+qXk_aSyf$oTZSWd)O2sPGHg&N6_Dw$6Ge=1?Z0gh~r z!#�_9#HiU1?n8AJyj;7`(f#*;$9mSU>j46r~Uu+6r8}vG}wJD5usW)idCI@xpy< z{I!o`xGv;4FEzfZcwOOO@|L!;tRi_4sGrx>1SVtFvmglShx1mX;vNUaz^wXR=U?;F zb4It08Kc)nPpsJinl;K|LKc4Y?k@y0=LPMi3R?+d5+O!D`^(6Ve~s8dLGC8#{v+3_ z^@zQVUfM84PgUsHY07WMx)DQ63JlKn1fY<*nI{S%Ez15b7_6PS@8@R(i!rAvg2}f6 z;4J>WI1?~k?*aGVv>Y=YOvU3c991|Xxdcq$)xo-j-SOw}o=BqHvHsTxed(d{Bo z!uyms;nkPi!_I$hn%Mfse=;J