Widgets

Widgets allow users to interact with scene objects and drive functionality
through clicking, dragging, and touch.

VTK.js currently has two flavors of widgets: the first, located in
Sources/Interaction/Widgets, is the VTK C++ style widgets that were
semi-ported over. The second, located in Sources/Widgets, is the new-style
VTK.js widgets that were architected from the ground-up to address shortcomings
with the original widget architecture.

The rest of this document will be only concerned with the new-style widgets, as
the old-style widgets are deprecated (but won’t be removed in the near-term).

Widget Architecture Overview

The widget architecture follows the Model-View-Controller (MVC) pattern. In
mapping the MVC pattern to VTK.js widgets, there are three primary components
involved:

  • vtkWidgetState is the model and the central source of truth for a widget.
    When a widget is available across different renderers, they all refer to the
    same widget state object for updates.
  • vtkWidgetRepresentation is the base class for the view, which listens to a
    widget state object and generates the appropriate VTK.js scene objects that
    represent the state.
  • vtkAbstractWidget is the base class that contains widget behavior,
    interaction code, and a reference to the widget state. This object is closest
    to the controller.

In addition to the three components above, there are widget manipulators, a
widget manager, and an abstract widget factory. In brief, manipulators take
events (e.g. mouse events) and translate them into some meaningful 3D
coordinate. For widgets, this is usually located within the widget behavior
code. The widget manager is a per-renderer manager that manages the lifecycle
of widgets attached to one specific renderer. The abstract widget factory is
the primary object that creates the widget state, constructs subclasses of
vtkAbstractWidget to be attached to a widget manager, and constructs the
associated representations with the view/renderer.

The following diagram demonstrates the relationships between the different
components of the widgets. The example shows off the case of a widget being
shown in two different renderers, with a single shared widget state.

Widgets Diagram

Widget Factory

The widget factory is used to construct widgets, widget state, and
representations. From the widget factory’s perspective, it starts by building a
single vtkWidgetState. When a widget is requested via getWidgetForView(),
the factory creates a new widget object, attaches the widget state to the
widget, creates the appropriate representations, and sets those representations
on the widget.

Widget developers should subclass vtkAbstractWidgetFactory in order to create
widgets. vtkAbstractWidget is meant as an internal class by the widget
factory, and thus should not be subclassed.

Widget State

The widget state is the central store and source of truth for widget data. By
separating widget data out from vtkAbstractWidget, many widgets across many
renderers can be kept in sync by referring to a single widget state. States can
also be nested with sub-states, leading to a tree of states. Changes to any
node within the state tree should propagate up and reflect as a change event in
the root state.

While constructing a widget state by hand is possible, it is much easier (and
recommended) to use vtkStateBuilder for the majority of use cases.
vtkStateBuilder simplifies the construction of widget states through the use
of a handful of APIs: addField(), addStateFromMixin(), etc. More
information can be found in the vtkStateBuilder docs.

Mixins are self-contained state bundles that further assist state creation for
common state properties. They can be applied to a sub-state via the
addStateFromMixin() builder API. Examples of commonly used properties include
position, visibility (mixin named “visible”), color, and size (mixin named
“scale1”).

Widget Representation

Representations are scene objects that render based on the attached widget
state. These are not to be instantiated directly; rather, these should be
referenced in an implementation of
vtkAbstractWidgetFactory.getRepresentationsForViewType.

There are two types of representation behaviors: handle and context. The
primary difference is handles are pickable and contexts are not pickable. If
you want to create a non-pickable representation, extend
vtkContextRepresentation which sets the representation behavior as a context.
Otherwise, extend vtkHandleRepresentation to get pickable handles.

Widget representations are implemented as VTK.js algorithms. That is, they must
implement requestData(inData, outData), where inData contains the widget
state and outData should be set with the resultant vtkPolyData. Here,
requestData is intended to generate a relevant representation given a widget
state, e.g. placing spheres at points provided as a list in the widget state.

When using a representation, care must be taken to ensure that the state has
certain properties available. As an example, vtkSphereHandleRepresentation
expects “origin” and “position” mixins to be specified, but optionally can take
the “visible” and “scale1” mixins. Each representation specifies their
dependency in their documentation and their requestData method.

Widget representations must generate actors to be added to a scene. Internally,
all actors should be appended to the model.actors array in order to be
rendered.

Scaling representations to have fixed pixel size

In order to scale a representation such that it retains the same size in
display space, a widget representation should use the scaleInPixels property
and the getPixelWorldHeightAtCoord(coord) method. When scaleInPixels is set
to true, a widget representation should multiply whatever scaling they perform
by the output of getPixelWorldHeightAtCoord(coord).

Look at the SphereHandleRepresentation as an example for how
getPixelWorldHeightAtCoord is used.

If scaleInPixels is false, then all scale values should be interpreted as
world scales.

Widget Manager

The widget manager manages the lifecycle of a widget associated with a
view/renderer. In order to add a widget into a renderer, a widget manager must
first exist for that renderer (via the widget manager’s setRenderer(ren)
call). The widget manager’s addWidget() method takes in a
vtkAbstractWidgetFactory and uses the factory to construct a widget and
associated representations for the particular view.

Widget manipulators

Manipulators are objects that implement a single method, handleEvent(callData, glRenderWindow). This method takes a mouse event (in callData) and
transforms it into a 3D coordinate position, returning an object containing at
least a worldCoords and eventually other values. This is different from
interactivity manipulators in that interactivity manipulators change how the
scene is viewed via manipulating the camera, whereas widget manipulators
transform mouse input into meaningful world coordinates, e.g. snapping
positions to a line.

An example manipulator would be a plane manipulator. When instantiated with a
plane point and normal, the plane manipulator will project mouse events onto the
plane in 3D and return that projected point.

SVG Representation Support

vtk.js no longer supports SVG-based widget representations, as managing the SVG
rendering mechanisms are better left to a separate library. All of the examples
that used SVG representations have been rewritten to use a rendering shim based
on snabbdom, located in Examples/Utilities/SVGHelpers.js.

If you are looking for a reasonably easy drop-in for rendering SVG
representations, check out the above SVGHelpers.js, which requires snabbdom
to be installed. An example usage can be found in the vtkPolyLineWidget
example.