vtk-easy: a nice js-like veneer over vtkjs syntax

Hi there,

Ever since the earliest VTK days I’ve had both respect for and aggravation with VTK’s verbose syntax, specifically the getter and setter syntax and the fact that operations aren’t chainable in languages like Python and JavaScript that support it. Vtkjs, leaning on this syntax style, buzzes in my ear the same way.

I finally too a stab at addressing this issue with vtk-easy ( GitHub - mhalle/vtk-easy: Ergonomic veneer over vtk.js — property-style access, fluent pipelines, auto-unwrapping · GitHub ) .

vtk-easy wraps vtkjs objects with proxies that provide getter and setters and other nice properties. It’s non-invasive (sitting completely above vtkjs) and optional (you can drop down to vtkjs at any time).

It seems to work all with the vtkjs examples (some are included in the package).

Here’s an example:

/* vtkjs imports omitted, same between versions */
import ez from 'vtk-easy';

const view = ez.create(vtkFullScreenRenderWindow, { 
    background: [0, 0, 0] 
});

const pointSource = ez.create(vtkPointSource, { 
    numberOfPoints: 25, radius: 0.25 
});

const pointsActor = ez.pipeline(pointSource)
  .actor({ property: { pointSize: 5 } });

const outlineActor = ez.pipeline(pointSource)
  .filter(vtkOutlineFilter)
  .actor({ property: { lineWidth: 5 } });

view.add(pointsActor, outlineActor);

view.renderer.resetCamera();
view.renderWindow.render();

compared to:


const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({ background: [0, 0, 0] });

const renderer = fullScreenRenderer.getRenderer();
const renderWindow = fullScreenRenderer.getRenderWindow();
const pointSource = vtkPointSource.newInstance({ numberOfPoints: 25, radius: 0.25 });

const outline = vtkOutlineFilter.newInstance();
outline.setInputConnection(pointSource.getOutputPort());

const pointMapper = vtkMapper.newInstance();
pointMapper.setInputConnection(pointSource.getOutputPort());

const pointActor = vtkActor.newInstance();
pointActor.setMapper(pointMapper);
pointActor.getProperty().setPointSize(5);

renderer.addActor(pointActor);

const outlineMapper = vtkMapper.newInstance();
outlineMapper.setInputConnection(outline.getOutputPort());
const outlineActor = vtkActor.newInstance();
outlineActor.setMapper(outlineMapper);
outlineActor.getProperty().setLineWidth(5);

renderer.addActor(outlineActor);
renderer.resetCamera();
renderWindow.render();

Please let me know what you think of it.

this looks nice.

I also want to mention that in the sci-viz team, there are rumblings of an ‘express’ api for python users. I am thinking to enable such an API in the JavaScript world through VTK.wasm.

Thanks.

Since I wrote this post, I’ve added two more features: a way to write a source or filter with almost no boilerplate, and a polydata construction helper.

It’s lovely that these features can bolt on top so cleanly.

Actually in Python, this is aldready working, thanks to Berk Geveci, please see: PythonicAPIComments also VTK 9.4: A Step Closer to the Ways of Python.

@amaclean no there’s even more in the works. I can’t find the gist link at the moment

I appreciate the brainstorming that went into the design. However, I think that it’s gone too far and lost some of the good design decisions of VTK.

In my opinion, object constructors should be Capitalized to stand out. Lowercase is unpythonic.

”*” imports (e.g., vtkmodules.simple_api import *) are an anti-pattern, because as in this case they push read and write into the default namespace where they conflict with builtins.

It also looks like data sources are evaluated eagerly, which conflicts with VTK’s pipeline model.

Maybe there’s a standard pipeline under there, but it’s hard to see how lazy evaluation happens from the example.

It’s great to have the python version to look at to help develop new features.

Javascript doesn’t have operator overloading ( >> in the python syntax ) so I used the .pipe() convention used in other JS pipeline packages. I borrowed the idea that pipeline building can be either eager with instances or deferred like a template. It’s a neat idea.

Optionally, the pipeline can be terminated with actor() to get an actor..

I added a merge function that allows multiple pipeline segments to be merged together with AddInputConnection and SetInputConnection.

I also added defineFilter and defineSource functions that allow new, fully vtkjs compliant filters and data sources to be created at runtime with less boilerplating.

Finally, I like the idea of using string color names, especially to work with a web environment. I added ez.rgb() and ez.rgba() functions that take any compliant web color – including from other color spaces – and convert them into number arrays for vtkjs. These colors are in srgb space just like the underlying libraries expect.

Finally, I added a view.show(actorlist) convenience method on RenderWindow wrappers.

Everything here is pretty lightweight, and again, it reflects well on vtkjs that this is possible without monkeypatching.