{ "cells": [ { "cell_type": "markdown", "id": "a1b2c3d4", "metadata": {}, "source": [ "# Built-in Renderers\n", "\n", "The `webgpu` package ships several renderers for common scientific\n", "visualization tasks. They handle shader code, buffer layouts, and\n", "instancing internally — you just provide data." ] }, { "cell_type": "markdown", "id": "b2c3d4e5", "metadata": {}, "source": [ "## ShapeRenderer — instanced geometry\n", "\n", "`ShapeRenderer` draws many copies (*instances*) of one shape efficiently.\n", "You define the base shape once, then provide arrays of positions, directions,\n", "and colors or scalar values.\n", "\n", "Shapes are created with `generate_cylinder`, `generate_cone`, etc. They can\n", "be combined with `+` and repositioned with `.move()` to build compound shapes\n", "like arrows." ] }, { "cell_type": "code", "execution_count": null, "id": "c3d4e5f6", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from webgpu.shapes import generate_cylinder, generate_cone, ShapeRenderer\n", "from webgpu.colormap import Colormap, Colorbar\n", "from webgpu.jupyter import Draw\n", "\n", "# Build an arrow from cylinder + cone\n", "shaft = generate_cylinder(n=12, radius=0.015, height=0.7, bottom_face=True)\n", "tip = generate_cone(n=12, radius=0.05, height=0.3, bottom_face=True).move((0, 0, 0.7))\n", "arrow = shaft + tip\n", "\n", "# Place 12 arrows in a ring, pointing tangentially\n", "n = 12\n", "pos, dirs, vals = [], [], []\n", "for i in range(n):\n", " angle = 2 * np.pi * i / n\n", " pos.append([0.5 * np.cos(angle), 0.5 * np.sin(angle), 0])\n", " dirs.append([-np.sin(angle), np.cos(angle), 0])\n", " vals.extend([i / (n - 1)] * 2) # two values per shape (start, end)\n", "\n", "cmap = Colormap(colormap=\"viridis\")\n", "renderer = ShapeRenderer(arrow, positions=pos, directions=dirs, values=vals, colormap=cmap)\n", "Draw([renderer, Colorbar(cmap)])" ] }, { "cell_type": "markdown", "id": "d4e5f6a7", "metadata": {}, "source": [ "## TriangulationRenderer — triangle meshes\n", "\n", "`TriangulationRenderer` renders raw triangles from a flat array of vertex\n", "coordinates. It computes face normals and applies lighting automatically.\n", "\n", "This is useful for parametric surfaces, imported meshes, or any geometry\n", "defined as triangles." ] }, { "cell_type": "code", "execution_count": null, "id": "e5f6a7b8", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from webgpu.triangles import TriangulationRenderer\n", "from webgpu.jupyter import Draw\n", "\n", "\n", "def parametric_mesh(func, u_range, v_range, nu=40, nv=40):\n", " \"\"\"Sample a parametric surface on a (u, v) grid and return triangle vertices.\"\"\"\n", " u = np.linspace(*u_range, nu)\n", " v = np.linspace(*v_range, nv)\n", " U, V = np.meshgrid(u, v)\n", " X, Y, Z = func(U, V)\n", "\n", " triangles = []\n", " for i in range(nv - 1):\n", " for j in range(nu - 1):\n", " p00 = [X[i, j], Y[i, j], Z[i, j]]\n", " p10 = [X[i+1, j], Y[i+1, j], Z[i+1, j]]\n", " p01 = [X[i, j+1], Y[i, j+1], Z[i, j+1]]\n", " p11 = [X[i+1, j+1], Y[i+1, j+1], Z[i+1, j+1]]\n", " triangles.extend([p00, p10, p01])\n", " triangles.extend([p10, p11, p01])\n", "\n", " return np.array(triangles, dtype=np.float32).reshape(-1)\n", "\n", "\n", "def torus(u, v, R=0.6, r=0.25):\n", " x = (R + r * np.cos(v)) * np.cos(u)\n", " y = (R + r * np.cos(v)) * np.sin(u)\n", " z = r * np.sin(v)\n", " return x, y, z\n", "\n", "\n", "points = parametric_mesh(torus, (0, 2 * np.pi), (0, 2 * np.pi), nu=48, nv=24)\n", "Draw(TriangulationRenderer(points, color=(0.3, 0.7, 1.0, 1.0)))" ] }, { "cell_type": "markdown", "id": "f6a7b8c9", "metadata": {}, "source": [ "## Combining renderers\n", "\n", "All renderer types can be mixed freely in one scene by passing a list to `Draw`." ] }, { "cell_type": "code", "execution_count": null, "id": "a7b8c9d0", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "from webgpu.shapes import generate_cylinder, generate_cone, ShapeRenderer\n", "from webgpu.triangles import TriangulationRenderer\n", "from webgpu.jupyter import Draw\n", "\n", "# A small triangle mesh\n", "mesh = TriangulationRenderer(\n", " [(0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 0, 0), (0, 1, 0), (1, 1, 0)],\n", " color=(0.2, 0.8, 0.4, 1.0),\n", ")\n", "\n", "# Cylinders at the corners\n", "cyl = generate_cylinder(n=16, radius=0.03, height=0.3, top_face=True, bottom_face=True)\n", "markers = ShapeRenderer(\n", " cyl,\n", " positions=[[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 0]],\n", " directions=[[0, 0, 1]] * 4,\n", " colors=[[1, 0.3, 0.1, 1]] * 4,\n", ")\n", "\n", "Draw([mesh, markers])" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.11.0" } }, "nbformat": 4, "nbformat_minor": 5 }