diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 9c3299677..6dbfe0ef0 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -32,14 +32,14 @@ jobs: # Get version from pyproject.toml. - name: Get version + subdirectory run: | - VERSION=$(python -c "import toml; print(toml.load('pyproject.toml')['project']['version'])") + VERSION=$(python -c "import viser; print(viser.__version__)") echo "VERSION=$VERSION" >> $GITHUB_ENV echo "DOCS_SUBDIR=versions/$VERSION" >> $GITHUB_ENV # Hack to overwrite version. - - name: Set version to 'latest' for pushes (this will appear in the doc banner) + - name: Set version to 'main' for pushes (this will appear in the doc banner) run: | - python -c "import toml; conf = toml.load('pyproject.toml'); conf['project']['version'] = 'latest'; toml.dump(conf, open('pyproject.toml', 'w'))" + echo "VISER_VERSION_STR_OVERRIDE=main" >> $GITHUB_ENV if: github.event_name == 'push' # Build documentation. diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 36148780b..b1939c7f4 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -30,9 +30,11 @@ jobs: - name: Only bundle client build for PyPI release run: | # This should delete everything in src/viser/client except for the - # build folder. + # build folder + original source files. We don't want to package + # .nodeenv, node_modules, etc in the release. mv src/viser/client/build ./__built_client rm -rf src/viser/client/* + git checkout src/viser/client mv ./__built_client src/viser/client/build - name: Build and publish env: diff --git a/docs/source/conf.py b/docs/source/conf.py index c251c2595..fa544f648 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -6,11 +6,12 @@ # full list see the documentation: # http://www.sphinx-doc.org/en/stable/config -from pathlib import Path +import os from typing import Dict, List import m2r2 -import toml + +import viser # -- Path setup -------------------------------------------------------------- @@ -25,10 +26,7 @@ copyright = "2024" author = "brentyi" -# The short X.Y version -version: str = toml.load( - Path(__file__).absolute().parent.parent.parent / "pyproject.toml" -)["project"]["version"] +version: str = os.environ.get("VISER_VERSION_STR_OVERRIDE", viser.__version__) # Formatting! # 0.1.30 => v0.1.30 diff --git a/docs/source/development.md b/docs/source/development.md index 0b22c0eca..55df4843d 100644 --- a/docs/source/development.md +++ b/docs/source/development.md @@ -31,12 +31,15 @@ pip install -e .[dev] pre-commit install ``` -It would be hard to write unit tests for `viser`. We rely on static typing for -robustness. To check your code, you can run the following: +For code quality, rely primarily on `pyright` and `ruff`: ```bash -# runs linting, formatting, and type-checking -viser-dev-checks +# Check static types. +pyright + +# Lint and format. +ruff check --fix . +ruff format . ``` ## Message updates diff --git a/docs/source/embedded_visualizations.rst b/docs/source/embedded_visualizations.rst new file mode 100644 index 000000000..0cfda5aa9 --- /dev/null +++ b/docs/source/embedded_visualizations.rst @@ -0,0 +1,198 @@ +Embedding Visualizations +=============================================== + +This guide describes how to export 3D visualizations from Viser and embed them into static webpages. The process involves three main steps: exporting scene state, creating a client build, and hosting the visualization. + +.. warning:: + + This workflow is experimental and not yet polished. We're documenting it + nonetheless, since we think it's quite useful! If you have suggestions or + improvements, issues and PRs are welcome. + + +Step 1: Exporting Scene State +---------------------------- + +You can export static or dynamic 3D data from a Viser scene using the scene +serializer. :func:`ViserServer.get_scene_serializer` returns a serializer +object that can serialize the current scene state to a binary format. + +Static Scene Export +~~~~~~~~~~~~~~~~~~~ + +For static 3D visualizations, use the following code to save the scene state: + +.. code-block:: python + + import viser + from pathlib import Path + + server = viser.ViserServer() + + # Add objects to the scene via server.scene + # For example: + # server.scene.add_mesh(...) + # server.scene.add_point_cloud(...) + server.scene.add_box("/box", color=(255, 0, 0), dimensions=(1, 1, 1)) + + # Serialize and save the scene state + data = server.get_scene_serializer().serialize() # Returns bytes + Path("recording.viser").write_bytes(data) + + +As a suggestion, you can also add a button for exporting the scene state: + +.. code-block:: python + + import viser + server = viser.ViserServer() + + # Add objects to the scene via server.scene. + # For example: + # server.scene.add_mesh(...) + # server.scene.add_point_cloud(...) + server.scene.add_box("/box", color=(255, 0, 0), dimensions=(1, 1, 1)) + + save_button = server.gui.add_button("Save Scene") + + @save_button.on_click + def _(event: viser.GuiEvent) -> None: + assert event.client is not None + event.client.send_file_download("recording.viser", server.get_scene_serializer().serialize()) + + server.sleep_forever() + +Dynamic Scene Export +~~~~~~~~~~~~~~~~~~~~ + +For dynamic visualizations with animation, you can create a "3D video" by inserting sleep commands between frames: + +.. code-block:: python + + import viser + import numpy as np + from pathlib import Path + + server = viser.ViserServer() + + # Add objects to the scene via server.scene + # For example: + # server.scene.add_mesh(...) + # server.scene.add_point_cloud(...) + box = server.scene.add_box("/box", color=(255, 0, 0), dimensions=(1, 1, 1)) + + # Create serializer. + serializer = server.get_scene_serializer() + + num_frames = 100 + for t in range(num_frames): + # Update existing scene objects or add new ones. + box.position = (0.0, 0.0, np.sin(t / num_frames * 2 * np.pi)) + + # Add a frame delay. + serializer.insert_sleep(1.0 / 30.0) + + # Save the complete animation. + data = serializer.serialize() # Returns bytes + Path("recording.viser").write_bytes(data) + +.. note:: + Always add scene elements using :attr:`ViserServer.scene`, not :attr:`ClientHandle.scene`. + +.. note:: + The ``.viser`` file is a binary format containing scene state data and is not meant to be human-readable. + +Step 2: Creating a Viser Client Build +----------------------------------- + +To serve the 3D visualization, you'll need two things: + +1. The ``.viser`` file containing your scene data +2. A build of the Viser client (static HTML/JS/CSS files) + +With Viser installed, create the Viser client build using the command-line tool: + +.. code-block:: bash + + # View available options + viser-build-client --help + + # Build to a specific directory + viser-build-client --output-dir viser-client/ + + +Step 3: Hosting +--------------- + +Directory Structure +~~~~~~~~~~~~~~~~~~~ + +For our hosting instructions, we're going to assume the following directory structure: + +.. code-block:: + + . + ├── recordings/ + │ └── recording.viser # Your exported scene data + └── viser-client/ + ├── index.html # Generated client files + ├── assets/ + └── ... + +This is just a suggestion; you can structure your files however you like. + +Local Development Server +~~~~~~~~~~~~~~~~~~~~~~~~ + +For testing locally, you can use Python's built-in HTTP server: + +.. code-block:: bash + + # Navigate to the parent directory containing both folders + cd /path/to/parent/dir + + # Start the server (default port 8000) + python -m http.server 8000 + +Then open your browser and navigate to: + +* ``http://localhost:8000/viser-client/`` (default port) + +This should show the a standard Viser client. To visualize the exported scene, you'll need to specify a URL via the ``?playbackPath=`` parameter: + +* ``http://localhost:8000/viser-client/?playbackPath=http://localhost:8000/recordings/recording.viser`` + + +GitHub Pages Deployment +~~~~~~~~~~~~~~~~~~~~~~~ + +To host your visualization on GitHub Pages: + +1. Create a new repository or use an existing one +2. Create a ``gh-pages`` branch or enable GitHub Pages on your main branch +3. Push your directory structure to the repository: + + .. code-block:: bash + + git add recordings/ viser-client/ + git commit -m "Add Viser visualization" + git push origin main # or gh-pages + +Your visualization will be available at: ``https://user.github.io/repo/viser-client/?playbackPath=https://user.github.io/repo/recordings/recording.viser`` + +You can embed this into other webpages using an HTML ``