绘制2D云图
<h3>1. 从数据中提取和处理</h3>
<pre><code>let renderData = getCludeData(data, config.dataSource);
const [minL, maxL] = d3.extent(renderData);
let colorInterpolator = interpolateColor(config.color, minL, maxL);</code></pre>
<ul>
<li>getCludeData(data, config.dataSource):这个函数根据数据源配置从原始数据中提取要渲染的部分。这里 data 是原始数据,config.dataSource 是指定数据来源的配置。</li>
<li>d3.extent(renderData):使用 D3.js 库的 extent 方法计算 renderData 的最小值和最大值。这个范围用于颜色映射。</li>
<li>interpolateColor(config.color, minL, maxL):创建一个颜色插值器,使用 config.color 中的颜色来映射 minL 到 maxL 范围内的数据值。这个插值器将数据值转换为相应的颜色。</li>
</ul>
<h3>2. 创建图像数据并设置颜色</h3>
<pre><code>const ImgData = new ImageData(data.x_grid, data.y_grid);
for (let i = 0, j = 0; i &lt; renderData.length; i++, j += 4) {
const value = renderData[i];
if (isNaN(value)) {
ImgData.data[j] = 0; // R channel
ImgData.data[j + 1] = 0; // G channel
ImgData.data[j + 2] = 0; // B channel
ImgData.data[j + 3] = 0; // A channel
} else {
const color = d3.rgb(colorInterpolator(value));
ImgData.data[j] = color.r; // R channel
ImgData.data[j + 1] = color.g; // G channel
ImgData.data[j + 2] = color.b; // B channel
ImgData.data[j + 3] = config.colorOpacity / 100 * 255; // A channel
}
}</code></pre>
<ul>
<li>new ImageData(data.x_grid, data.y_grid):创建一个新的 ImageData 对象,用于存储图像的像素数据。data.x_grid 和 data.y_grid 是图像的宽度和高度。</li>
<li>循环遍历 renderData 并将其值转换为图像的颜色数据:</li>
<li>isNaN(value):检查数据值是否为 NaN(表示无效数据),如果是,则将对应的像素设置为完全透明。</li>
<li>d3.rgb(colorInterpolator(value)):将数据值转换为颜色。</li>
<li>设置 ImgData.data[j]、ImgData.data[j + 1]、ImgData.data[j + 2] 和 ImgData.data[j + 3] 分别对应红色、绿色、蓝色和透明度通道。
<h3>3. 配置 Canvas</h3></li>
</ul>
<pre><code>const canvas = document.createElement('canvas');
const context = canvas.getContext('2d', { willReadFrequently: true });
const canvasElm = options.canvas;
const canvasElmWidth = canvasElm.width;
const canvasElmHeight = canvasElm.height;
const scaleZoom = Math.floor(d3.min([canvasElmWidth / data.x_grid, canvasElmHeight / data.y_grid])) / 2;
canvas.width = data.x_grid * scaleZoom;
canvas.height = data.y_grid * scaleZoom;</code></pre>
<ul>
<li>创建一个新的 canvas 元素,并获取其 2D 上下文 context。</li>
<li>从 options.canvas 获取已有 Canvas 元素的宽度和高度。</li>
<li>计算缩放比例 scaleZoom,以确保图像适合在 Canvas 上显示。如果图像的尺寸大于 Canvas 的尺寸,则缩放图像。</li>
<li>根据计算的缩放比例设置 Canvas 的宽度和高度。</li>
</ul>
<h3>4. 图像插值和处理</h3>
<pre><code>let res = null;
if (config.interpolationMethod === '1') {
const newImageData1 = ImageInterpolation.bicubicInterpolation(ImgData, canvas.width, canvas.height);
res = await createImageBitmap(newImageData1, 0, 0, canvas.width, canvas.height);
} else {
res = await createImageBitmap(ImgData, 0, 0, data.x_grid, data.y_grid);
}</code></pre>
<ul>
<li>根据 config.interpolationMethod 选择插值方法:</li>
<li>如果方法是 '1',则使用 bicubicInterpolation 函数进行三次插值,以平滑图像数据。</li>
<li>否则,直接创建 ImageBitmap 对象。</li>
<li>createImageBitmap:将图像数据转换为 ImageBitmap 对象,用于高效显示图像。
<h3>5. 渲染图像到 Canvas</h3></li>
</ul>
<pre><code>context.drawImage(res, 0, 0, canvas.width, canvas.height);
const newImageData = context.getImageData(0, 0, canvas.width, canvas.height);</code></pre>
<ul>
<li>使用 context.drawImage 将处理后的图像绘制到 Canvas 上。</li>
<li>context.getImageData 获取绘制后的图像数据,用于进一步处理或显示。</li>
</ul>
<h3>6. VTK 渲染设置</h3>
<pre><code>const renderer = vtkRenderer.newInstance();
renderer.setBackground(0, 0, 0, 0);
const vtkTexture = window.vtk.Rendering.Core.vtkTexture.newInstance();
vtkTexture.setInterpolate(true);
vtkTexture.setJsImageData(newImageData);
const planeSource = window.vtk.Filters.Sources.vtkPlaneSource.newInstance();
planeSource.setCenter(0, 0, 0);
planeSource.setNormal(0, 0, 1);
const mapper = window.vtk.Rendering.Core.vtkMapper.newInstance();
mapper.setInputConnection(planeSource.getOutputPort());
const actor = window.vtk.Rendering.Core.vtkActor.newInstance();
actor.setMapper(mapper);
actor.addTexture(vtkTexture);
renderer.addActor(actor);
renderer.setLayer(2);</code></pre>
<ul>
<li>创建 VTK 渲染器实例 vtkRenderer 并设置背景色为透明(0, 0, 0, 0)。</li>
<li>创建 VTK 纹理 vtkTexture 并将 Canvas 上的图像数据加载到纹理中。启用插值以提高纹理质量。</li>
<li>创建 VTK 平面源 vtkPlaneSource 并设置平面的中心和法线方向。</li>
<li>创建 VTK 映射器 vtkMapper,将平面源连接到映射器。</li>
<li>创建 VTK 演员 vtkActor,将映射器和纹理应用到演员上。</li>
<li>将演员添加到渲染器中,并设置渲染层为 2。</li>
</ul>
<h3>总结</h3>
<p>这段代码的目的是将图像数据处理成适合 3D 渲染的纹理,并在 VTK 渲染器中显示。它涉及从数据中提取图像数据、创建图像数据、配置 Canvas、处理图像数据、渲染图像并将其应用到 VTK 渲染器中。这些步骤确保图像能够以高质量的纹理显示在 3D 环境中。</p>
<p>完整代码</p>
<pre><code class="language-javascript">/**
*
* @param {*} ImgData
* @param {*} bounds
* @param {*} center
* @returns
*/
async addImgClude(data, bounds, options = {}, slice = 1) {
let renderData = getCludeData(data, config.dataSource);
const [minL, maxL] = d3.extent(renderData);
let colorInterpolator = interpolateColor(config.color, minL, maxL);
const ImgData = new ImageData(data.x_grid, data.y_grid);
for (let i = 0, j = 0; i &lt; renderData.length; i++, j += 4) {
const value = renderData[i]; // 拿到模长
// nan数据不显示
if (isNaN(value)) {
ImgData.data[j] = 0; // R channel
ImgData.data[j + 1] = 0; // G channel
ImgData.data[j + 2] = 0; // B channel
ImgData.data[j + 3] = 0; // A 通道
} else {
const color = d3.rgb(colorInterpolator(value));
ImgData.data[j] = color.r; // R channel
ImgData.data[j + 1] = color.g; // G channel
ImgData.data[j + 2] = color.b; // B channel
ImgData.data[j + 3] = config.colorOpacity / 100 * 255; // A 通道
}
}
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d', { willReadFrequently: true });
const canvasElm = options.canvas;
const canvasElmWidth = canvasElm.width;
const canvasElmHeight = canvasElm.height;
// 获取一个最接近原图的缩放比例
const scaleZoom = Math.floor(d3.min([canvasElmWidth / data.x_grid, canvasElmHeight / data.y_grid])) / 2;
canvas.width = data.x_grid * scaleZoom;
canvas.height = data.y_grid * scaleZoom;
let res = null;
// 三次插值算法
if (config.interpolationMethod === '1') {
const newImageData1 = ImageInterpolation.bicubicInterpolation(ImgData, canvas.width, canvas.height);
res = await createImageBitmap(newImageData1, 0, 0, canvas.width, canvas.height);
} else {
res = await createImageBitmap(ImgData, 0, 0, data.x_grid, data.y_grid);
}
context.drawImage(res, 0, 0, canvas.width, canvas.height);
const newImageData = context.getImageData(0, 0, canvas.width, canvas.height);
const renderer = vtkRenderer.newInstance();
// 将渲染器的背景色设置为透明
renderer.setBackground(0, 0, 0, 0); // RGBA 中的 A 通道设置为 0 表示完全透明
// vtk 纹理
const vtkTexture = window.vtk.Rendering.Core.vtkTexture.newInstance();
// 创建平面源
const planeSource = window.vtk.Filters.Sources.vtkPlaneSource.newInstance();
planeSource.setCenter(0, 0, 0); // 平面的中心点
planeSource.setNormal(0, 0, 1); // 平面的法线方向
// 方法用于设置纹理插值。当插值设置为 true 时,它会对纹理进行插值,以确保在图像放大或缩小时保持图像的平滑性。
vtkTexture.setInterpolate(true);
// 直接加载canvas的imgdata数据
vtkTexture.setJsImageData(newImageData);
// 创建映射器
const mapper = window.vtk.Rendering.Core.vtkMapper.newInstance();
mapper.setInputConnection(planeSource.getOutputPort());
// 创建一个actor
const actor = window.vtk.Rendering.Core.vtkActor.newInstance();
actor.setMapper(mapper);
actor.addTexture(vtkTexture);
renderer.addActor(actor);
renderer.setLayer(2);
}</code></pre>