Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
dataDescription = ({
name: "Engine",
acknowledgement: "volvis.org and General Electric",
description: "CT scan of two cylinders of an engine block",
link: "https://klacansky.com/open-scivis-datasets/",
dataType: "uint8",
endian: "little",
scale: [1, 1, 1],
xExtent: 256,
yExtent: 256,
zExtent: 128
})
Insert cell
Insert cell
dataValues = {
const zip = await FileAttachment("engine_256x256x128_uint8.zip").zip(); // Retrieve attachment and decompress it.
const file = zip.file("engine_256x256x128_uint8.raw"); // Get the actual data file in the decompressed archive.
const buffer = await file.arrayBuffer(); // Get the byte buffer of the file.
const byteArray = new Uint8Array(buffer); // Create an uint8-array-view of the file buffer.
return byteArray; // Return the data as an uint8 array.
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function createZSliceImage(ctx, data, width, height, sliceIndex) {
// Create image buffer with the same width and height as the dataset.
// The image data has 4 channels (red, green, blue, alpha).
const image = ctx.createImageData(width, height);

// Loop through all pixels in the image and set the color based on the data.
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
// Compute the data index in the 3D volume.
// The first part is the offset to the current volume slice (width * height * sliceIndex).
// The second part is the offset inside the slice to the current voxel (width * y + x).
const dataIndex = (width * height * sliceIndex) + (width * y + x);

// ✔️ SOLUTION for exercise 2 (part 1) ✔️
const value = invertColor ? 255 - data[dataIndex] : data[dataIndex];

// ✔️ SOLUTION for exercise 2 (part 2) ✔️
const color = d3.color(colorScale(value / 255))

// Compute the index of the pixel in the image data, considering the 4 color channels.
const imageIndex = (width * y + x) * 4;

// Set the pixel color.
image.data[imageIndex] = color.r; // Red ✔️ Exercise 2 ✔️
image.data[imageIndex + 1] = color.g; // Green ✔️ Exercise 2 ✔️
image.data[imageIndex + 2] = color.b; // Blue ✔️ Exercise 2 ✔️
image.data[imageIndex + 3] = 255; // Alpha
}
}

// Asynchronously create a bitmap from the image data (returns a promise).
return createImageBitmap(image);
}
Insert cell
Insert cell
Insert cell
async function* plotSlice(width, height, description, values, imageFn, sliceIndex) {
// Create the canvas and 2D rendering context.
const ctx = DOM.context2d(width, height);

// Create and draw the slice image. The returned bitmap image can be used
// to draw the slice image scaled to the dimensions of the canvas. The data
// aspect ratio has to be taken into account to prevent stretching of the
// slice image.
const image = await imageFn(ctx, values, description.xExtent, description.yExtent, sliceIndex);
const dataAspect = description.yExtent / description.xExtent;
ctx.drawImage(image, 0, 0, width, width * dataAspect);
// Draw the info label (current image number and overall number of images).
const info = "Image " + (sliceIndex + 1) + "/" + (description.zExtent);
ctx.font = "16px sans-serif";
drawSliceInfo(ctx, info);

// Return the canvas element.
yield ctx.canvas;
}
Insert cell
Insert cell
plotSlice(500, 500, dataDescription, dataValues, createZSliceImage, zSliceIndex)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import {dataDescription as headDataDescription, dataValues as headDataValues} from "@mroehlig/3d-volume-rendering-with-webgl-three-js"
Insert cell
Insert cell
headDataDescription
Insert cell
headDataValues // Use the imported data to create and visualize a slice image...
Insert cell
Insert cell
Insert cell
viewof headZSliceIndex = Inputs.range([0, headDataDescription.zExtent - 1], {value: headDataDescription.zExtent / 2, step: 1, label: "head z-slice index"})
Insert cell
Insert cell
plotSlice(500, 500, headDataDescription, headDataValues, createZSliceImage, headZSliceIndex)
Insert cell
Insert cell
Insert cell
viewof invertColor = Inputs.toggle({label: "Invert color", value: true})
Insert cell
Insert cell
colorScale = {
// Get a ColorBrewer color palette with the maximum available number of colors using D3.
const palette = d3.schemeRdPu[d3.schemeRdPu.length - 1];
// Create a color scale from the palette using D3. The scale function can then be used
// to retrieve interpolated color values for input values between 0 and 1.
return d3.scaleLinear()
.domain(Array.from({length: palette.length}, (_, i) => i / (palette.length - 1)))
.range(palette);
}
Insert cell
Insert cell
drawGradient(colorScale)
Insert cell
Insert cell
interpolatedColor = d3.color(colorScale(0.5))
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
import {sliceViewer} from "@mroehlig/interactive-volume-visualization-of-2d-slices-in-x-y-and-z-plane"
Insert cell
Insert cell
sliceViewer
Insert cell
Insert cell
Insert cell
function getZSliceDataIndex(column, row, extents, sliceIndex) {
return (extents[0] * extents[1]) * sliceIndex + extents[0] * row + column;
}
Insert cell
Insert cell
function getXSliceDataIndex(column, row, extents, sliceIndex) {
return extents[0] * extents[1] * column + extents[0] * row + sliceIndex;
}
Insert cell
Insert cell
function getYSliceDataIndex(column, row, extents, sliceIndex) {
return extents[0] * extents[1] * row + extents[0] * sliceIndex + column;
}
Insert cell
Insert cell
// ✔️ SOLUTION for exercise 3: add extents and dataIndexFn as parameters ✔️
function createSliceImage(ctx, data, width, height, sliceIndex, extents, dataIndexFn) {
// Create image buffer with the same width and height as the dataset.
// The image data has 4 channels (red, green, blue, alpha).
const image = ctx.createImageData(width, height);

// Loop through all pixels in the image and set the color based on the data.
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
// Compute the data index in the 3D volume.
// ✔️ SOLUTION for exercise 3 ✔️
const dataIndex = dataIndexFn(x, y, extents, sliceIndex);

// Exercise 2 (part 1 and 2).
const value = invertColor ? 255 - data[dataIndex] : data[dataIndex];
const color = d3.color(colorScale(value / 255))

// Compute the index of the pixel in the image data, considering the 4 color channels.
const imageIndex = (width * y + x) * 4;

// Set the pixel color.
image.data[imageIndex] = color.r; // Red (Exercise 2)
image.data[imageIndex + 1] = color.g; // Green (Exercise 2)
image.data[imageIndex + 2] = color.b; // Blue (Exercise 2)
image.data[imageIndex + 3] = 255; // Alpha
}
}

