molstar
Version:
A comprehensive macromolecular library.
250 lines (249 loc) • 11.3 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 DataFormat from '../../common/data-format';
import * as File from '../../common/file';
import * as Coords from '../algebra/coordinate';
import * as Box from '../algebra/box';
import { ConsoleLogger } from '../../../../mol-util/console-logger';
import { State } from '../state';
import { findUniqueBlocks } from './identify';
import { compose } from './compose';
import { encode } from './encode';
import { SpacegroupCell } from '../../../../mol-math/geometry';
import { Vec3 } from '../../../../mol-math/linear-algebra';
import { UUID } from '../../../../mol-util';
import { createTypedArray } from '../../../../mol-io/common/typed-array';
import { LimitsConfig } from '../../config';
import { fileHandleFromDescriptor } from '../../../common/file-handle';
export function execute(params, outputProvider) {
return __awaiter(this, void 0, void 0, function () {
var start, guid, sourceFile, _a, e_1;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
start = getTime();
State.pendingQueries++;
guid = UUID.create22();
params.detail = Math.min(Math.max(0, params.detail | 0), LimitsConfig.maxOutputSizeInVoxelCountByPrecisionLevel.length - 1);
ConsoleLogger.logId(guid, 'Info', "id=".concat(params.sourceId, ",encoding=").concat(params.asBinary ? 'binary' : 'text', ",detail=").concat(params.detail, ",").concat(queryBoxToString(params.box)));
_b.label = 1;
case 1:
_b.trys.push([1, 4, 5, 6]);
_a = fileHandleFromDescriptor;
return [4 /*yield*/, File.openRead(params.sourceFilename)];
case 2:
sourceFile = _a.apply(void 0, [_b.sent(), params.sourceFilename]);
return [4 /*yield*/, _execute(sourceFile, params, guid, outputProvider)];
case 3:
_b.sent();
return [2 /*return*/, true];
case 4:
e_1 = _b.sent();
ConsoleLogger.errorId(guid, e_1);
return [2 /*return*/, false];
case 5:
if (sourceFile)
sourceFile.close();
ConsoleLogger.logId(guid, 'Time', "".concat(Math.round(getTime() - start), "ms"));
State.pendingQueries--;
return [7 /*endfinally*/];
case 6: return [2 /*return*/];
}
});
});
}
function getTime() {
var t = process.hrtime();
return t[0] * 1000 + t[1] / 1000000;
}
function blockDomain(domain, blockSize) {
var delta = Coords.fractional(blockSize * domain.delta[0], blockSize * domain.delta[1], blockSize * domain.delta[2]);
return Coords.domain('Block', {
origin: domain.origin,
dimensions: domain.dimensions,
delta: delta,
sampleCount: Coords.sampleCounts(domain.dimensions, delta)
});
}
function createSampling(header, index, dataOffset) {
var sampling = header.sampling[index];
var dataDomain = Coords.domain('Data', {
origin: Coords.fractional(header.origin[0], header.origin[1], header.origin[2]),
dimensions: Coords.fractional(header.dimensions[0], header.dimensions[1], header.dimensions[2]),
delta: Coords.fractional(header.dimensions[0] / sampling.sampleCount[0], header.dimensions[1] / sampling.sampleCount[1], header.dimensions[2] / sampling.sampleCount[2]),
sampleCount: sampling.sampleCount
});
return {
index: index,
rate: sampling.rate,
byteOffset: sampling.byteOffset + dataOffset,
dataDomain: dataDomain,
blockDomain: blockDomain(dataDomain, header.blockSize)
};
}
function createDataContext(file) {
return __awaiter(this, void 0, void 0, function () {
var _a, header, dataOffset, origin, dimensions;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, DataFormat.readHeader(file)];
case 1:
_a = _b.sent(), header = _a.header, dataOffset = _a.dataOffset;
origin = Coords.fractional(header.origin[0], header.origin[1], header.origin[2]);
dimensions = Coords.fractional(header.dimensions[0], header.dimensions[1], header.dimensions[2]);
return [2 /*return*/, {
file: file,
header: header,
spacegroup: SpacegroupCell.create(header.spacegroup.number, Vec3.ofArray(header.spacegroup.size), Vec3.scale(Vec3.zero(), Vec3.ofArray(header.spacegroup.angles), Math.PI / 180)),
dataBox: { a: origin, b: Coords.add(origin, dimensions) },
sampling: header.sampling.map(function (s, i) { return createSampling(header, i, dataOffset); })
}];
}
});
});
}
function createQuerySampling(data, sampling, queryBox) {
var fractionalBox = Box.gridToFractional(Box.expandGridBox(Box.fractionalToGrid(queryBox, sampling.dataDomain), 1));
var blocks = findUniqueBlocks(data, sampling, fractionalBox);
var ret = {
sampling: sampling,
fractionalBox: fractionalBox,
gridDomain: Box.fractionalToDomain(fractionalBox, 'Query', sampling.dataDomain.delta),
blocks: blocks
};
return ret;
}
function pickSampling(data, queryBox, forcedLevel, precision) {
if (forcedLevel > 0) {
return createQuerySampling(data, data.sampling[Math.min(data.sampling.length, forcedLevel) - 1], queryBox);
}
var sizeLimit = LimitsConfig.maxOutputSizeInVoxelCountByPrecisionLevel[precision] || (2 * 1024 * 1024);
for (var _i = 0, _a = data.sampling; _i < _a.length; _i++) {
var s = _a[_i];
var gridBox = Box.fractionalToGrid(queryBox, s.dataDomain);
var approxSize = Box.volume(gridBox);
if (approxSize <= sizeLimit) {
var sampling = createQuerySampling(data, s, queryBox);
if (sampling.blocks.length <= LimitsConfig.maxRequestBlockCount) {
return sampling;
}
}
}
return createQuerySampling(data, data.sampling[data.sampling.length - 1], queryBox);
}
function emptyQueryContext(data, params, guid) {
return { kind: 'Empty', guid: guid, params: params, data: data };
}
function getQueryBox(data, queryBox) {
switch (queryBox.kind) {
case 'Cartesian': return Box.fractionalBoxReorderAxes(Box.cartesianToFractional(queryBox, data.spacegroup), data.header.axisOrder);
case 'Fractional': return Box.fractionalBoxReorderAxes(queryBox, data.header.axisOrder);
default: return data.dataBox;
}
}
function allocateValues(domain, numChannels, valueType) {
var values = [];
for (var i = 0; i < numChannels; i++) {
values[values.length] = createTypedArray(valueType, domain.sampleVolume);
}
return values;
}
function createQueryContext(data, params, guid) {
var inputQueryBox = getQueryBox(data, params.box);
var queryBox;
if (!data.header.spacegroup.isPeriodic) {
if (!Box.areIntersecting(data.dataBox, inputQueryBox)) {
return emptyQueryContext(data, params, guid);
}
queryBox = Box.intersect(data.dataBox, inputQueryBox);
}
else {
queryBox = inputQueryBox;
}
var dimensions = Box.dimensions(queryBox);
if (dimensions.some(function (d) { return isNaN(d); })) {
throw new Error('The query box is not defined.');
}
if (dimensions[0] * dimensions[1] * dimensions[2] > LimitsConfig.maxFractionalBoxVolume) {
throw new Error('The query box volume is too big.');
}
var samplingInfo = pickSampling(data, queryBox, params.forcedSamplingLevel !== void 0 ? params.forcedSamplingLevel : 0, params.detail);
if (samplingInfo.blocks.length === 0)
return emptyQueryContext(data, params, guid);
return {
kind: 'Data',
guid: guid,
data: data,
params: params,
samplingInfo: samplingInfo,
values: allocateValues(samplingInfo.gridDomain, data.header.channels.length, data.header.valueType)
};
}
function _execute(file, params, guid, outputProvider) {
return __awaiter(this, void 0, void 0, function () {
var output, data, query, e_2, query;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
output = void 0;
_a.label = 1;
case 1:
_a.trys.push([1, 5, 6, 7]);
return [4 /*yield*/, createDataContext(file)];
case 2:
data = _a.sent();
query = createQueryContext(data, params, guid);
if (!(query.kind === 'Data')) return [3 /*break*/, 4];
// Step 3b: Compose the result data
return [4 /*yield*/, compose(query)];
case 3:
// Step 3b: Compose the result data
_a.sent();
_a.label = 4;
case 4:
// Step 4: Encode the result
output = outputProvider();
encode(query, output);
output.end();
return [3 /*break*/, 7];
case 5:
e_2 = _a.sent();
query = { kind: 'Error', guid: guid, params: params, message: "".concat(e_2) };
try {
if (!output)
output = outputProvider();
encode(query, output);
}
catch (f) {
throw f;
}
throw e_2;
case 6:
if (output)
output.end();
return [7 /*endfinally*/];
case 7: return [2 /*return*/];
}
});
});
}
function roundCoord(c) {
return Math.round(100000 * c) / 100000;
}
function queryBoxToString(queryBox) {
switch (queryBox.kind) {
case 'Cartesian':
case 'Fractional':
var a = queryBox.a, b = queryBox.b;
var r = roundCoord;
return "box-type=".concat(queryBox.kind, ",box-a=(").concat(r(a[0]), ",").concat(r(a[1]), ",").concat(r(a[2]), "),box-b=(").concat(r(b[0]), ",").concat(r(b[1]), ",").concat(r(b[2]), ")");
default:
return "box-type=".concat(queryBox.kind);
}
}