molstar
Version:
A comprehensive macromolecular library.
267 lines • 11.6 kB
JavaScript
/**
* 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 { __awaiter, __generator } from "tslib";
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 function createContext(filename, channels, blockSize, isPeriodic) {
return __awaiter(this, void 0, void 0, function () {
var _a, extent, valueType, grid, origin, samplingCounts, cubeBuffer, litteEndianCubeBuffer, ctx, _b, byteOffset, _i, _c, s;
var _d;
return __generator(this, function (_e) {
switch (_e.label) {
case 0:
_a = channels[0].data.header, extent = _a.extent, valueType = _a.valueType, grid = _a.grid, origin = _a.origin;
samplingCounts = getSamplingCounts(extent, blockSize);
cubeBuffer = Buffer.from(new ArrayBuffer(channels.length * blockSize * blockSize * blockSize * getElementByteSize(valueType)));
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(function (v, i) { return v !== extent[i]; }) || origin.some(function (v) { return v !== 0; })) {
isPeriodic = false;
}
_d = {};
_b = fileHandleFromDescriptor;
return [4 /*yield*/, File.createFile(filename)];
case 1:
ctx = (_d.file = _b.apply(void 0, [_e.sent(), filename]),
_d.isPeriodic = isPeriodic,
_d.channels = channels,
_d.valueType = valueType,
_d.blockSize = blockSize,
_d.cubeBuffer = cubeBuffer,
_d.litteEndianCubeBuffer = litteEndianCubeBuffer,
_d.kernel = { size: 5, coefficients: [1, 4, 6, 4, 1], coefficientSum: 16 },
_d.sampling = samplingCounts.map(function (__, i) { return createSampling(i, valueType, channels.length, samplingCounts, blockSize); }),
_d.dataByteOffset = 0,
_d.totalByteSize = 0,
_d.progress = { current: 0, max: 0 },
_d);
byteOffset = 0;
for (_i = 0, _c = ctx.sampling; _i < _c.length; _i++) {
s = _c[_i];
// 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 [2 /*return*/, ctx];
}
});
});
}
export function processData(ctx) {
return __awaiter(this, void 0, void 0, function () {
var channel, _i, _a, src;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
channel = ctx.channels[0];
_b.label = 1;
case 1:
if (!!channel.data.slices.isFinished) return [3 /*break*/, 7];
_i = 0, _a = ctx.channels;
_b.label = 2;
case 2:
if (!(_i < _a.length)) return [3 /*break*/, 5];
src = _a[_i];
return [4 /*yield*/, src.provider.readSlices(src.data)];
case 3:
_b.sent();
_b.label = 4;
case 4:
_i++;
return [3 /*break*/, 2];
case 5: return [4 /*yield*/, processSlices(ctx)];
case 6:
_b.sent();
return [3 /*break*/, 1];
case 7: return [2 /*return*/];
}
});
});
}
/** Determine the suitable sampling rates for the input data */
function getSamplingCounts(baseSampleCount, blockSize) {
var ret = [baseSampleCount];
var prev = baseSampleCount;
var hasSingleBoxSampling = false;
while (true) {
var next = [0, 0, 0];
var max = 0;
for (var i = 0; i < 3; i++) {
var 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) {
var values = [];
for (var i = 0; i < numChannels; i++)
values[i] = createTypedArray(valueType, sampleCount[0] * sampleCount[1] * blockSize);
return {
values: values,
buffers: values.map(function (xs) { return Buffer.from(xs.buffer); }),
slicesWritten: 0
};
}
function createDownsamplingBuffer(valueType, sourceSampleCount, targetSampleCount, numChannels) {
var ret = [];
for (var 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) {
var sampleCount = sampleCounts[index];
var valuesInfo = [];
for (var 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: sampleCount,
blocks: createBlockBuffer(sampleCount, blockSize, valueType, numChannels),
valuesInfo: 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) {
var channels = ctx.channels;
var _a = ctx.sampling[0], blocks = _a.blocks, sampleCount = _a.sampleCount;
var size = sampleCount[0] * sampleCount[1];
var srcOffset = sliceIndex * size;
var targetOffset = blocks.slicesWritten * size;
for (var channelIndex = 0; channelIndex < channels.length; channelIndex++) {
var src = channels[channelIndex].data.slices.values;
var target = blocks.values[channelIndex];
for (var i = 0; i < size; i++) {
var v = src[srcOffset + i];
target[targetOffset + i] = v;
}
}
blocks.slicesWritten++;
}
function updateValuesInfo(sampling) {
var blocks = sampling.blocks, sampleCount = sampling.sampleCount;
var size = blocks.slicesWritten * sampleCount[0] * sampleCount[1];
for (var channelIndex = 0; channelIndex < blocks.values.length; channelIndex++) {
var values = blocks.values[channelIndex];
var valuesInfo = sampling.valuesInfo[channelIndex];
var sum = valuesInfo.sum, sqSum = valuesInfo.sqSum, max = valuesInfo.max, min = valuesInfo.min;
for (var i = 0; i < size; i++) {
var 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;
}
function writeBlocks(ctx, isDataFinished) {
return __awaiter(this, void 0, void 0, function () {
var _i, _a, s;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_i = 0, _a = ctx.sampling;
_b.label = 1;
case 1:
if (!(_i < _a.length)) return [3 /*break*/, 4];
s = _a[_i];
if (!shouldSamplingBeWritten(s, ctx.blockSize, isDataFinished)) return [3 /*break*/, 3];
updateValuesInfo(s);
return [4 /*yield*/, Writer.writeBlockLayer(ctx, s)];
case 2:
_b.sent();
_b.label = 3;
case 3:
_i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/];
}
});
});
}
function processSlices(ctx) {
return __awaiter(this, void 0, void 0, function () {
var channel, sliceCount, i, isDataFinished;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
channel = ctx.channels[0];
sliceCount = channel.data.slices.sliceCount;
i = 0;
_a.label = 1;
case 1:
if (!(i < sliceCount)) return [3 /*break*/, 5];
copyLayer(ctx, i);
Downsampling.downsampleLayer(ctx);
return [4 /*yield*/, writeBlocks(ctx, false)];
case 2:
_a.sent();
isDataFinished = i === sliceCount - 1 && channel.data.slices.isFinished;
if (!isDataFinished) return [3 /*break*/, 4];
Downsampling.finalize(ctx);
return [4 /*yield*/, writeBlocks(ctx, true)];
case 3:
_a.sent();
_a.label = 4;
case 4:
i++;
return [3 /*break*/, 1];
case 5: return [2 /*return*/];
}
});
});
}
//# sourceMappingURL=sampling.js.map