├── .gitignore ├── README.md ├── dist └── index.html ├── package-lock.json ├── package.json ├── src ├── cone-filter.js ├── cone.js ├── image-slicing.js ├── volume-transfer.js ├── volume.js └── widgets.js ├── standalone.html └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist/*.js 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!CAUTION] 2 | > This repository is no longer maintained. As such, there is no guarantee that the examples will run with the latest version of vtk.js and/or nodejs. The code will be left here for reference. 3 | > 4 | > To see up-to-date examples, visit [the vtk.js examples page](https://kitware.github.io/vtk-js/examples/). The examples in this repo are mostly based on examples in the vtk.js repo. 5 | 6 | # VTK.js examples 7 | 8 | This repository contains VTK.js examples used in the VTK.js+OHIF tutorials. A 9 | link will be provided to the final slides (to be inserted). 10 | 11 | ## Examples overview 12 | 13 | There are a few examples in this repo. They are listed below: 14 | - `standalone.html`: A self-contained example that renders a cone. Useful as a playground and prototyping. 15 | - `src/cone.js`: Same result as `standalone.html`, but using a webpack build approach. Good base for 16 | further application development. 17 | - `src/cone-filter.js`: Example that demonstrates VTK.js filters. 18 | - `src/volume.js`: Example that demonstrates basic volume rendering without transfer functions. Refer to 19 | the transfer function volume example for a complete rendering example. 20 | - `src/volume-transfer.js`: Example that demonstrates basic volume rendering with transfer functions. 21 | - `src/image-slicing.js`: Example that demonstrates volume slicing and interaction. 22 | - `src/widgets.js`: Example that demonstrates a volume cropping widget. 23 | 24 | ## Building the examples 25 | 26 | For `standalone.html`, no build instructions are required. Just load it up in your browser! 27 | 28 | For the rest of the examples, you first must install the npm packages by running the following: 29 | ``` 30 | npm install 31 | ``` 32 | 33 | Once you've run that, you can now run one of the example projects: 34 | - `npm run dev:cone` 35 | - `npm run dev:cone-filter` 36 | - `npm run dev:volume` 37 | - `npm run dev:volume-transfer` 38 | - `npm run dev:image-slicing` 39 | - `npm run dev:widgets` 40 | 41 | Running the above commands will use the webpack-dev-server, which will watch 42 | the transpiled files and reload the page whenever changes occur. Once run, you can see 43 | the results at http://localhost:8080/. 44 | -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vtkjs-example", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "build": "echo Please look at README.md for examples that you can build.", 8 | "dev": "echo Please look at README.md for examples that you can build.", 9 | "dev:cone": "EXAMPLE=cone.js webpack-dev-server --mode=development", 10 | "dev:cone-filter": "EXAMPLE=cone-filter.js webpack-dev-server --mode=development", 11 | "dev:volume": "EXAMPLE=volume.js webpack-dev-server --mode=development", 12 | "dev:volume-transfer": "EXAMPLE=volume-transfer.js webpack-dev-server --mode=development", 13 | "dev:image-slicing": "EXAMPLE=image-slicing.js webpack-dev-server --mode=development", 14 | "dev:widgets": "EXAMPLE=widgets.js webpack-dev-server --mode=development" 15 | }, 16 | "author": "", 17 | "license": "BSD-3-Clause", 18 | "dependencies": { 19 | "vtk.js": "^11.1.3" 20 | }, 21 | "devDependencies": { 22 | "kw-web-suite": "^8.0.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/cone-filter.js: -------------------------------------------------------------------------------- 1 | import vtkGenericRenderWindow from 'vtk.js/Sources/Rendering/Misc/GenericRenderWindow'; 2 | 3 | import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; 4 | import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; 5 | 6 | import vtkConeSource from 'vtk.js/Sources/Filters/Sources/ConeSource'; 7 | 8 | import vtkOutlineFilter from 'vtk.js/Sources/Filters/General/OutlineFilter'; 9 | 10 | 11 | // --- Set up our renderer --- 12 | 13 | const container = document.querySelector('#container'); 14 | 15 | // We use the wrapper here to abstract out manual RenderWindow/Renderer/OpenGLRenderWindow setup 16 | const genericRenderWindow = vtkGenericRenderWindow.newInstance(); 17 | genericRenderWindow.setContainer(container); 18 | genericRenderWindow.resize(); 19 | 20 | const renderer = genericRenderWindow.getRenderer(); 21 | const renderWindow = genericRenderWindow.getRenderWindow(); 22 | 23 | 24 | // --- Set up the cone actor --- 25 | 26 | const actor = vtkActor.newInstance(); 27 | const mapper = vtkMapper.newInstance(); 28 | 29 | // this generates a cone 30 | const coneSource = vtkConeSource.newInstance({ 31 | height: 1.0, 32 | }); 33 | 34 | // the mapper reads in the cone polydata 35 | // this sets up a pipeline: coneSource -> mapper 36 | mapper.setInputConnection(coneSource.getOutputPort()); 37 | 38 | // tell the actor which mapper to use 39 | actor.setMapper(mapper); 40 | 41 | 42 | // --- Add actor to scene --- 43 | 44 | renderer.addActor(actor); 45 | 46 | 47 | // --- Reset camera and render the scene --- 48 | 49 | renderer.resetCamera(); 50 | renderWindow.render(); 51 | 52 | 53 | // --- set up our filter 54 | 55 | const filter = vtkOutlineFilter.newInstance(); 56 | 57 | // coneSource -> filter 58 | filter.setInputConnection(coneSource.getOutputPort()); 59 | 60 | const outlineActor = vtkActor.newInstance(); 61 | const outlineMapper = vtkMapper.newInstance(); 62 | outlineActor.setMapper(outlineMapper); 63 | 64 | // filter -> mapper 65 | outlineMapper.setInputConnection(filter.getOutputPort()); 66 | 67 | 68 | // --- render --- 69 | 70 | renderer.addActor(outlineActor); 71 | renderWindow.render(); 72 | 73 | 74 | // --- Expose globals so we can play with values in the dev console --- 75 | 76 | global.renderWindow = renderWindow; 77 | global.renderer = renderer; 78 | global.coneSource = coneSource; 79 | global.actor = actor; 80 | global.mapper = mapper; 81 | global.filter = filter; 82 | 83 | // listen to changes to the filter and re-render 84 | filter.onModified(() => { 85 | renderWindow.render(); 86 | }); 87 | -------------------------------------------------------------------------------- /src/cone.js: -------------------------------------------------------------------------------- 1 | import vtkGenericRenderWindow from 'vtk.js/Sources/Rendering/Misc/GenericRenderWindow'; 2 | 3 | import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor'; 4 | import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper'; 5 | 6 | import vtkConeSource from 'vtk.js/Sources/Filters/Sources/ConeSource'; 7 | 8 | 9 | // --- Set up our renderer --- 10 | 11 | const container = document.querySelector('#container'); 12 | 13 | // We use the wrapper here to abstract out manual RenderWindow/Renderer/OpenGLRenderWindow setup 14 | const genericRenderWindow = vtkGenericRenderWindow.newInstance(); 15 | genericRenderWindow.setContainer(container); 16 | genericRenderWindow.resize(); 17 | 18 | const renderer = genericRenderWindow.getRenderer(); 19 | const renderWindow = genericRenderWindow.getRenderWindow(); 20 | 21 | 22 | // --- Set up the cone actor --- 23 | 24 | const actor = vtkActor.newInstance(); 25 | const mapper = vtkMapper.newInstance(); 26 | 27 | // this generates a cone 28 | const coneSource = vtkConeSource.newInstance({ 29 | height: 1.0, 30 | }); 31 | 32 | // the mapper reads in the cone polydata 33 | // this sets up a pipeline: coneSource -> mapper 34 | mapper.setInputConnection(coneSource.getOutputPort()); 35 | 36 | // tell the actor which mapper to use 37 | actor.setMapper(mapper); 38 | 39 | 40 | // --- Add actor to scene --- 41 | 42 | renderer.addActor(actor); 43 | 44 | 45 | // --- Reset camera and render the scene --- 46 | 47 | renderer.resetCamera(); 48 | renderWindow.render(); 49 | 50 | 51 | // --- Expose globals so we can play with values in the dev console --- 52 | 53 | global.renderWindow = renderWindow; 54 | global.renderer = renderer; 55 | global.coneSource = coneSource; 56 | global.actor = actor; 57 | global.mapper = mapper; 58 | -------------------------------------------------------------------------------- /src/image-slicing.js: -------------------------------------------------------------------------------- 1 | import vtkGenericRenderWindow from 'vtk.js/Sources/Rendering/Misc/GenericRenderWindow'; 2 | 3 | import vtkHttpDataSetReader from 'vtk.js/Sources/IO/Core/HttpDataSetReader'; 4 | import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction'; 5 | import vtkPiecewiseFunction from 'vtk.js/Sources/Common/DataModel/PiecewiseFunction'; 6 | import vtkImageMapper from 'vtk.js/Sources/Rendering/Core/ImageMapper'; 7 | import vtkImageSlice from 'vtk.js/Sources/Rendering/Core/ImageSlice'; 8 | import vtkInteractorStyleImage from 'vtk.js/Sources/Interaction/Style/InteractorStyleImage'; 9 | import ImageConstants from 'vtk.js/Sources/Rendering/Core/ImageMapper/Constants'; 10 | 11 | const { SlicingMode } = ImageConstants; 12 | 13 | 14 | // --- Set up our renderer --- 15 | 16 | const container = document.querySelector('#container'); 17 | 18 | // We use the wrapper here to abstract out manual RenderWindow/Renderer/OpenGLRenderWindow setup 19 | const genericRenderWindow = vtkGenericRenderWindow.newInstance(); 20 | genericRenderWindow.setContainer(container); 21 | genericRenderWindow.resize(); 22 | 23 | const renderer = genericRenderWindow.getRenderer(); 24 | const renderWindow = genericRenderWindow.getRenderWindow(); 25 | 26 | // renderer camera to parallel projection 27 | renderer.getActiveCamera().setParallelProjection(true); 28 | 29 | 30 | // --- Set up interactor style for image slicing 31 | 32 | const istyle = vtkInteractorStyleImage.newInstance(); 33 | istyle.setInteractionMode('IMAGE_SLICING'); 34 | renderWindow.getInteractor().setInteractorStyle(istyle); 35 | 36 | 37 | // --- Set up the slicing actor --- 38 | 39 | const actor = vtkImageSlice.newInstance(); 40 | const mapper = vtkImageMapper.newInstance(); 41 | 42 | mapper.setSliceAtFocalPoint(true); 43 | mapper.setSlicingMode(SlicingMode.Z); 44 | 45 | // tell the actor which mapper to use 46 | actor.setMapper(mapper); 47 | 48 | 49 | // --- set up default window/level --- 50 | 51 | actor.getProperty().setColorWindow(255); 52 | actor.getProperty().setColorLevel(127); 53 | 54 | 55 | // --- load remote dataset --- 56 | 57 | const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true }); 58 | 59 | // wire up the reader to the mapper 60 | mapper.setInputConnection(reader.getOutputPort()); 61 | 62 | reader 63 | .setUrl('https://kitware.github.io/vtk-js/data/volume/LIDC2.vti') 64 | .then(() => reader.loadData()) 65 | .then(() => { 66 | // --- Add volume actor to scene --- 67 | renderer.addActor(actor); 68 | 69 | // --- Reset camera and render the scene --- 70 | renderer.resetCamera(); 71 | renderWindow.render(); 72 | }); 73 | 74 | 75 | // --- Expose globals so we can play with values in the dev console --- 76 | 77 | global.renderWindow = renderWindow; 78 | global.renderer = renderer; 79 | global.actor = actor; 80 | global.mapper = mapper; 81 | 82 | -------------------------------------------------------------------------------- /src/volume-transfer.js: -------------------------------------------------------------------------------- 1 | import vtkGenericRenderWindow from 'vtk.js/Sources/Rendering/Misc/GenericRenderWindow'; 2 | 3 | import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume'; 4 | import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper'; 5 | import vtkHttpDataSetReader from 'vtk.js/Sources/IO/Core/HttpDataSetReader'; 6 | import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction'; 7 | import vtkPiecewiseFunction from 'vtk.js/Sources/Common/DataModel/PiecewiseFunction'; 8 | 9 | import vtkColorMaps from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction/ColorMaps'; 10 | 11 | 12 | // --- Set up our renderer --- 13 | 14 | const container = document.querySelector('#container'); 15 | 16 | // We use the wrapper here to abstract out manual RenderWindow/Renderer/OpenGLRenderWindow setup 17 | const genericRenderWindow = vtkGenericRenderWindow.newInstance(); 18 | genericRenderWindow.setContainer(container); 19 | genericRenderWindow.resize(); 20 | 21 | const renderer = genericRenderWindow.getRenderer(); 22 | const renderWindow = genericRenderWindow.getRenderWindow(); 23 | 24 | 25 | // --- Set up the volume actor --- 26 | 27 | const actor = vtkVolume.newInstance(); 28 | const mapper = vtkVolumeMapper.newInstance(); 29 | 30 | // tell the actor which mapper to use 31 | actor.setMapper(mapper); 32 | 33 | 34 | // --- set up our color lookup table and opacity piecewise function 35 | 36 | const lookupTable = vtkColorTransferFunction.newInstance(); 37 | const piecewiseFun = vtkPiecewiseFunction.newInstance(); 38 | 39 | // set up color transfer function 40 | lookupTable.applyColorMap(vtkColorMaps.getPresetByName('Cool to Warm')); 41 | // hardcode an initial mapping range here. 42 | // Normally you would instead use the range from 43 | // imageData.getPointData().getScalars().getRange() 44 | lookupTable.setMappingRange(0, 256); 45 | lookupTable.updateRange(); 46 | 47 | // set up simple linear opacity function 48 | // This assumes a data range of 0 -> 256 49 | for (let i = 0; i <= 8; i++) { 50 | piecewiseFun.addPoint(i * 32, i / 8); 51 | } 52 | 53 | // set the actor properties 54 | actor.getProperty().setRGBTransferFunction(0, lookupTable); 55 | actor.getProperty().setScalarOpacity(0, piecewiseFun); 56 | 57 | 58 | // --- load remote dataset --- 59 | 60 | const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true }); 61 | 62 | // wire up the reader to the mapper 63 | mapper.setInputConnection(reader.getOutputPort()); 64 | 65 | reader 66 | .setUrl('https://kitware.github.io/vtk-js/data/volume/LIDC2.vti') 67 | .then(() => reader.loadData()) 68 | .then(() => { 69 | // --- Add volume actor to scene --- 70 | renderer.addVolume(actor); 71 | 72 | // update lookup table mapping range based on input dataset 73 | const range = reader.getOutputData().getPointData().getScalars().getRange(); 74 | lookupTable.setMappingRange(...range); 75 | lookupTable.updateRange(); 76 | 77 | // --- Reset camera and render the scene --- 78 | renderer.resetCamera(); 79 | renderWindow.render(); 80 | }); 81 | 82 | 83 | // --- Expose globals so we can play with values in the dev console --- 84 | 85 | global.renderWindow = renderWindow; 86 | global.renderer = renderer; 87 | global.actor = actor; 88 | global.mapper = mapper; 89 | 90 | -------------------------------------------------------------------------------- /src/volume.js: -------------------------------------------------------------------------------- 1 | import vtkGenericRenderWindow from 'vtk.js/Sources/Rendering/Misc/GenericRenderWindow'; 2 | 3 | import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume'; 4 | import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper'; 5 | import vtkHttpDataSetReader from 'vtk.js/Sources/IO/Core/HttpDataSetReader'; 6 | 7 | 8 | // --- Set up our renderer --- 9 | 10 | const container = document.querySelector('#container'); 11 | 12 | // We use the wrapper here to abstract out manual RenderWindow/Renderer/OpenGLRenderWindow setup 13 | const genericRenderWindow = vtkGenericRenderWindow.newInstance(); 14 | genericRenderWindow.setContainer(container); 15 | genericRenderWindow.resize(); 16 | 17 | const renderer = genericRenderWindow.getRenderer(); 18 | const renderWindow = genericRenderWindow.getRenderWindow(); 19 | 20 | 21 | // --- Set up the volume actor --- 22 | 23 | const actor = vtkVolume.newInstance(); 24 | const mapper = vtkVolumeMapper.newInstance(); 25 | 26 | // tell the actor which mapper to use 27 | actor.setMapper(mapper); 28 | 29 | 30 | // --- load remote dataset --- 31 | 32 | const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true }); 33 | 34 | // wire up the reader to the mapper 35 | mapper.setInputConnection(reader.getOutputPort()); 36 | 37 | reader 38 | .setUrl('https://kitware.github.io/vtk-js/data/volume/LIDC2.vti') 39 | .then(() => reader.loadData()) 40 | .then(() => { 41 | // --- Add volume actor to scene --- 42 | renderer.addVolume(actor); 43 | 44 | // --- Reset camera and render the scene --- 45 | renderer.resetCamera(); 46 | renderWindow.render(); 47 | }); 48 | 49 | 50 | // --- Expose globals so we can play with values in the dev console --- 51 | 52 | global.renderWindow = renderWindow; 53 | global.renderer = renderer; 54 | global.actor = actor; 55 | global.mapper = mapper; 56 | 57 | -------------------------------------------------------------------------------- /src/widgets.js: -------------------------------------------------------------------------------- 1 | import vtkGenericRenderWindow from 'vtk.js/Sources/Rendering/Misc/GenericRenderWindow'; 2 | 3 | import vtkVolume from 'vtk.js/Sources/Rendering/Core/Volume'; 4 | import vtkVolumeMapper from 'vtk.js/Sources/Rendering/Core/VolumeMapper'; 5 | import vtkHttpDataSetReader from 'vtk.js/Sources/IO/Core/HttpDataSetReader'; 6 | import vtkColorTransferFunction from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction'; 7 | import vtkPiecewiseFunction from 'vtk.js/Sources/Common/DataModel/PiecewiseFunction'; 8 | 9 | import vtkImageCroppingWidget from 'vtk.js/Sources/Widgets/Widgets3D/ImageCroppingWidget'; 10 | import vtkImageCropFilter from 'vtk.js/Sources/Filters/General/ImageCropFilter'; 11 | import vtkWidgetManager from 'vtk.js/Sources/Widgets/Core/WidgetManager'; 12 | 13 | import vtkColorMaps from 'vtk.js/Sources/Rendering/Core/ColorTransferFunction/ColorMaps'; 14 | 15 | 16 | // --- Set up our renderer --- 17 | 18 | const container = document.querySelector('#container'); 19 | 20 | // We use the wrapper here to abstract out manual RenderWindow/Renderer/OpenGLRenderWindow setup 21 | const genericRenderWindow = vtkGenericRenderWindow.newInstance(); 22 | genericRenderWindow.setContainer(container); 23 | genericRenderWindow.resize(); 24 | 25 | const renderer = genericRenderWindow.getRenderer(); 26 | const renderWindow = genericRenderWindow.getRenderWindow(); 27 | 28 | 29 | // --- Set up the volume actor --- 30 | 31 | const actor = vtkVolume.newInstance(); 32 | const mapper = vtkVolumeMapper.newInstance(); 33 | 34 | // tell the actor which mapper to use 35 | actor.setMapper(mapper); 36 | 37 | 38 | // --- set up our color lookup table and opacity piecewise function 39 | 40 | const lookupTable = vtkColorTransferFunction.newInstance(); 41 | const piecewiseFun = vtkPiecewiseFunction.newInstance(); 42 | 43 | // set up color transfer function 44 | lookupTable.applyColorMap(vtkColorMaps.getPresetByName('Cool to Warm')); 45 | // hardcode an initial mapping range here. 46 | // Normally you would instead use the range from 47 | // imageData.getPointData().getScalars().getRange() 48 | lookupTable.setMappingRange(0, 256); 49 | lookupTable.updateRange(); 50 | 51 | // set up simple linear opacity function 52 | // This assumes a data range of 0 -> 256 53 | for (let i = 0; i <= 8; i++) { 54 | piecewiseFun.addPoint(i * 32, i / 8); 55 | } 56 | 57 | // set the actor properties 58 | actor.getProperty().setRGBTransferFunction(0, lookupTable); 59 | actor.getProperty().setScalarOpacity(0, piecewiseFun); 60 | 61 | 62 | // --- setup our widget manager and widget --- 63 | 64 | const widgetManager = vtkWidgetManager.newInstance(); 65 | widgetManager.setRenderer(renderer); 66 | 67 | // this is a widget factory 68 | const widget = vtkImageCroppingWidget.newInstance(); 69 | // this is an instance of a widget associated with a renderer 70 | const viewWidget = widgetManager.addWidget(widget); 71 | 72 | 73 | // --- set up crop filter 74 | 75 | const cropFilter = vtkImageCropFilter.newInstance(); 76 | // we listen to cropping widget state to inform the crop filter 77 | const cropState = widget.getWidgetState().getCroppingPlanes(); 78 | cropState.onModified(() => 79 | cropFilter.setCroppingPlanes(cropState.getPlanes()) 80 | ); 81 | 82 | 83 | // --- load remote dataset --- 84 | 85 | const reader = vtkHttpDataSetReader.newInstance({ fetchGzip: true }); 86 | 87 | // wire up the reader, crop filter, and mapper 88 | cropFilter.setInputConnection(reader.getOutputPort()); 89 | mapper.setInputConnection(cropFilter.getOutputPort()); 90 | 91 | reader 92 | .setUrl('https://kitware.github.io/vtk-js/data/volume/LIDC2.vti') 93 | .then(() => reader.loadData()) 94 | .then(() => { 95 | // --- Add volume actor to scene --- 96 | renderer.addVolume(actor); 97 | 98 | // update lookup table mapping range based on input dataset 99 | const range = reader.getOutputData().getPointData().getScalars().getRange(); 100 | lookupTable.setMappingRange(...range); 101 | lookupTable.updateRange(); 102 | 103 | // update crop widget and filter with image info 104 | const image = reader.getOutputData(); 105 | cropFilter.setCroppingPlanes(...image.getExtent()); 106 | widget.copyImageDataDescription(image); 107 | 108 | // --- Reset camera and render the scene --- 109 | renderer.resetCamera(); 110 | renderWindow.render(); 111 | 112 | // --- Enable interactive picking of widgets --- 113 | widgetManager.enablePicking(); 114 | renderWindow.render(); 115 | }); 116 | 117 | 118 | // --- Expose globals so we can play with values in the dev console --- 119 | 120 | global.renderWindow = renderWindow; 121 | global.renderer = renderer; 122 | global.actor = actor; 123 | global.mapper = mapper; 124 | global.widget = widget; 125 | -------------------------------------------------------------------------------- /standalone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const vtkRules = require('vtk.js/Utilities/config/dependency.js').webpack.core.rules; 4 | 5 | const example = process.env['EXAMPLE'].replace(/[\/]|\.\./g, '') || 'cone.js'; 6 | 7 | module.exports = { 8 | entry: { 9 | app: path.join(__dirname, 'src', example), 10 | }, 11 | output: { 12 | path: path.join(__dirname, 'dist'), 13 | filename: '[name].js', 14 | }, 15 | module: { 16 | rules: [ 17 | { 18 | test: /\.js$/, 19 | loader: 'babel-loader', 20 | exclude: /node_modules/, 21 | }, 22 | ].concat(vtkRules), 23 | }, 24 | resolve: { 25 | extensions: ['.js'], 26 | }, 27 | devServer: { 28 | contentBase: path.join(__dirname, 'dist'), 29 | disableHostCheck: true, 30 | hot: false, 31 | quiet: false, 32 | noInfo: false, 33 | stats: { 34 | colors: true, 35 | }, 36 | }, 37 | }; 38 | 39 | --------------------------------------------------------------------------------