Python backend send via JSON to vtk.js

Hi,
I am running a python web-server which is calculating the new geometry of a model to then send back to the website visualiser using vtk.js to visualise the information. I would like to send the updated geometry using JSON via the following process “vtkJSONDataSetWriter”. On the python side I generate some geometry using Pyvista as this is convenient wrapper for generating the coloured mesh, however I am not sure how to translate this into a JSON object to send back to website. A pseudo example below:

# Pseudo code on the python web server
from pyvista import examples
import pyvista as pv
import vtk

# Request comes from the client website via AJAX request, then new geometry is calculated...
# ...in this simple example I just include a stock example
mesh = examples.download_st_helens().warp_by_scalar()
# Add scalar array with range (0, 100) that correlates with elevation
mesh['values'] = pv.plotting.normalize(mesh['Elevation']) * 100

# Not sure how to use the function below to write to a JSON object
vtkDcmWriter = vtk.vtkJSONDataSetWriter()
json_object = vtkDcmWriter.Write(actor)

# Return the JSON object to the javascript
return json_object

On the Client side the following pseudo code

# Receive JSON object from server side... not pass the json_object to the reader
const reader = vtkHttpDataSetReader.newInstance(json_object)

const mapper = vtkMapper.newInstance();
mapper.setInputConnection(reader.getOutputPort());

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

renderer.addActor(actor);

// Then update the actor in my website with Javascript, however not completely sure how to achieve this.

Any help on this would be much appreciated - struggling to find any examples of the server side needed to send to the client. Thanks.

1 Like

Wow that won’t be that easy.

  1. The writer writes datasets not actors
  2. The writer generate a directory, not a single file. Moreover it is not just a single JSON file.
  3. You don’t create a reader from that json file.
    • You need to call setUrl or another method to actually ask the reader to process the data.
    • You can find an example on how to use that reader here

Then maybe I should ask, what is the root of that goal? Panel already offer VTK synchronization with a local vtk.js scene.

Thanks Sebastien… realised I may be attempting the impossible / hitting my head against a brick wall. Ultimately I’m try to generate a web app that takes a user input runs a calculation on the server side, and then plots the vtk render of the results in my website. I also want to use vtk.js because it has useful cell and point picker which I want to integrate as a feature in my plots. The plot I am trying to achieve is a mesh, however the color scalar values are calculated based on the input on the client web side.

Perhaps the best solution is to send the calculated geometry and color scalar data (i.e xyz coords, cell connectivity and color scalar values for the nodes) from the server side back as JSON to the JavaScript on the client side which loops through the raw data generating the plot?

I had a look at panel, however it didn’t allow me to readily customise callbacks such as point or cell picker functions which would be required for my web app. If you have any advice on this as a route would greatly appreciate it.

Thanks again for your pointers so far!!

I see… It might be easier to just use the vtp (vtkXMLPolyDataWriter) then.

Great - many thanks for this… I had a go and managed to send the xml and render the image… I created a super simple polyline file with some scalar colors to send as the “polydata_xml” file. The java script side loads the geometry information correctly, however does not display the scalar color information as expected. The blue entire polyline is blue and not as the example I am trying to render, I am not sure if it is an error in the encoding of the xml or the mapping of the scalar values to colors?

# Example python server side info using pyvista to generate the vtk file and color scalars
    def make_points():
        """Helper to make XYZ points"""
        theta = np.linspace(-4 * np.pi, 4 * np.pi, 100)
        z = np.linspace(-2, 2, 100)
        r = z ** 2 + 1
        x = r * np.sin(theta)
        y = r * np.cos(theta)
        return np.column_stack((x, y, z))

    points = make_points()
    points[0:5, :]

    def polyline_from_points(points):
        poly = pv.PolyData()
        poly.points = points
        the_cell = np.arange(0, len(points), dtype=np.int_)
        the_cell = np.insert(the_cell, 0, len(points))
        poly.lines = the_cell
        return poly

    polyline = polyline_from_points(points)
    print(np.arange(polyline.n_points))
    polyline["scalars"] = np.arange(polyline.n_points)
    print(polyline)

    filename = "temp/vtk_test.vtp"
    writer = vtk.vtkXMLPolyDataWriter()
    writer.SetFileName(filename)
    writer.SetInputData(polyline)
    writer.Write()

    with open(filename) as f:
        polydata_xml = f.read()

    return polydata_xml

This is the javascript function invoked when the xml is returned:

// Callback function for when request completes
        request.onload = () => {

            var data = request.responseText;
            var writerReader = vtk.IO.XML.vtkXMLPolyDataReader.newInstance()
            const textEncoder = new TextEncoder();
            writerReader.parseAsArrayBuffer(textEncoder.encode(data))
            console.log(textEncoder.encode(data))

            const mapper = vtk.Rendering.Core.vtkMapper.newInstance();
            mapper.setInputConnection(writerReader.getOutputPort());

            const actor = vtk.Rendering.Core.vtkActor.newInstance();
            actor.setMapper(mapper);

            // ----------------------------------------------------------------------------
            // Add the actor to the renderer and set the camera based on it
            // ----------------------------------------------------------------------------
            const renderWindow = vtk.Rendering.Core.vtkRenderWindow.newInstance();
            const renderer = vtk.Rendering.Core.vtkRenderer.newInstance({ background: [0.2, 0.3, 0.4] });
            renderWindow.addRenderer(renderer);
            renderer.addActor(actor);
            renderer.resetCamera();

            // ----------------------------------------------------------------------------
            // Use OpenGL as the backend to view the all this
            // ----------------------------------------------------------------------------

            const openglRenderWindow = vtk.Rendering.OpenGL.vtkRenderWindow.newInstance();
            renderWindow.addView(openglRenderWindow);

            // ----------------------------------------------------------------------------
            // Create a div section to put this into
            // ----------------------------------------------------------------------------
            const container = document.querySelector('#vtk_render')
            openglRenderWindow.setContainer(container);
            openglRenderWindow.setSize(500, 500);

            // ----------------------------------------------------------------------------
            // Setup an interactor to handle mouse events
            // ----------------------------------------------------------------------------

            const interactor = vtk.Rendering.Core.vtkRenderWindowInteractor.newInstance();
            interactor.setView(openglRenderWindow);
            interactor.initialize();
            interactor.bindEvents(container);

            // ----------------------------------------------------------------------------
            // Setup interactor style to use
            // ----------------------------------------------------------------------------

            const trackball_camera = vtk.Interaction.Style.vtkInteractorStyleTrackballCamera.newInstance()
            interactor.setInteractorStyle(trackball_camera);

I don’t see you setting the color range or even defining the lookup table.

See some relevant code section here and here.

Thanks for your help Sebastien! Really helpful :slight_smile: