UNPKG

molstar

Version:

A comprehensive macromolecular library.

196 lines (195 loc) 7.57 kB
/** * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * * Taken/adapted from DensityServer (https://github.com/dsehnal/DensityServer) * * @author David Sehnal <david.sehnal@gmail.com> */ import * as Data from './data-model'; import * as File from '../common/file'; import * as Downsampling from './downsampling'; import * as Writer from './writer'; import * as DataFormat from '../common/data-format'; import { getElementByteSize, createTypedArray } from '../../../mol-io/common/typed-array'; import { SimpleBuffer } from '../../../mol-io/common/simple-buffer'; import { fileHandleFromDescriptor } from '../../common/file-handle'; export async function createContext(filename, channels, blockSize, isPeriodic) { const { extent, valueType, grid, origin } = channels[0].data.header; const samplingCounts = getSamplingCounts(extent, blockSize); const cubeBuffer = Buffer.from(new ArrayBuffer(channels.length * blockSize * blockSize * blockSize * getElementByteSize(valueType))); const litteEndianCubeBuffer = SimpleBuffer.IsNativeEndianLittle ? cubeBuffer : Buffer.from(new ArrayBuffer(channels.length * blockSize * blockSize * blockSize * getElementByteSize(valueType))); // The data can be periodic iff the extent is the same as the grid and origin is 0. if (grid.some((v, i) => v !== extent[i]) || origin.some(v => v !== 0)) { isPeriodic = false; } const ctx = { file: fileHandleFromDescriptor(await File.createFile(filename), filename), isPeriodic, channels, valueType, blockSize, cubeBuffer, litteEndianCubeBuffer, kernel: { size: 5, coefficients: [1, 4, 6, 4, 1], coefficientSum: 16 }, sampling: samplingCounts.map((__, i) => createSampling(i, valueType, channels.length, samplingCounts, blockSize)), dataByteOffset: 0, totalByteSize: 0, progress: { current: 0, max: 0 } }; let byteOffset = 0; for (const s of ctx.sampling) { // Max progress = total number of blocks that need to be written. ctx.progress.max += Data.samplingBlockCount(s, blockSize); s.byteOffset = byteOffset; byteOffset += s.byteSize; } ctx.dataByteOffset = 4 + DataFormat.encodeHeader(Data.createHeader(ctx)).byteLength; ctx.totalByteSize = ctx.dataByteOffset + byteOffset; return ctx; } export async function processData(ctx) { const channel = ctx.channels[0]; while (!channel.data.slices.isFinished) { for (const src of ctx.channels) { await src.provider.readSlices(src.data); } await processSlices(ctx); } } /** Determine the suitable sampling rates for the input data */ function getSamplingCounts(baseSampleCount, blockSize) { const ret = [baseSampleCount]; let prev = baseSampleCount; let hasSingleBoxSampling = false; while (true) { const next = [0, 0, 0]; let max = 0; for (let i = 0; i < 3; i++) { const s = Math.floor((prev[i] + 1) / 2); if (s < 2) return ret; if (s > max) max = s; next[i] = s; } // no point in downsampling below the block size. if (max < blockSize) { if (hasSingleBoxSampling) return ret; hasSingleBoxSampling = true; } ret.push(next); prev = next; } } function createBlockBuffer(sampleCount, blockSize, valueType, numChannels) { const values = []; for (let i = 0; i < numChannels; i++) values[i] = createTypedArray(valueType, sampleCount[0] * sampleCount[1] * blockSize); return { values, buffers: values.map(xs => Buffer.from(xs.buffer)), slicesWritten: 0 }; } function createDownsamplingBuffer(valueType, sourceSampleCount, targetSampleCount, numChannels) { const ret = []; for (let i = 0; i < numChannels; i++) { ret[ret.length] = { downsampleH: createTypedArray(valueType, sourceSampleCount[1] * targetSampleCount[0]), downsampleHK: createTypedArray(valueType, 5 * targetSampleCount[0] * targetSampleCount[1]), slicesWritten: 0, startSliceIndex: 0 }; } return ret; } function createSampling(index, valueType, numChannels, sampleCounts, blockSize) { const sampleCount = sampleCounts[index]; const valuesInfo = []; for (let i = 0; i < numChannels; i++) { valuesInfo[valuesInfo.length] = { sum: 0.0, sqSum: 0.0, max: Number.NEGATIVE_INFINITY, min: Number.POSITIVE_INFINITY }; } return { rate: 1 << index, sampleCount, blocks: createBlockBuffer(sampleCount, blockSize, valueType, numChannels), valuesInfo, downsampling: index < sampleCounts.length - 1 ? createDownsamplingBuffer(valueType, sampleCount, sampleCounts[index + 1], numChannels) : void 0, byteOffset: 0, byteSize: numChannels * sampleCount[0] * sampleCount[1] * sampleCount[2] * getElementByteSize(valueType), writeByteOffset: 0 }; } function copyLayer(ctx, sliceIndex) { const { channels } = ctx; const { blocks, sampleCount } = ctx.sampling[0]; const size = sampleCount[0] * sampleCount[1]; const srcOffset = sliceIndex * size; const targetOffset = blocks.slicesWritten * size; for (let channelIndex = 0; channelIndex < channels.length; channelIndex++) { const src = channels[channelIndex].data.slices.values; const target = blocks.values[channelIndex]; for (let i = 0; i < size; i++) { const v = src[srcOffset + i]; target[targetOffset + i] = v; } } blocks.slicesWritten++; } function updateValuesInfo(sampling) { const { blocks, sampleCount } = sampling; const size = blocks.slicesWritten * sampleCount[0] * sampleCount[1]; for (let channelIndex = 0; channelIndex < blocks.values.length; channelIndex++) { const values = blocks.values[channelIndex]; const valuesInfo = sampling.valuesInfo[channelIndex]; let { sum, sqSum, max, min } = valuesInfo; for (let i = 0; i < size; i++) { const v = values[i]; sum += v; sqSum += v * v; if (v > max) max = v; else if (v < min) min = v; } valuesInfo.sum = sum; valuesInfo.sqSum = sqSum; valuesInfo.max = max; valuesInfo.min = min; } } function shouldSamplingBeWritten(sampling, blockSize, isDataFinished) { if (isDataFinished) return sampling.blocks.slicesWritten > 0; return sampling.blocks.slicesWritten >= blockSize; } async function writeBlocks(ctx, isDataFinished) { for (const s of ctx.sampling) { if (shouldSamplingBeWritten(s, ctx.blockSize, isDataFinished)) { updateValuesInfo(s); await Writer.writeBlockLayer(ctx, s); } } } async function processSlices(ctx) { const channel = ctx.channels[0]; const sliceCount = channel.data.slices.sliceCount; for (let i = 0; i < sliceCount; i++) { copyLayer(ctx, i); Downsampling.downsampleLayer(ctx); await writeBlocks(ctx, false); const isDataFinished = i === sliceCount - 1 && channel.data.slices.isFinished; if (isDataFinished) { Downsampling.finalize(ctx); await writeBlocks(ctx, true); } } }