Sync react state with Vtk.js renderer / scene state

What’s the right way to derive the state from the vtk.js renderer (or other source) so that it can be properly reflected in the frontend using React?

I’m using vtk.js in React, and have created an app with controls that can adjust the camera and properties of actors.

I’m managing the state of the vtk.js scene with a custom object, but would prefer to derive the state directly from the vtk.js scene itself.

I see that a lot of objects including the vtkRenderer have a function getState() which seems appropriate. Unfortunately when I call renderer.getState() I get a Maximum call stack size exceeded error. Similarly when I call actor.getState() I get an error related to reading the isA property. I also tried calling renderer.get() / actor.get() prior to calling getState() to ‘reify’ some of those object values, but that didn’t help.

For a little more context, my custom state object is an array of Actor objects shown below.

I’m using useReducer (like redux) to manage the state, which provides an opportunity to change the state of the vtk.js scene and then call a function that will return the new state. That looks like this:

Any advice anyone can provide would be greatly appreciated! Thanks in advance :pray:

Normally getState() should work if you ask it on a non cyclic data structure. It is possible that a bug sneak in. Anyhow, the idea behind is the following which might not exactly match your need.

const serializableJSObj = objA.getState();
const objB = vtk(serializableJSObj);
// objB is technically a clone of objA 

You may need to create a object walker and pick which properties matter to you with something like that

const saveState = obj.get('propA', 'propB', ...)
// later
obj.set(saveState);

HTH

1 Like

Thanks for the quick reply @Sebastien_Jourdain I’ll give it a try and let you know. If I can pin down a bug with .getState() I’ll open an issue.

@Sebastien_Jourdain I played with this a bit last week and it seems to be a good solution. I’ll post it here in case you can give any feedback or it will be useful to others.

This function should be able to take and vtk.js object and return a js object configured by the fields argument.

interface Field {
  key: string, // field name on return object
  path?: string, // override get field path if key is for display purposes
  fields?: Field[] // array of subfields
}

// recursive "object walker" configured by fields object
const deriveVTKState = (fields: Field[], object: any) => {
  if (!object) return

  return fields.reduce((p, f, i, arr) => {
    // can't do f.get(f.path) because some fields don't exist
    // until functions are called like object.getActors()
    let call = f.path ? `get${f.path.charAt(0).toUpperCase() + f.path.slice(1)}` : `get${f.key.charAt(0).toUpperCase() + f.key.slice(1)}`
    let subObject
    try {
      subObject = object[call]()
    } catch {
      return { ...p, [f.key]: undefined }
    }

    if (typeof subObject === undefined) return { ...p, [f.key]: undefined }

    if (f.fields) {
      // returns true if subObject.length === 0
      if (typeof subObject.length !== 'undefined') {
        return {
          ...p, [f.key]: subObject.map(val => {
            return deriveVTKState(f.fields, val)
          })
        }
      } else return { ...p, [f.key]: deriveVTKState(f.fields, subObject) }
    }
    return { ...p, [f.key]: subObject }
  }, {} as { [index: string]: any })
}