{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 数据结构\n", "\n", "在 vtk.js 中,因为我们主要关注渲染,我们只有两种类型的数据结构。我们有 vtkPolyData,可以用于几何渲染,还有 vtkImageData,可以用于体积渲染。在VTK中,我们有更多类型的数据集,并且有几个过滤器可以帮助你从一种类型转换为另一种类型。\n", "\n", "在这里,我们解释了这些数据结构的一些基础,以便如果你想要的话,可以手动创建它们。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## ImageData\n", "\n", "ImageData 是隐式的、轴对齐的网格,如下图所示。\n", "\n", "![](images/imagedata.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "ImageData 可以设置的属性如下:\n", "\n", "- `origin`:网格左下角在三维世界中的位置\n", "- `dimensions`:每个轴上有多少个点\n", "- `spacing`:每个轴上点之间的均匀间距\n", "一个具体的例子是,每个轴上有 5 个点或 4 个单元格的网格,沿每个轴的范围从 $[-2, 2]$。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "ImageData(dimensions=[5, 5, 5], origin=[-2, -2, -2], spacing=[1, 1, 1])" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "import dash_vtk\n", "dash_vtk.ImageData(\n", " dimensions=[5,5,5],\n", " origin=[-2,-2,-2],\n", " spacing=[1,1,1],\n", ")" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Div(children=[View([VolumeRepresentation([VolumeController(None), ImageData(children=[PointData([DataArray(registration='setScalars', values=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124])])], dimensions=[5, 5, 5], origin=[-2, -2, -2], spacing=[1, 1, 1])])])], style={'width': '100%', 'height': '400px'})" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from dash import html\n", "html.Div([dash_vtk.View([\n", " dash_vtk.VolumeRepresentation([\n", " # GUI to control Volume Rendering\n", " # + Setup good default at startup\n", " dash_vtk.VolumeController(),\n", " # Actual Imagedata\n", " dash_vtk.ImageData(\n", " dimensions=[5, 5, 5],\n", " origin=[-2, -2, -2],\n", " spacing=[1, 1, 1],\n", " children=[\n", " dash_vtk.PointData([\n", " dash_vtk.DataArray(\n", " registration=\"setScalars\",\n", " values=list(range(5*5*5)),\n", " )\n", " ])\n", " ],\n", " ),\n", " ]),\n", "])], style={\"width\": \"100%\", \"height\": \"400px\"})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## PolyData\n", "\n", "`PolyData` 是由点和单元组成的表面网格。单元可以是:\n", "\n", "- `verts`:顶点或点,在屏幕上显示为一个小方块\n", "- `lines`:将点连接成一段或多段线的线\n", "- `polys`:凸面多边形,如三角形、矩形、圆形等\n", "- `strips`:三角形带有效地将三角形组合在一起,没有重复的点,只是为了连通性\n", "\n", "单元的定义方式是通过基于索引的数组来映射到给定的点索引。例如,假设你想创建一条有 2 个分段的线,你至少需要在点数组中定义 3 个点。如果这些点在你的点数组中是首先定义的,那么线数组应该填充如下:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "nb_points = 3\n", "lines = [nb_points, 0, 1, 2]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "要创建两条相互独立的线,你可以按照以下方式操作:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "lines = [\n", " 3, 0, 1, 2, # First line of 2 segments / 3 points\n", " 2, 3, 4, # Second line of 1 segment / 2 points\n", " 4, 10, 11, 12, 14 # Third line of 3 segments / 4 points\n", "]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "你可以在下面的图片中看到一个具体的例子:\n", "\n", "![](images/polydata.jpg)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Div(children=[View([GeometryRepresentation([PolyData(children=[PointData([DataArray(name='onPoints', values=[0, 0.33, 0.66, 1])]), CellData([DataArray(name='onCells', values=[0, 1])])], lines=[3, 1, 3, 2], points=[0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0], polys=[3, 0, 1, 2])])])], style={'width': '100%', 'height': '400px'})" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from dash import html\n", "import dash_vtk\n", "\n", "html.Div([dash_vtk.View([\n", " dash_vtk.GeometryRepresentation(\n", " children=[\n", " dash_vtk.PolyData(\n", " points=[\n", " 0,0,0,\n", " 1,0,0,\n", " 0,1,0,\n", " 1,1,0,\n", " ],\n", " lines=[3, 1, 3, 2],\n", " polys=[3, 0, 1, 2],\n", " children=[\n", " dash_vtk.PointData([\n", " dash_vtk.DataArray(\n", " #registration='setScalars', # To activate field\n", " name='onPoints',\n", " values=[0, 0.33, 0.66, 1],\n", " )\n", " ]),\n", " dash_vtk.CellData([\n", " dash_vtk.DataArray(\n", " # registration='setScalars', # To activate field\n", " name='onCells',\n", " values=[0, 1],\n", " )\n", " ])\n", " ],\n", " ),\n", " ],\n", " ),\n", "])], style={\"width\": \"100%\", \"height\": \"400px\"})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`dash_vtk.PolyData` 元素有一个额外的属性,可以根据点数组中定义的点的顺序的某些假设自动生成单元。该属性名为 `connectivity`,默认值为 `manual`,意味着不会采取任何自动操作。但是,可以将该属性设置为 `points`,以自动将顶点设置为实际查看提供的点,或者设置为 `triangles`,它使用每组连续的 3 个点创建三角形,最后是 `strips`,它消耗所有的点,形成一条三角形带。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fields\n", "\n", "拥有一个网格是一个良好的开始,但大多数情况下,你可能希望将一个字段附加到给定的网格上,这样你就可以在 3D 上下文中开始查看它。\n", "\n", "`Fields` 是映射到点或单元的数组。它们可以是不同大小的标量或向量。\n", "\n", "下面的图表试图解释位于点上的字段与渲染中的单元字段之间的区别,但根据你所拥有的数据类型,它也确实有着不同的含义。\n", "\n", "![](images/fields.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "注意:按照惯例,我们总是将数据附加到 `ImageData` 的点上进行体积渲染,并且数组必须注册为标量。" ] } ], "metadata": { "kernelspec": { "display_name": "py311", "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.11.7" } }, "nbformat": 4, "nbformat_minor": 2 }