// Asynchronously create a bitmap from the image data (returns a promise).
return createImageBitmap(image);
}
Insert cell
Insert cell
async function* plotSliceExt(size, description, values, widthIndex, heightIndex, planeIndex, sliceIndex, indexFn) {
// Create the canvas and 2D rendering context.
const extents = [description.xExtent, description.yExtent, description.zExtent]
// Take the volume aspect ratio and spacing of voxels into account.
const dataAspect = extents[heightIndex] / extents[widthIndex];
const width = (size / dataAspect) / Math.abs(description.scale[widthIndex]);
const ctx = DOM.context2d(width, size);
// Create and draw the slice image. The returned bitmap image can be used
// to draw the slice image scaled to the dimensions of the canvas. The data
// aspect ratio has to be taken into account to prevent stretching of the
// slice image.
const image = await createSliceImage(ctx, values, extents[widthIndex], extents[heightIndex], sliceIndex, extents, indexFn);
ctx.drawImage(image, 0, 0, width * Math.abs(description.scale[heightIndex]), size);
// Draw the info label (current image number and overall number of images).
const info = "Image " + (sliceIndex + 1) + "/" + (extents[planeIndex]);
ctx.font = "16px sans-serif";
drawSliceInfo(ctx, info);

// Return the canvas element.
yield ctx.canvas;
}
Insert cell
Insert cell
plotSliceExt(250, headDataDescription, headDataValues, 0, 1, 2, headZSliceIndex, getZSliceDataIndex)
Insert cell
Insert cell
Insert cell
plotSliceExt(250, headDataDescription, headDataValues, 2, 1, 0, xSliceIndex, getXSliceDataIndex)
Insert cell
Insert cell
Insert cell
plotSliceExt(250, headDataDescription, headDataValues, 0, 2, 1, ySliceIndex, getYSliceDataIndex)
Insert cell
Insert cell
Insert cell
function drawSliceInfo(ctx, info) {
// Draw the info string with white color in the upper left corner.
ctx.fillStyle = "white";
ctx.fillText(info, 5, 24);
}
Insert cell
Insert cell
function* duplicateCanvas(canvas) {
// Create a new canvas and 2D rendering context with the same dimensions as the input canvas.
const ctx = DOM.context2d(canvas.clientWidth, canvas.clientHeight);
// Draw the content of the input canvas onto the new canvas.
ctx.drawImage(canvas, 0, 0, canvas.clientWidth, canvas.clientHeight);
// Return the new canvas.
yield ctx.canvas;
}
Insert cell
Insert cell
function* drawGradient(colors, n = 256) {
// Create a new canvas with it's width set to the number of colors and it's height to one.
const ctx = DOM.context2d(n, 1, 1);
// Adjust the styling of the canvas to it is displayed larger.
ctx.canvas.style.width = "100%";
ctx.canvas.style.height = "40px";
ctx.canvas.style.imageRendering = "pixelated";
// Fill the canvas's pixels based on the given color function.
for (let i = 0; i < n; ++i) {
// For each color draw a colored rectangle in the size of a pixel.
ctx.fillStyle = colors(i / (n - 1));
ctx.fillRect(i, 0, 1, 1);
}

// Return the canvas.
yield ctx.canvas;
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more