Sync zoom factor between vtk.js canvas and second canvas rendered on top of it

I am rendering a 3d scene using vtk.js.

I am using vtkMouseCameraTrackballZoomToMouseManipulator to add zoom to mouse functionality.

Setup is pretty standard I can add example code if needed.

That part works fine. I render my scene and I can zoom to mouse.

But now I would like to add another canvas on top of this one where I will be rendering some 2D stuff. I would like both of those zoom at the same rate.

Since vtk.js is handling zooming of the 3d scene fine I have created second canvas and my onWheel handler for it looks something like this, react code.

  const transformedMousePosition = useCallback(
    (x: number, y: number) => {
      if (ctx) {
        const originalPoint = new DOMPoint(x, y);
        return ctx.getTransform().invertSelf().transformPoint(originalPoint);
      }
    },
    [ctx]
  );

  const onWheel = (e: WheelEvent<HTMLCanvasElement>) => {
    if (ctx && e.shiftKey) {
      const { left, top } = e.currentTarget.getBoundingClientRect();
      const tmp = transformedMousePosition(e.clientX - left, e.clientY - top);
      if (tmp) {
        const zoom = ???;
        ctx.translate(tmp.x, tmp.y);
        ctx.scale(zoom, zoom);
        ctx.translate(-tmp.x, -tmp.y);

        clear();
        draw()
      }
    }
  };

That code works on its own when I setup some logic for the line const zoom = ???; based on e.deltaX or e.deltaY. But ofc it is out of sync with vtk.js scene zoom.

But how can I find out what value should I setup the const zoom = ???; so its zooming is synchronised with vtk.js 3d scene zooming?

Ive tried looking into vtkMouseCameraTrackballZoomToMouseManipulator source code. I found references to delta in few places. A call to dolltToPosition and the factor value but I can’t figure out how to replicat it.

You probably want to map a world xyz distance to some display (xy) coordinate and extract your zoom factor from that. You can listen to camera.onModified(() => ...)

1 Like

@Sebastien_Jourdain Thank you very much it was a good lead.

I end up doing something slightly different but in the same direction. I had a problem with camera.onModified(() => ...) as it was not behaving well on rapid zoom’s.

What I end up doing is passing camera object to the second canvas and using vtkCamera.getParallelScale() and updating onWeel logic to

  const oldps = useRef<number | null>(null);
  // ...
  const onWheel = (e: WheelEvent<HTMLCanvasElement>) => {
    if (ctx && e.shiftKey) {
      const { left, top } = e.currentTarget.getBoundingClientRect();
      const tmp = transformedMousePosition(e.clientX - left, e.clientY - top);
      const newps = vtkCamera.getParallelScale();
      const zoomDelta = oldps.current ? (newps - oldps.current) / newps : 0;
      if (tmp && zoomDelta !== 0) {
        const zoom = 1 - zoomDelta;
        ctx.translate(tmp.x, tmp.y);
        ctx.scale(zoom, zoom);
        ctx.translate(-tmp.x, -tmp.y);

        clearCanvas();
        drawCanvas()
        );
      }
      oldps.current = newps;
    }
  };

Thank you very much, again, for a lead.

Now I need to do pan.

1 Like