From bc152c918277cb5aa3c097bd504e96e4caf3adff Mon Sep 17 00:00:00 2001 From: Sankhesh Jhaveri Date: Tue, 7 Dec 2021 15:59:19 -0500 Subject: [PATCH 1/3] feat(Rendering): Introduce Geometry2DRepresentation --- src/core/Geometry2DRepresentation.js | 230 +++++++++++++++++++++++++++ src/core/index.js | 3 + src/index.js | 1 + usage/Geometry/PolyDataViewer.js | 96 ++++++++--- 4 files changed, 305 insertions(+), 25 deletions(-) create mode 100644 src/core/Geometry2DRepresentation.js diff --git a/src/core/Geometry2DRepresentation.js b/src/core/Geometry2DRepresentation.js new file mode 100644 index 0000000..da06eeb --- /dev/null +++ b/src/core/Geometry2DRepresentation.js @@ -0,0 +1,230 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; + +import { ViewContext, RepresentationContext, DownstreamContext } from './View'; +import { vec2Equals } from '../utils'; + +import vtkActor2D from '@kitware/vtk.js/Rendering/Core/Actor2D.js'; +import vtkMapper2D from '@kitware/vtk.js/Rendering/Core/Mapper2D.js'; +import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps.js'; +import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction.js'; +import vtkCoordinate from '@kitware/vtk.js/Rendering/Core/Coordinate.js'; +import { Coordinate } from '@kitware/vtk.js/Rendering/Core/Coordinate/Constants.js'; + +/** + * Geometry2DRepresentation is useful for rendering polydata in 2D screen space. + * It takes the following set of properties: + * - representation: ['POINTS', 'WIREFRAME', 'SURFACE'], + * - pointSize: 1, + * - color: [1,1,1], + * - opacity: 1, + */ +export default class Geometry2DRepresentation extends Component { + constructor(props) { + super(props); + + // Guard to prevent rendering if no data + this.validData = false; + this.currentVisibility = true; + + // Create vtk.js actor/mapper + this.actor = vtkActor2D.newInstance({ + visibility: false, + representationId: props.id, + }); + this.lookupTable = vtkColorTransferFunction.newInstance(); + this.transformCoordinate = vtkCoordinate.newInstance({ + coordinateSystem: Coordinate.DISPLAY, + }); + this.mapper = vtkMapper2D.newInstance({ + lookupTable: this.lookupTable, + useLookupTableScalarRange: false, + scalarVisibility: false, + transformCoordinate: this.transformCoordinate, + }); + this.actor.setMapper(this.mapper); + + this.subscriptions = []; + } + + render() { + return ( + + {(view) => { + if (!this.view) { + view.renderer.addActor2D(this.actor); + this.view = view; + } + return ( + + +
+ {this.props.children} +
+
+
+ ); + }} +
+ ); + } + + componentDidMount() { + this.update(this.props); + } + + componentDidUpdate(prevProps, prevState, snapshot) { + this.update(this.props, prevProps); + } + + componentWillUnmount() { + while (this.subscriptions.length) { + this.subscriptions.pop().unsubscribe(); + } + + if (this.view && this.view.renderer) { + this.view.renderer.removeActor(this.actor); + } + + this.actor.delete(); + this.actor = null; + + this.mapper.delete(); + this.mapper = null; + + this.lookupTable.delete(); + this.lookupTable = null; + + this.transformCoordinate.delete(); + this.transformCoordinate = null; + } + + update(props, previous) { + const { + actor, + mapper, + property, + colorMapPreset, + colorDataRange, + transformCoordinate, + } = props; + let changed = false; + + if (actor && (!previous || actor !== previous.actor)) { + changed = this.actor.set(actor) || changed; + } + if (mapper && (!previous || mapper !== previous.mapper)) { + changed = this.mapper.set(mapper) || changed; + } + if (property && (!previous || property !== previous.property)) { + changed = this.actor.getProperty().set(property) || changed; + } + + if ( + colorMapPreset && + this.lookupTable && + (!previous || colorMapPreset !== previous.colorMapPreset) + ) { + changed = true; + const preset = vtkColorMaps.getPresetByName(colorMapPreset); + this.lookupTable.applyColorMap(preset); + this.lookupTable.setMappingRange(...colorDataRange); + this.lookupTable.updateRange(); + } + + if ( + colorDataRange && + this.lookupTable && + (!previous || !vec2Equals(colorDataRange, previous.colorDataRange)) + ) { + changed = true; + this.lookupTable.setMappingRange(...colorDataRange); + this.lookupTable.updateRange(); + } + + if ( + transformCoordinate && + this.transformCoordinate && + (!previous || transformCoordinate !== previous.transformCoordinate) + ) { + changed = true; + this.transformCoordinate.set(transformCoordinate); + } + + // actor visibility + if (actor && actor.visibility !== undefined) { + this.currentVisibility = actor.visibility; + changed = + this.actor.setVisibility(this.currentVisibility && this.validData) || + changed; + } + + if (changed) { + // trigger render + this.dataChanged(); + } + } + + dataAvailable() { + if (!this.validData) { + this.validData = true; + this.actor.setVisibility(this.currentVisibility); + + // trigger render + this.dataChanged(); + } + } + + dataChanged() { + if (this.view) { + this.view.renderView(); + } + } +} + +Geometry2DRepresentation.defaultProps = { + colorMapPreset: 'erdc_rainbow_bright', + colorDataRange: [0, 1], +}; + +Geometry2DRepresentation.propTypes = { + /** + * The ID used to identify this component. + */ + id: PropTypes.string, + + /** + * Properties to set to the actor + */ + actor: PropTypes.object, + + /** + * Properties to set to the actor + */ + mapper: PropTypes.object, + + /** + * Properties to set to the actor.property + */ + property: PropTypes.object, + + /** + * Preset name for the lookup table color map + */ + colorMapPreset: PropTypes.string, + + /** + * Data range use for the colorMap + */ + colorDataRange: PropTypes.arrayOf(PropTypes.number), + + /** + * Coordinate system that the input dataset is in. + */ + transformCoordinate: PropTypes.object, + + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + ]), +}; diff --git a/src/core/index.js b/src/core/index.js index 5a3e542..e646c07 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -7,6 +7,7 @@ import vtkReader from './Reader'; import vtkShareDataSet from './ShareDataSet'; import vtkView from './View'; import vtkGeometryRepresentation from './GeometryRepresentation'; +import vtkGeometry2DRepresentation from './Geometry2DRepresentation'; import vtkGlyphRepresentation from './GlyphRepresentation'; import vtkImageData from './ImageData'; import vtkDataArray from './DataArray'; @@ -24,6 +25,7 @@ export const Reader = vtkReader; export const ShareDataSet = vtkShareDataSet; export const View = vtkView; export const GeometryRepresentation = vtkGeometryRepresentation; +export const Geometry2DRepresentation = vtkGeometry2DRepresentation; export const GlyphRepresentation = vtkGlyphRepresentation; export const ImageData = vtkImageData; export const DataArray = vtkDataArray; @@ -42,6 +44,7 @@ export default { ShareDataSet: vtkShareDataSet, View: vtkView, GeometryRepresentation: vtkGeometryRepresentation, + Geometry2DRepresentation: vtkGeometry2DRepresentation, GlyphRepresentation: vtkGlyphRepresentation, ImageData: vtkImageData, DataArray: vtkDataArray, diff --git a/src/index.js b/src/index.js index 768523a..c077c6c 100644 --- a/src/index.js +++ b/src/index.js @@ -23,6 +23,7 @@ export const Reader = Core.Reader; export const ShareDataSet = Core.ShareDataSet; export const View = Core.View; export const GeometryRepresentation = Core.GeometryRepresentation; +export const Geometry2DRepresentation = Core.Geometry2DRepresentation; export const GlyphRepresentation = Core.GlyphRepresentation; export const ImageData = Core.ImageData; export const DataArray = Core.DataArray; diff --git a/usage/Geometry/PolyDataViewer.js b/usage/Geometry/PolyDataViewer.js index 5f1d9d2..0888944 100644 --- a/usage/Geometry/PolyDataViewer.js +++ b/usage/Geometry/PolyDataViewer.js @@ -1,46 +1,92 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { View, GeometryRepresentation, PolyData } from 'react-vtk-js'; +import { + View, + GeometryRepresentation, + Geometry2DRepresentation, + PolyData, +} from 'react-vtk-js'; + +import { Representation } from '@kitware/vtk.js/Rendering/Core/Property/Constants'; +import { DisplayLocation } from '@kitware/vtk.js/Rendering/Core/Property2D/Constants'; +import { Coordinate } from '@kitware/vtk.js/Rendering/Core/Coordinate/Constants'; // React complains about unique key prop but I don't see why function Example(props) { return ( -
- +
+ + + + + + + - +
); From 22a95b7c56c75895a0a5e2d588709526fcea527c Mon Sep 17 00:00:00 2001 From: Sankhesh Jhaveri Date: Fri, 10 Dec 2021 18:15:31 -0500 Subject: [PATCH 2/3] style: Prefer double quotes in html attributes --- usage/Geometry/PolyDataViewer.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/usage/Geometry/PolyDataViewer.js b/usage/Geometry/PolyDataViewer.js index 0888944..b42d5a1 100644 --- a/usage/Geometry/PolyDataViewer.js +++ b/usage/Geometry/PolyDataViewer.js @@ -16,16 +16,16 @@ import { Coordinate } from '@kitware/vtk.js/Rendering/Core/Coordinate/Constants' function Example(props) { return (
- + Date: Thu, 16 Dec 2021 10:42:39 -0500 Subject: [PATCH 3/3] build(vtk-js): bump minimum vtk-js version for new vtkMapper2D --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 787f687..eca0932 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "semantic-release": "semantic-release" }, "peerDependencies": { - "@kitware/vtk.js": "^20.1.3", + "@kitware/vtk.js": "^21.3.0", "react": "^16.0.0" }, "devDependencies": { @@ -35,7 +35,7 @@ "@babel/plugin-transform-runtime": "^7.12.10", "@babel/preset-env": "^7.12.11", "@babel/preset-react": "^7.12.10", - "@kitware/vtk.js": "^20.1.3", + "@kitware/vtk.js": "^21.3.0", "@rollup/plugin-babel": "^5.2.2", "@rollup/plugin-commonjs": "17.0.0", "@rollup/plugin-eslint": "^8.0.1",