|
7 | 7 | "source": [ |
8 | 8 | "# Isosurfaces\n", |
9 | 9 | "\n", |
10 | | - "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." |
| 10 | + "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.\n", |
| 11 | + "\n", |
| 12 | + "Isolines (contour lines) draw curves of constant value on surfaces and clipping planes, using screen-space derivatives for consistent line width." |
11 | 13 | ] |
12 | 14 | }, |
13 | 15 | { |
|
91 | 93 | "scene.redraw()" |
92 | 94 | ] |
93 | 95 | }, |
| 96 | + { |
| 97 | + "cell_type": "markdown", |
| 98 | + "id": "b1", |
| 99 | + "metadata": {}, |
| 100 | + "source": [ |
| 101 | + "# Isolines\n", |
| 102 | + "\n", |
| 103 | + "Isolines (contour lines) show where a function takes specific values. They are drawn directly in the fragment shader using screen-space derivatives for consistent line width at any zoom level.\n", |
| 104 | + "\n", |
| 105 | + "- `IsolineRenderer` is a subclass of `CFRenderer` that adds isoline rendering.\n", |
| 106 | + "- `ClippingIsolineRenderer` does the same on a clipping plane cross-section.\n", |
| 107 | + "- `show_field=True` renders the colored field with isolines; `show_field=False` (default) renders only the lines." |
| 108 | + ] |
| 109 | + }, |
| 110 | + { |
| 111 | + "cell_type": "markdown", |
| 112 | + "id": "b2", |
| 113 | + "metadata": {}, |
| 114 | + "source": [ |
| 115 | + "### Quick Usage with `Draw`\n", |
| 116 | + "\n", |
| 117 | + "The simplest way to add isolines: pass `isolines=True` or a number of levels to `Draw`." |
| 118 | + ] |
| 119 | + }, |
| 120 | + { |
| 121 | + "cell_type": "code", |
| 122 | + "execution_count": null, |
| 123 | + "id": "b3", |
| 124 | + "metadata": {}, |
| 125 | + "outputs": [], |
| 126 | + "source": [ |
| 127 | + "from ngsolve import *\n", |
| 128 | + "from ngsolve_webgpu import *\n", |
| 129 | + "from ngsolve_webgpu.jupyter import Draw\n", |
| 130 | + "\n", |
| 131 | + "mesh = Mesh(unit_square.GenerateMesh(maxh=0.05))\n", |
| 132 | + "Draw(sin(10*x)*cos(10*y), mesh, order=4, isolines=10)" |
| 133 | + ] |
| 134 | + }, |
| 135 | + { |
| 136 | + "cell_type": "markdown", |
| 137 | + "id": "b4", |
| 138 | + "metadata": {}, |
| 139 | + "source": [ |
| 140 | + "### Programmatic Control\n", |
| 141 | + "\n", |
| 142 | + "For more control, create an `IsolineRenderer` directly. It is a subclass of `CFRenderer` that adds isoline rendering. Use `show_field=True` to also show the colored field underneath." |
| 143 | + ] |
| 144 | + }, |
| 145 | + { |
| 146 | + "cell_type": "code", |
| 147 | + "execution_count": null, |
| 148 | + "id": "b5", |
| 149 | + "metadata": {}, |
| 150 | + "outputs": [], |
| 151 | + "source": [ |
| 152 | + "from ngsolve import *\n", |
| 153 | + "from ngsolve_webgpu import *\n", |
| 154 | + "from webgpu.jupyter import Draw\n", |
| 155 | + "\n", |
| 156 | + "mesh = Mesh(unit_square.GenerateMesh(maxh=0.05))\n", |
| 157 | + "mesh_data = MeshData(mesh)\n", |
| 158 | + "cf = exp(-(10*((x-0.5)**2 + (y-0.5)**2)))\n", |
| 159 | + "function_data = FunctionData(mesh_data, cf, order=4)\n", |
| 160 | + "\n", |
| 161 | + "renderer = IsolineRenderer(function_data, n_lines=12, thickness=2.0, show_field=True)\n", |
| 162 | + "scene = Draw([renderer, Colorbar(renderer.colormap)])" |
| 163 | + ] |
| 164 | + }, |
| 165 | + { |
| 166 | + "cell_type": "code", |
| 167 | + "execution_count": null, |
| 168 | + "id": "b6", |
| 169 | + "metadata": {}, |
| 170 | + "outputs": [], |
| 171 | + "source": [ |
| 172 | + "renderer.isolines.n_lines = 25\n", |
| 173 | + "renderer.isolines.color = (1, 0, 0, 1)\n", |
| 174 | + "scene.redraw()" |
| 175 | + ] |
| 176 | + }, |
| 177 | + { |
| 178 | + "cell_type": "markdown", |
| 179 | + "id": "b7", |
| 180 | + "metadata": {}, |
| 181 | + "source": [ |
| 182 | + "### Isolines of One Function over Another\n", |
| 183 | + "\n", |
| 184 | + "`IsolineRenderer` draws only the isoline pixels and discards everything else, so it can be layered over a different visualization. This lets you show the isolines of one function on top of the color field of another." |
| 185 | + ] |
| 186 | + }, |
| 187 | + { |
| 188 | + "cell_type": "code", |
| 189 | + "execution_count": null, |
| 190 | + "id": "b8", |
| 191 | + "metadata": {}, |
| 192 | + "outputs": [], |
| 193 | + "source": [ |
| 194 | + "from ngsolve import *\n", |
| 195 | + "from ngsolve_webgpu import *\n", |
| 196 | + "from webgpu.jupyter import Draw\n", |
| 197 | + "\n", |
| 198 | + "mesh = Mesh(unit_square.GenerateMesh(maxh=0.03))\n", |
| 199 | + "mesh_data = MeshData(mesh)\n", |
| 200 | + "\n", |
| 201 | + "temperature = FunctionData(mesh_data, sin(3*x)*cos(3*y), order=4)\n", |
| 202 | + "pressure = FunctionData(mesh_data, exp(-5*((x-0.3)**2 + (y-0.7)**2)), order=4)\n", |
| 203 | + "\n", |
| 204 | + "colormap = Colormap()\n", |
| 205 | + "r_field = CFRenderer(temperature, colormap=colormap)\n", |
| 206 | + "r_iso = IsolineRenderer(pressure, n_lines=12, thickness=1)\n", |
| 207 | + "\n", |
| 208 | + "Draw([r_field, r_iso, Colorbar(colormap)])" |
| 209 | + ] |
| 210 | + }, |
| 211 | + { |
| 212 | + "cell_type": "markdown", |
| 213 | + "id": "b9", |
| 214 | + "metadata": {}, |
| 215 | + "source": [ |
| 216 | + "### Isolines on a 3D Clipping Plane\n", |
| 217 | + "\n", |
| 218 | + "Isolines also work on clipping plane cross-sections. Use `ClippingIsolineRenderer` to overlay isolines of a function on the clip plane." |
| 219 | + ] |
| 220 | + }, |
| 221 | + { |
| 222 | + "cell_type": "code", |
| 223 | + "execution_count": null, |
| 224 | + "id": "b10", |
| 225 | + "metadata": {}, |
| 226 | + "outputs": [], |
| 227 | + "source": [ |
| 228 | + "from ngsolve import *\n", |
| 229 | + "from ngsolve_webgpu import *\n", |
| 230 | + "from webgpu.clipping import Clipping\n", |
| 231 | + "from webgpu.jupyter import Draw\n", |
| 232 | + "\n", |
| 233 | + "mesh = Mesh(unit_cube.GenerateMesh(maxh=0.1))\n", |
| 234 | + "mesh_data = MeshData(mesh)\n", |
| 235 | + "cf = sin(5*x)*cos(5*y)*exp(z)\n", |
| 236 | + "function_data = FunctionData(mesh_data, cf, order=3)\n", |
| 237 | + "\n", |
| 238 | + "clipping = Clipping()\n", |
| 239 | + "clipping.mode = clipping.Mode.PLANE\n", |
| 240 | + "clipping.center = [0.5, 0.5, 0.5]\n", |
| 241 | + "\n", |
| 242 | + "colormap = Colormap()\n", |
| 243 | + "clip_iso = ClippingIsolineRenderer(function_data, clipping=clipping, colormap=colormap, n_lines=10, show_field=True)\n", |
| 244 | + "cfr = IsolineRenderer(function_data, n_lines=10, show_field=True, clipping=clipping, colormap=colormap)\n", |
| 245 | + "\n", |
| 246 | + "Draw([cfr, clip_iso, Colorbar(colormap)])" |
| 247 | + ] |
| 248 | + }, |
| 249 | + { |
| 250 | + "cell_type": "markdown", |
| 251 | + "id": "b11", |
| 252 | + "metadata": {}, |
| 253 | + "source": [ |
| 254 | + "### Properties\n", |
| 255 | + "\n", |
| 256 | + "| Property | Description |\n", |
| 257 | + "|---|---|\n", |
| 258 | + "| `renderer.isolines.n_lines` | Number of evenly-spaced levels between colormap min and max |\n", |
| 259 | + "| `renderer.isolines.thickness` | Line width in approximate pixels (default 1.5) |\n", |
| 260 | + "| `renderer.isolines.color` | RGBA tuple for the line color (default black) |\n", |
| 261 | + "| `renderer.isolines.show_field` | Whether to also render the colored field (default False) |" |
| 262 | + ] |
| 263 | + }, |
94 | 264 | { |
95 | 265 | "cell_type": "code", |
96 | 266 | "execution_count": null, |
97 | | - "id": "deb6e1a2-afd5-4b68-b9ce-20dc7b2ba4a8", |
| 267 | + "id": "b12", |
98 | 268 | "metadata": {}, |
99 | 269 | "outputs": [], |
100 | 270 | "source": [] |
|
0 commit comments