vtk.js modifying WidgetState always reloads all WidgetRepresentations

Dear vtk-team,

I’ve created a simple widget with a state and two representations:

  • a basic representation displaying a ball at the position of the mouse
  • a complex representation generating a large polyData

The representations are automatically recomputed whenever requestData is called by VTK. The problem I’m facing is that both representations are recomputed as soon as something in the WidgetState has changed. I’d however like to have the first representation update regularly (e.g. every time the mouse moves), while the second one should only be recomputed once in a while. Is there a way to influence when requestData() is being called on each representation?

I’ve tried to accomplish this by creating two sub-states (one containing the mouse position - being updated regularly - and one containing the data to construct the large polyData). I gave the two sub-states a different name and different labels array. I then used these separate labels in the viewToRepresentationMapping, which should make each representation only access it’s own substate.

I however noticed that both representations are still updated on every change to one of the substates. Moreover, the input passed along with requestData is always an array of length 1, containing the complete widgetState (instead of the expected substate).

Is this normal, or am I doing something wrong? Thanks a lot in advance.

This is how I’m trying to assign separate states to representations:

const viewToRepresentations = (viewType) => {
     return [
        {
          builder: lightweightRepresentation,
          labels: ['lightweight']
        },
        {
          builder: heavyRepresentation,
          labels: ['heavy']
        }
     ]
}

And this is where I define these substates

const generateState = () => {
  return vtkStateBuilder
    .createBuilder()
    .addField({
      name: 'customObject',
      initialValue: undefined
    })
    .addStateFromMixin({
      labels: ['lightweight'],
      mixins: ['origin', 'direction', 'manipulator', 'visible'],
      name: 'lightweight',
      initialValues: {
        origin: [0, 0, 0],
        direction: [0, 0, 1],
        visible: true
      }
    })
    .addStateFromMixin({
      labels: ['heavy'],
      mixins: ['origin', 'direction', 'manipulator', 'visible'],
      name: 'heavy',
      initialValues: {
        origin: [0, 0, 0],
        direction: [0, 0, 1],
        visible: true
      }
    })
}

Hi, there are two parts to this. The first part is obtaining the substates that correspond to the representation as per the label-based mapping in your widget’s getRepresentationsForViewType(). The second is updating only when the substate(s) have changed.

You are correct that a representation’s requestData receives an array (set of inputs) with a single input, the entire widget state. In order to obtain the list of substates, you must call publicAPI.getRepresentationStates(inData[0]). That should correspond to all states with the corresponding label, e.g. "heavy".

One approach to determining if your substates have changed is to look at state.getMTime(), and comparing it with a cached value. That way you do not recompute your expensive polydata every time.
The approach would be something like the following:

let lastUpdated = [];
publicAPI.requestData = (inData, outData) => {
  const list = publicAPI.getRepresentationStates(inData[0]);
  const latestMTimes = list.map((state) => state.getMTime());
  if (latestMTimes.some((time, idx) => time > (lastUpdated[idx] || 0))) {
    lastUpdated = latestMTimes;
    ... compute poly data here
  }
  outData[0] = cachedPolyDataValue;
};

Hi Forrest,

Thanks a lot for the very quick reply. The manual caching indeed seems like a promising way to proceed, since it allows me to have full control over the calculations.

I however ran into another issue when trying to implement this. It seems that, when returning a cached polyData object, VTK tries to re-request data forever. I tested this further by setting up a simple representation with the following requestData:

let cached = vtkPolyData.newInstance();

  publicAPI.requestData = (inData, outData) => {
    console.log('requesting cached data');
    outData[0] = cached;
  };

As soon as I enable the widget, the console is spammed with my print statement, even when not moving the mouse. The page also slows down, eventually even preventing me from reloading. I also put a console.log in the other non-caching representation, and there everything behaves normally (e.g. requestData is only called when moving the mouse). Any thoughts on this? Perhaps this is a bug somewhere in VTK? Or is there maybe a way to ‘trick’ VTK in thinking that it’s a new polyData object?

This method is called to figure out if the requestData should execute. You can also override it to have a different behavior.

Or when caching the output, you can reset your local mtime to match the dataset one.

Thanks for the reply.

So do I understand correctly that I’m able to define publicAPI.shouldUpdate = [my custom boolean funcion] from within each representation?

yes

Great, this will help me a lot! :pray: