vtk.js

vtk.js


绘制等值面

<p>利用 VTK 库生成并渲染多个等值面(contour lines),每个等值面的颜色不同。以下是对代码各部分的详细解释:</p> <p>函数概述</p> <pre><code>async vtkLoad3D3CContourline(data, renderer, options = {}) { ... }</code></pre> <p>vtkLoad3D3CContourline 是一个异步函数,用于在给定的 renderer 上渲染多个等值面。data 包含了体数据源,options 是可选的配置参数。</p> <h3>数据范围和颜色插值</h3> <pre><code>const dataRange = global.RenderVtk.dataRange; const rangeBase = (dataRange[1] - dataRange[0]) / config.lineNum; const colorInterpolator = interpolateColor(config.color, 0, config.lineNum); const colors = [];</code></pre> <ul> <li>dataRange 从全局对象 global.RenderVtk 中获取数据的值范围。</li> <li>rangeBase 计算每个等值面之间的间隔。</li> <li>colorInterpolator 是一个函数,用于根据等值面索引插值颜色。config.color 是颜色数组。</li> <li>colors 用于存储每个等值面的颜色。</li> </ul> <pre><code>if (config.showRangeStart === 0 &amp;amp;&amp;amp; config.showRangeEnd === 1) { config.showRangeStart = dataRange[0]; config.showRangeEnd = dataRange[1]; } const colorMin = config.showRangeStart * (config.scaleBase || 1); const colorMax = config.showRangeEnd * (config.scaleBase || 1);</code></pre> <ul> <li>如果 config.showRangeStart 和 config.showRangeEnd 的初始值是默认的(0 和 1),则将它们设置为数据的实际范围。</li> <li>colorMin 和 colorMax 是根据配置和数据范围计算的颜色范围,用于确定每个等值面的颜色。</li> </ul> <h3>循环创建等值面</h3> <pre><code>for (let index = 0; index &amp;lt; config.lineNum; index++) { const value = dataRange[0] + rangeBase * (index + 1); const vtkImageMarchingCubes = window.vtk.Filters.General.vtkImageMarchingCubes; const actor = vtkActor.newInstance(); const mapper = vtkMapper.newInstance(); const marchingCube = vtkImageMarchingCubes.newInstance({ contourValue: value, computeNormals: true, mergePoints: true }); const vtkXMLImageDataReader = window.vtk.IO.XML.vtkXMLImageDataReader; const vtiReader = vtkXMLImageDataReader.newInstance();</code></pre> <ul> <li>循环 config.lineNum 次,每次创建一个新的等值面。</li> <li>value 是当前等值面的等值值。</li> <li>创建 vtkImageMarchingCubes 实例用于计算等值面,contourValue 设置为当前的 value。</li> <li>actor 和 mapper 分别用于表示和映射数据。</li> </ul> <pre><code>switch (souces) { case '0': vtiReader.parseAsArrayBuffer(data.mag_velocity); break; case '1': vtiReader.parseAsArrayBuffer(data.u_velocity); break; case '2': vtiReader.parseAsArrayBuffer(data.v_velocity); break; case '3': vtiReader.parseAsArrayBuffer(data.w_velocity); break; default: vtiReader.parseAsArrayBuffer(data.array_buffer); break; }</code></pre> <p>根据 souces 的值选择不同的数据源,并将数据解析为 VTI 格式。</p> <pre><code>const vtiData = vtiReader.getOutputData(0); actor.setMapper(mapper); mapper.setInputConnection(marchingCube.getOutputPort()); marchingCube.setInputData(vtiData);</code></pre> <ul> <li>从 VTI 读取器中获取数据,并将其设置为 marchingCube 的输入数据。</li> <li>将 mapper 的输入连接到 marchingCube 的输出端口。</li> <li>将 mapper 设置为 actor 的映射器。</li> </ul> <h3>设置颜色和不透明度</h3> <pre><code>let color = colorInterpolator(index); if (value &amp;gt; colorMax) { color = colorInterpolator(config.lineNum - 1); } if (value &amp;lt; colorMin) { color = colorInterpolator(0); } colors.push(color); const colorRgb = d3.rgb(color); actor.getProperty().setColor(colorRgb.r / 255, colorRgb.g / 255, colorRgb.b / 255); actor.getProperty().setOpacity(config.colorOpacity / 100); // 设置不透明度 renderer.addActor(actor);</code></pre> <ul> <li>使用 colorInterpolator 获取当前等值面的颜色。</li> <li>如果 value 超过 colorMax 或低于 colorMin,则调整颜色以符合范围。</li> <li>将颜色转换为 RGB 格式,并将其设置为 actor 的颜色属性。</li> <li>设置不透明度。</li> <li>将 actor 添加到 renderer 中进行渲染。</li> </ul> <h3>总结</h3> <p>这段代码通过以下步骤实现了等值面的加载和渲染:</p> <ol> <li>计算数据范围和颜色范围:确定每个等值面的位置和颜色。</li> <li>创建并配置等值面:使用 vtkImageMarchingCubes 生成等值面,并配置其颜色和不透明度。</li> <li>将等值面添加到渲染器:每个等值面作为一个 actor 被添加到渲染器中进行显示。</li> </ol> <p>完整代码</p> <pre><code class="language-javascript">/** * 加载等值面 * @param {*} data * @param {*} renderer */ async vtkLoad3D3CContourline(data, renderer, options = {}) { const dataRange = global.RenderVtk.dataRange; const rangeBase = (dataRange[1] - dataRange[0]) / config.lineNum; // 绘制6个等值面 每个面颜色不一致 const colorInterpolator = interpolateColor(config.color, 0, config.lineNum); const colors = []; if (config.showRangeStart === 0 &amp;amp;&amp;amp; config.showRangeEnd === 1) { config.showRangeStart = dataRange[0]; config.showRangeEnd = dataRange[1]; } const colorMin = config.showRangeStart * (config.scaleBase || 1); const colorMax = config.showRangeEnd * (config.scaleBase || 1); for (let index = 0; index &amp;lt; config.lineNum; index++) { const value = dataRange[0] + rangeBase * (index + 1); const vtkImageMarchingCubes = window.vtk.Filters.General.vtkImageMarchingCubes; const actor = vtkActor.newInstance(); const mapper = vtkMapper.newInstance(); const marchingCube = vtkImageMarchingCubes.newInstance({ contourValue: value, computeNormals: true, mergePoints: true }); const vtkXMLImageDataReader = window.vtk.IO.XML.vtkXMLImageDataReader; const vtiReader = vtkXMLImageDataReader.newInstance(); // 不同的数据源加载 switch (souces) { case '0': vtiReader.parseAsArrayBuffer(data.mag_velocity); break; case '1': vtiReader.parseAsArrayBuffer(data.u_velocity); break; case '2': vtiReader.parseAsArrayBuffer(data.v_velocity); break; case '3': vtiReader.parseAsArrayBuffer(data.w_velocity); break; default: vtiReader.parseAsArrayBuffer(data.array_buffer); break; } const vtiData = vtiReader.getOutputData(0); actor.setMapper(mapper); mapper.setInputConnection(marchingCube.getOutputPort()); marchingCube.setInputData(vtiData); let color = colorInterpolator(index); if (value &amp;gt; colorMax) { color = colorInterpolator(config.lineNum - 1); } if (value &amp;lt; colorMin) { color = colorInterpolator(0); } // const firstIsoValue = (dataRange[0] + dataRange[1]) / 3; // marchingCube.setContourValue(firstIsoValue); colors.push(color); const colorRgb = d3.rgb(color); actor.getProperty().setColor(colorRgb.r / 255, colorRgb.g / 255, colorRgb.b / 255); actor.getProperty().setOpacity(config.colorOpacity / 100); // 设置不透明度 renderer.addActor(actor); } }</code></pre>

页面列表

ITEM_HTML