{ "cells": [ { "cell_type": "markdown", "id": "a1", "metadata": {}, "source": [ "# Animation & Live Updates\n", "\n", "Solutions can be updated interactively using `scene.redraw()` or animated over stored timesteps using the `Animation` wrapper. When the documentation is exported to HTML, the `Animation` wrapper additionally serializes every recorded frame into the page so the slider keeps working in the static export — no Python kernel required." ] }, { "cell_type": "markdown", "id": "a2", "metadata": {}, "source": [ "## Live redrawing with parameters\n", "\n", "Update a scene when an NGSolve `Parameter` changes — useful in a live notebook session:" ] }, { "cell_type": "code", "execution_count": null, "id": "a3", "metadata": {}, "outputs": [], "source": [ "from ngsolve import *\n", "from ngsolve_webgpu.jupyter import Draw\n", "\n", "mesh = Mesh(unit_square.GenerateMesh(maxh=0.1))\n", "p = Parameter(10)\n", "scene = Draw(sin(p * x) * sin(10 * y), mesh, order=3)" ] }, { "cell_type": "code", "execution_count": null, "id": "a4", "metadata": {}, "outputs": [], "source": [ "# Change parameter and redraw (only effective in a live kernel)\n", "p.Set(20)\n", "scene.redraw()" ] }, { "cell_type": "markdown", "id": "a8", "metadata": {}, "source": [ "## Timestep animation with `Animation`\n", "\n", "`Animation` wraps a renderer, walks its CoefficientFunction tree for `GridFunction` and `Parameter` objects, and records snapshots when you call `add_time()`. A slider in the GUI scrubs through the recorded frames.\n", "\n", "**Important for HTML export:** record all frames *before* calling `Draw()`. The export hook runs at `Draw()` time, snapshots the per-frame GPU buffers, and embeds them in the page. Frames added after `Draw()` won't appear in the static export." ] }, { "cell_type": "code", "execution_count": null, "id": "a9", "metadata": {}, "outputs": [], "source": [ "from ngsolve import *\n", "from ngsolve_webgpu.mesh import MeshData\n", "from ngsolve_webgpu.cf import FunctionData, CFRenderer\n", "from ngsolve_webgpu.animate import Animation\n", "from webgpu.colormap import Colorbar\n", "from webgpu.jupyter import Draw\n", "\n", "mesh = Mesh(unit_square.GenerateMesh(maxh=0.1))\n", "gf = GridFunction(H1(mesh, order=3))\n", "t = Parameter(0)\n", "f = sin(10 * (x - 0.1 * t))\n", "\n", "gf.Interpolate(f)\n", "\n", "md = MeshData(mesh)\n", "fd = FunctionData(md, gf, order=3)\n", "cfr = CFRenderer(fd)\n", "cfr.colormap.set_min_max(-1, 1)\n", "\n", "ani = Animation(cfr)\n", "ani.add_time() # frame 0 (current state)\n", "\n", "# Record 20 frames before drawing — they will all be embedded in the HTML\n", "for tval in range(1, 21):\n", " t.Set(tval * 5)\n", " gf.Interpolate(f)\n", " ani.add_time()\n", "\n", "Draw([ani, Colorbar(cfr.colormap)])" ] }, { "cell_type": "markdown", "id": "a11", "metadata": {}, "source": [ "Drag the **Frame** slider in the GUI panel to scrub through the recorded timesteps. In the live notebook the same scene also reacts to `add_time()` calls made *after* `Draw` — for example inside a time-stepping loop — and `scene.redraw()` will update the canvas.\n", "\n", "### What gets exported\n", "\n", "For each recorded frame, `Animation.get_export_interactions()` re-evaluates the underlying CF, reads back the relevant GPU buffers (`data_2d`, `data_3d`), and stores them as raw frame snapshots. The JS engine's `time_animation` handler uploads the matching snapshot to the live buffer when the slider moves and triggers a re-render — purely client-side." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.14.5" } }, "nbformat": 4, "nbformat_minor": 5 }