{ "cells": [ { "cell_type": "markdown", "id": "a1", "metadata": {}, "source": [ "# Isosurfaces\n", "\n", "Isosurface rendering extracts and displays the zero level-set of a scalar function inside a 3D mesh. A second function can be colored on the isosurface." ] }, { "cell_type": "markdown", "id": "a2", "metadata": {}, "source": [ "## Basic Isosurface\n", "\n", "Render the zero level-set of a function, colored by another function:" ] }, { "cell_type": "code", "execution_count": null, "id": "a3", "metadata": {}, "outputs": [], "source": [ "from netgen.occ import *\n", "from ngsolve import *\n", "from ngsolve_webgpu.mesh import MeshData\n", "from ngsolve_webgpu.cf import FunctionData\n", "from ngsolve_webgpu.isosurface import IsoSurfaceRenderer, NegativeSurfaceRenderer, NegativeClippingRenderer\n", "from webgpu.clipping import Clipping\n", "from webgpu.colormap import Colorbar, Colormap\n", "from webgpu.jupyter import Draw\n", "\n", "box = Box((-1, -1, -1), (1, 1, 1))\n", "mesh = Mesh(OCCGeometry(box).GenerateMesh(maxh=0.2))\n", "\n", "gf = GridFunction(H1(mesh, order=2))\n", "levelset = 0.8**2 - (x**2 + y**2 + z**2)\n", "gf.Set(levelset)\n", "\n", "mesh_data = MeshData(mesh)\n", "colormap = Colormap()\n", "clipping = Clipping()\n", "\n", "func_data = FunctionData(mesh_data, x, order=2)\n", "levelset_data = FunctionData(mesh_data, -gf, order=2)\n", "\n", "iso = IsoSurfaceRenderer(func_data, levelset_data, clipping, colormap)\n", "neg_surface = NegativeSurfaceRenderer(func_data, levelset_data, clipping=clipping, colormap=colormap)\n", "neg_clip = NegativeClippingRenderer(func_data, levelset_data, clipping, colormap)\n", "\n", "clipping.mode = clipping.Mode.PLANE\n", "scene = Draw([iso, neg_clip, neg_surface, Colorbar(colormap)])" ] }, { "cell_type": "markdown", "id": "a4", "metadata": {}, "source": [ "Three renderers work together:\n", "\n", "- **`IsoSurfaceRenderer`** — extracts and renders the zero level-set surface via a compute shader. The level-set data is negated by convention (negative = inside).\n", "- **`NegativeSurfaceRenderer`** — renders the outer mesh surface only where the level-set is negative (inside the domain).\n", "- **`NegativeClippingRenderer`** — renders the clipping cross-section only inside the level-set domain." ] }, { "cell_type": "markdown", "id": "a5", "metadata": {}, "source": [ "## Updating the Level Set\n", "\n", "The isosurface updates when the underlying `GridFunction` changes and `scene.redraw()` is called." ] }, { "cell_type": "code", "execution_count": null, "id": "a6", "metadata": {}, "outputs": [], "source": [ "gf.Set(0.5**2 - (x**2 + y**2 + z**2))\n", "scene.redraw()" ] }, { "cell_type": "code", "execution_count": null, "id": "deb6e1a2-afd5-4b68-b9ce-20dc7b2ba4a8", "metadata": {}, "outputs": [], "source": [] } ], "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 }