Skip to content

Commit

Permalink
feat(ImageMarchingCubes): Added 2D image or slice contouring
Browse files Browse the repository at this point in the history
A new isocontouring algorithm for 2D images or a zslice from a
volume has been added. Multiple contours are supported, and merging
intersection points along edges may be enabled or disabled.
  • Loading branch information
wschroed committed Nov 21, 2017
1 parent d620129 commit 55368dc
Show file tree
Hide file tree
Showing 7 changed files with 427 additions and 0 deletions.
12 changes: 12 additions & 0 deletions Sources/Common/DataModel/ImplicitBoolean/index.js
Expand Up @@ -19,6 +19,18 @@ function vtkImplicitBoolean(publicAPI, model) {
// Set our className
model.classHierarchy.push('vtkImplicitBoolean');

publicAPI.getMTime = () => {
let mTime = model.superClass.getMTime();
if (!model.functions || model.functions.length <= 0) {
return mTime;
}

for (let i = 0; i < model.functions.length; ++i) {
mTime = Math.max(mTime, model.functions[i].getMTime());
}
return mTime;
};

publicAPI.getOperationAsString = () => macro.enumToString(Operation, model.operation);

publicAPI.setOperationToUnion = () => publicAPI.setOperation(0);
Expand Down
24 changes: 24 additions & 0 deletions Sources/Filters/General/ImageMarchingSquares/api.md
@@ -0,0 +1,24 @@
## Introduction

vtkImageMarchingSquares - isocontour an image (or slice from a volume)

Given a specified contour value, generate isolines using the
Marching Squares algorithm (the 2D version of the 3D Marching Cubes
algorithm).

## Public API

### contourValues

Set/Get an array of isocontour values.

### sliceNumber

Set/Get the k-slice number of the input volume. By default the
sliceNumber = 0.

### mergePoints

As lines forming the isolines are generated, indicate whether
conincident points are to be merged. Merging produces connected polylines
at the cost of additional memory and computation.
48 changes: 48 additions & 0 deletions Sources/Filters/General/ImageMarchingSquares/caseTable.js
@@ -0,0 +1,48 @@
// ----------------------------------------------------------------------------
// Marching squares case functions (using lines to generate the 2D tessellation).
// For each case, a list of edge ids that form the triangles. A -1 marks the
// end of the list of edges. Edges are taken three at a time to generate
// triangle points.
// ----------------------------------------------------------------------------
const MARCHING_SQUARES_CASES = [
[-1, -1, -1, -1, -1], /* 0 */
[0, 3, -1, -1, -1], /* 1 */
[1, 0, -1, -1, -1], /* 2 */
[1, 3, -1, -1, -1], /* 3 */
[2, 1, -1, -1, -1], /* 4 */
[0, 3, 2, 1, -1], /* 5 */
[2, 0, -1, -1, -1], /* 6 */
[2, 3, -1, -1, -1], /* 7 */
[3, 2, -1, -1, -1], /* 8 */
[0, 2, -1, -1, -1], /* 9 */
[1, 0, 3, 2, -1], /* 10 */
[1, 2, -1, -1, -1], /* 11 */
[3, 1, -1, -1, -1], /* 12 */
[0, 1, -1, -1, -1], /* 13 */
[3, 0, -1, -1, -1], /* 14 */
[-1, -1, -1, -1, -1], /* 15 */
];

const EDGES = [
[0, 1],
[1, 3],
[2, 3],
[0, 2],
];

function getCase(index) {
return MARCHING_SQUARES_CASES[index];
}

// Define the four edges of the pixel by the following pairs of vertices
function getEdge(eid) {
return EDGES[eid];
}

// ----------------------------------------------------------------------------
// Static API
// ----------------------------------------------------------------------------
export default {
getCase,
getEdge,
};
@@ -0,0 +1,20 @@
<table>
<tr>
<td>Volume resolution</td>
<td>
<input class='volumeResolution' type="range" min="10" max="100" step="1" value="50" />
</td>
</tr>
<tr>
<td>Radius</td>
<td>
<input class='sphereRadius' type="range" min="0.01" max="1.0" step="0.01" value="0.025" />
</td>
</tr>
<tr>
<td>Merge Points</td>
<td>
<input class='mergePoints' type="checkbox" unchecked />
</td>
</tr>
</table>
110 changes: 110 additions & 0 deletions Sources/Filters/General/ImageMarchingSquares/example/index.js
@@ -0,0 +1,110 @@
import 'vtk.js/Sources/favicon';

import vtkActor from 'vtk.js/Sources/Rendering/Core/Actor';
import vtkFullScreenRenderWindow from 'vtk.js/Sources/Rendering/Misc/FullScreenRenderWindow';
import vtkImageMarchingSquares from 'vtk.js/Sources/Filters/General/ImageMarchingSquares';
import vtkOutlineFilter from 'vtk.js/Sources/Filters/General/OutlineFilter';
import vtkMapper from 'vtk.js/Sources/Rendering/Core/Mapper';
import vtkSampleFunction from 'vtk.js/Sources/Imaging/Hybrid/SampleFunction';
import vtkSphere from 'vtk.js/Sources/Common/DataModel/Sphere';
// import vtkPlane from 'vtk.js/Sources/Common/DataModel/Plane';
import vtkImplicitBoolean from 'vtk.js/Sources/Common/DataModel/ImplicitBoolean';

import controlPanel from './controller.html';

const { Operation } = vtkImplicitBoolean;

// ----------------------------------------------------------------------------
// Standard rendering code setup
// ----------------------------------------------------------------------------

const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance();
const renderer = fullScreenRenderer.getRenderer();
const renderWindow = fullScreenRenderer.getRenderWindow();

// ----------------------------------------------------------------------------
// Example code
// ----------------------------------------------------------------------------
const actor = vtkActor.newInstance();
renderer.addActor(actor);

const mapper = vtkMapper.newInstance();
actor.setMapper(mapper);

// Build pipeline
const sphere = vtkSphere.newInstance({ center: [-2.5, 0.0, 0.0], radius: 0.5 });
const sphere2 = vtkSphere.newInstance({ center: [2.5, 0.0, 0.0], radius: 0.5 });
// const plane = vtkPlane.newInstance({ origin: [0, 0, 0], normal: [0, 1, 0] });
const impBool = vtkImplicitBoolean.newInstance({ operation: Operation.UNION, functions: [sphere, sphere2] });
const sample = vtkSampleFunction.newInstance({ implicitFunction: impBool, sampleDimensions: [5, 3, 3], modelBounds: [-5.0, 5.0, -1.0, 1.0, -1.0, 1.0] });

// Isocontour
const mSquares = vtkImageMarchingSquares.newInstance({ slice: 1 });

// Connect the pipeline proper
mSquares.setInputConnection(sample.getOutputPort());
mapper.setInputConnection(mSquares.getOutputPort());

// Update the pipeline to obtain metadata (range) about scalars
sample.update();
const cValues = [];
const [min, max] = sample.getOutputData().getPointData().getScalars().getRange();
for (let i = 0; i < 20; ++i) {
cValues[i] = min + ((i / 19) * (max - min));
}
mSquares.setContourValues(cValues);

// Create an outline
const outline = vtkOutlineFilter.newInstance();
outline.setInputConnection(sample.getOutputPort());
const outlineMapper = vtkMapper.newInstance();
outlineMapper.setInputConnection(outline.getOutputPort());
const outlineActor = vtkActor.newInstance();
outlineActor.setMapper(outlineMapper);
renderer.addActor(outlineActor);

// ----------------------------------------------------------------------------
// UI control handling
// ----------------------------------------------------------------------------
fullScreenRenderer.addController(controlPanel);

// Define the volume resolution
document.querySelector('.volumeResolution').addEventListener('input', (e) => {
const value = Number(e.target.value);
sample.setSampleDimensions(value, value, value);
mSquares.setSlice((value / 2.0));
renderWindow.render();
});

// Define the sphere radius
document.querySelector('.sphereRadius').addEventListener('input', (e) => {
const value = Number(e.target.value);
sphere.setRadius(value);
sphere2.setRadius(value);
sample.modified();
renderWindow.render();
});

// Indicate whether to merge conincident points or not
document.querySelector('.mergePoints').addEventListener('change', (e) => {
mSquares.setMergePoints(!!e.target.checked);
renderWindow.render();
});


// -----------------------------------------------------------
const cam = renderer.getActiveCamera();
cam.setFocalPoint(0, 0, 0);
cam.setPosition(0, 0, 1);
renderer.resetCamera();
renderWindow.render();

// -----------------------------------------------------------
// Make some variables global so that you can inspect and
// modify objects in your browser's developer console:
// -----------------------------------------------------------

global.source = sample;
global.filter = mSquares;
global.mapper = mapper;
global.actor = actor;

0 comments on commit 55368dc

Please sign in to comment.