[vtk.js] Mesh only shows after clicking on the screen

Hello everyone,
I am using vtk.js (imported as an external file) to display a time series of meshes.
My code is heavily inspired from the time series example.
Everything works quite well except that:

  1. when a user first loads the web page, the renderer appears as a black screen and the first mesh only appears when clicking on the renderer
  2. when interacting with the time slider, the renderer turns black again and one needs to click again on the renderer to see the next mesh. (my time series has about 160 big meshes so loading takes a few seconds).
    I would like the renderer to show the meshes without any additional interaction with the screen. Could someone tell me how to do this ?

Here is my code:

<html>
<body>
<script type="text/javascript" src="https://unpkg.com/vtk.js"></script>
<script type="text/javascript">
var fullScreenRenderer = vtk.Rendering.Misc.vtkFullScreenRenderWindow.newInstance({
    background: [0, 0, 0],
    containerStyle: { width: '1200px', height: "800px", marginLeft: "300px"} 
  });
  var renderWindow = fullScreenRenderer.getRenderWindow();
  var renderer = fullScreenRenderer.getRenderer();
  var actor = vtk.Rendering.Core.vtkActor.newInstance();
  renderer.addActor(actor);
  var mapper = vtk.Rendering.Core.vtkMapper.newInstance(); // this is the right mapper
  actor.setMapper(mapper);  
  var camera             = vtk.Rendering.Core.vtkCamera.newInstance();

  // add a control panel
  var controlPanel = "<html><div class='slidecontainer'><label for='zoomslider'>Zoom:</label> <input id='zoomslider' type='range' class='slider' min='0.4' max='5' step='0.2'/><p>" + "<label for='timeslider'>Gestational age:</label> <input id='timeslider' type='range' class='slider' min='20' max='36' step='0.1'/><div class='ticks'> <span class='tick'>22</span> <span class='tick'>24</span> <span class='tick'>26</span> <span class='tick'>28</span> <span class='tick'>30</span> <span class='tick'>32</span> <span class='tick'>34</span> <span class='tick'>36</span></div> <p><span id='timevalue'>...</span></p>  <button id='play' class='btn'><i class='fa fa-play-circle'></i>Play</button>  </div> </html>";
  fullScreenRenderer.addController(controlPanel);
//Zoom slider
  var zoomslider = document.querySelector('#zoomslider');
  zoomslider.value = 1;
  zoomslider.addEventListener('input', (e) => {
camera.setViewAngle(15.0/Number(e.target.value));
 renderer.setActiveCamera(camera);
      renderWindow.render();
    });
//time slider
var timeslider = document.querySelector('#timeslider');
var timevalue = document.querySelector('#timevalue');
timeslider.addEventListener('input', (e) => {
  var i = Number(e.target.value);
  timevalue.innerText = e.target.value + " weeks";
  var file = '/assets/atlas/outer_cortical_surface/brain_' + i.toFixed(1) + "0.vtp";
  var reader = vtk.IO.XML.vtkXMLPolyDataReader.newInstance();
  reader.setUrl(file);
  mapper.setInputConnection(reader.getOutputPort());
  renderWindow.render();
  });
timeslider.value = 20;
timevalue.innerText = timeslider.value + " weeks";
var reader = vtk.IO.XML.vtkXMLPolyDataReader.newInstance();
  var file = '/assets/atlas/outer_cortical_surface/brain_0.vtp';
reader.setUrl(file);
mapper.setInputConnection(reader.getOutputPort());
  camera.setPosition(27.519753836746474, 604.1863725248345, -279.2425808488232);
  camera.setViewAngle(15.0);
 renderer.setActiveCamera(camera);
  renderWindow.render();
 </script>
 </body>
 </html>

Thanks in advance!
Fleur

The issue is that you call renderWindow.render(); when the data is not ready. You need to handle the async part of the data loading.

The setUrl method return a promise in which you should call renderWindow.render(); when resolved.

Something like reader.setUrl(file).then(renderWindow.render);

Thanks for your answer!
Unfortunately it doesn’t work.

I tried doing:

var file = '/assets/atlas/outer_cortical_surface/brain_0.vtp';
mapper.setInputConnection(reader.getOutputPort());
reader.setUrl(file).then(
renderWindow.render()
);

and the issue remains exactly the same.

My bad, it does work!
I just replaced renderWindow.render() with renderWindow.render

Another related question:
Now that the “click” problem has been solved, when I move the time slider, the former brain disappears, the screen turns black for a few seconds, and the next brain appears.
Is there a way to ensure a better transition between two brains? While the data is loading, I would prefer brain number 1 to still appear instead of seeing a black screen.

So ideally what you want to do, is to pass the dataset only once it is available. So use mapper.setInputData(reader.getOutputData()) in the setUrl().then(...) right before you call render. Also, you should reuse the reader instance rather than creating a new one each time. Also, you might be able to do some dataset caching as well to skip the re-decoding of the browser cached file.