Source code for fvdb.viz._viewer_server

# Copyright Contributors to the OpenVDB Project
# SPDX-License-Identifier: Apache-2.0
#
from __future__ import annotations

import uuid
import warnings
import webbrowser

from .._fvdb_cpp import Viewer as ViewerCpp

# Global viewer server. Create by calling init()
_viewer_server_cpp: ViewerCpp | None = None


def _get_viewer_server_cpp() -> ViewerCpp:
    """
    Get the global viewer server C++ instance or raise a :class:`RuntimeError` if it is not initialized.

    Returns:
        viewer_server (ViewerCpp): The global viewer server C++ instance.

    """
    global _viewer_server_cpp
    if _viewer_server_cpp is None:
        raise RuntimeError("Viewer server is not initialized. Call fvdb.viz.init() first.")
    return _viewer_server_cpp


[docs] def init( ip_address: str = "127.0.0.1", port: int = 8080, vk_device_id: int = 0, verbose: bool = False, ): """ Initialize the viewer web-server on the given IP address and port. You must call this function first before visualizing any scenes. Example usage: .. code-block:: python import fvdb # Initialize the viewer server on localhost:8080 fvdb.viz.init(ip_address="127.0.0.1", port=8080) # Add a scene to the viewer with a point cloud in the scene scene = fvdb.viz.Scene("My Scene") scene.add_point_cloud(...) # Show the viewer in the browser or inline in a Jupyter notebook fvdb.viz.show() # Keep the script running until the user interrupts fvdb.viz.wait_for_interrupt() .. note:: If the viewer server is already initialized, this function will do nothing and will print a warning message. Args: ip_address (str): The IP address to bind the viewer server to. Default is ``"127.0.0.1"``. port (int): The port to bind the viewer server to. Default is ``8080``. vk_device_id (int): The Vulkan device ID to use for rendering. Default is ``0``. verbose (bool): If True, the viewer server will print verbose output to the console. Default is ``False``. """ global _viewer_server_cpp if _viewer_server_cpp is None: try: import nanovdb_editor as editor compiler = editor.Compiler() compute = editor.Compute(compiler) di = compute.device_interface() di.create_device_manager(enable_validation=False) di.create_device(device_index=vk_device_id, enable_external_usage=False) except Exception as e: raise RuntimeError( f"Failed to create Vulkan device with ID {vk_device_id}. You may have an incompatible version of Vulkan installed." ) from e _viewer_server_cpp = ViewerCpp(ip_address=ip_address, port=port, device_id=vk_device_id, verbose=verbose) else: warnings.warn( f"Viewer server is already initialized with IP = {_viewer_server_cpp.ip_address()} and port = {_viewer_server_cpp.port()}." )
[docs] def show(): """ Show an interactive viewer in the browser or inline in a Jupyter notebook. Example usage: .. code-block:: python import fvdb # Initialize the viewer server on localhost:8080 fvdb.viz.init(ip_address="127.0.0.1", port=8080) # Add a scene to the viewer with a point cloud in the scene scene = fvdb.viz.Scene("My Scene") scene.add_point_cloud(...) # Show the viewer in the browser or inline in a Jupyter notebook fvdb.viz.show() # Keep the script running until the user interrupts fvdb.viz.wait_for_interrupt() .. note:: You must call :func:`fvdb.viz.init()` before calling this function. If the viewer server is not initialized, this function will raise a RuntimeError. """ viewer_server = _get_viewer_server_cpp() viewer_server_ip: str = viewer_server.ip_address() viewer_server_port: int = viewer_server.port() url = f"http://{viewer_server_ip}:{viewer_server_port}" try: from IPython import get_ipython from IPython.display import HTML, display if get_ipython() is not None: # Generate a unique ID for this viewer instance viewer_id = f"viewer-container-{uuid.uuid4().hex[:8]}" html_content = f""" <div id="{viewer_id}"></div> <script> (function() {{ // Get the current page's protocol and hostname (without port) // This ensures we construct the URL correctly even when Jupyter // is running on a non-default port var protocol = window.location.protocol; // e.g., "http:" or "https:" var hostname = window.location.hostname; // e.g., "localhost" or "example.com" var viewerPort = "{viewer_server_port}"; // Explicitly construct the viewer URL var viewerUrl = protocol + "//" + hostname + ":" + viewerPort; console.log("Viewer URL: " + viewerUrl); // Create and insert the iframe var iframe = document.createElement('iframe'); iframe.src = viewerUrl; iframe.width = '100%'; iframe.height = '600px'; iframe.style.border = 'none'; var container = document.getElementById('{viewer_id}'); if (container) {{ container.appendChild(iframe); }} else {{ console.error('Could not find container element with id: {viewer_id}'); }} }})(); </script> """ display(HTML(html_content)) return except ImportError: pass webbrowser.open_new_tab(url)
def reset(): """ Reset the viewer server state. This will clear all scenes and views and ads back the default scene. """ viewer_server = _get_viewer_server_cpp() viewer_server.reset() def remove_scene(scene_name: str): """ Remove a scene from the viewer server. Args: scene_name (str): The name of the scene to remove. """ viewer_server = _get_viewer_server_cpp() viewer_server.remove_scene(scene_name) def remove_view(scene_name: str, name: str): """ Remove a view from the viewer server. Args: scene_name (str): The name of the scene view belongs to. name (str): The name of the view to remove. """ viewer_server = _get_viewer_server_cpp() viewer_server.remove_view(scene_name, name)
[docs] def wait_for_interrupt(): """ Block execution until the viewer is interrupted by the user. This function blocks the current thread until the viewer receives an interrupt signal (via Ctrl-C). Use this to keep a script running while interacting with the viewer. """ viewer_server = _get_viewer_server_cpp() viewer_server.wait_for_interrupt()