绘制等值面
<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; 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 &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 &gt; colorMax) {
color = colorInterpolator(config.lineNum - 1);
}
if (value &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; 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 &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 &gt; colorMax) {
color = colorInterpolator(config.lineNum - 1);
}
if (value &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>