molstar
Version:
A comprehensive macromolecular library.
253 lines • 9.62 kB
JavaScript
/**
* Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.parsePly = void 0;
var tslib_1 = require("tslib");
var result_1 = require("../result");
var mol_task_1 = require("../../../mol-task");
var schema_1 = require("./schema");
var tokenizer_1 = require("../common/text/tokenizer");
var db_1 = require("../../../mol-data/db");
var token_1 = require("../common/text/column/token");
function State(data, runtimeCtx) {
var tokenizer = (0, tokenizer_1.Tokenizer)(data);
return {
data: data,
tokenizer: tokenizer,
runtimeCtx: runtimeCtx,
comments: [],
elementSpecs: [],
elements: []
};
}
function markHeader(tokenizer) {
var endHeaderIndex = tokenizer.data.indexOf('end_header', tokenizer.position);
if (endHeaderIndex === -1)
throw new Error("no 'end_header' record found");
// TODO set `tokenizer.lineNumber` correctly
tokenizer.tokenStart = tokenizer.position;
tokenizer.tokenEnd = endHeaderIndex;
tokenizer.position = endHeaderIndex;
tokenizer_1.Tokenizer.eatLine(tokenizer);
}
function parseHeader(state) {
var tokenizer = state.tokenizer, comments = state.comments, elementSpecs = state.elementSpecs;
markHeader(tokenizer);
var headerLines = tokenizer_1.Tokenizer.getTokenString(tokenizer).split(/\r?\n/);
if (headerLines[0] !== 'ply')
throw new Error("data not starting with 'ply'");
if (headerLines[1] !== 'format ascii 1.0')
throw new Error("format not 'ascii 1.0'");
var currentName;
var currentCount;
var currentProperties;
function addCurrentElementSchema() {
if (currentName !== undefined && currentCount !== undefined && currentProperties !== undefined) {
var isList = false;
for (var i = 0, il = currentProperties.length; i < il; ++i) {
var p = currentProperties[i];
if (p.kind === 'list') {
isList = true;
break;
}
}
if (isList && currentProperties.length !== 1) {
// TODO handle lists with appended properties
// currently only the list part will be accessible
}
if (isList) {
elementSpecs.push({
kind: 'list',
name: currentName,
count: currentCount,
property: currentProperties[0]
});
}
else {
elementSpecs.push({
kind: 'table',
name: currentName,
count: currentCount,
properties: currentProperties
});
}
}
}
for (var i = 2, il = headerLines.length; i < il; ++i) {
var l = headerLines[i];
var ls = l.split(' ');
if (l.startsWith('comment')) {
comments.push(l.substr(8));
}
else if (l.startsWith('element')) {
addCurrentElementSchema();
currentProperties = [];
currentName = ls[1];
currentCount = parseInt(ls[2]);
}
else if (l.startsWith('property')) {
if (currentProperties === undefined)
throw new Error("properties outside of element");
if (ls[1] === 'list') {
currentProperties.push({
kind: 'list',
countType: (0, schema_1.PlyType)(ls[2]),
dataType: (0, schema_1.PlyType)(ls[3]),
name: ls[4]
});
}
else {
currentProperties.push({
kind: 'column',
type: (0, schema_1.PlyType)(ls[1]),
name: ls[2]
});
}
}
else if (l.startsWith('end_header')) {
addCurrentElementSchema();
}
else {
console.warn('unknown header line');
}
}
}
function parseElements(state) {
var elementSpecs = state.elementSpecs;
for (var i = 0, il = elementSpecs.length; i < il; ++i) {
var spec = elementSpecs[i];
if (spec.kind === 'table')
parseTableElement(state, spec);
else if (spec.kind === 'list')
parseListElement(state, spec);
}
}
function getColumnSchema(type) {
switch (type) {
case 'char':
case 'uchar':
case 'int8':
case 'uint8':
case 'short':
case 'ushort':
case 'int16':
case 'uint16':
case 'int':
case 'uint':
case 'int32':
case 'uint32':
return db_1.Column.Schema.int;
case 'float':
case 'double':
case 'float32':
case 'float64':
return db_1.Column.Schema.float;
}
}
function parseTableElement(state, spec) {
var elements = state.elements, tokenizer = state.tokenizer;
var count = spec.count, properties = spec.properties;
var propertyCount = properties.length;
var propertyNames = [];
var propertyTypes = [];
var propertyTokens = [];
var propertyColumns = new Map();
for (var i = 0, il = propertyCount; i < il; ++i) {
var tokens = tokenizer_1.TokenBuilder.create(tokenizer.data, count * 2);
propertyTokens.push(tokens);
}
for (var i = 0, il = count; i < il; ++i) {
for (var j = 0, jl = propertyCount; j < jl; ++j) {
tokenizer_1.Tokenizer.skipWhitespace(tokenizer);
tokenizer_1.Tokenizer.markStart(tokenizer);
tokenizer_1.Tokenizer.eatValue(tokenizer);
tokenizer_1.TokenBuilder.addUnchecked(propertyTokens[j], tokenizer.tokenStart, tokenizer.tokenEnd);
}
}
for (var i = 0, il = propertyCount; i < il; ++i) {
var _a = properties[i], type = _a.type, name_1 = _a.name;
var column = (0, token_1.TokenColumn)(propertyTokens[i], getColumnSchema(type));
propertyNames.push(name_1);
propertyTypes.push(type);
propertyColumns.set(name_1, column);
}
elements.push({
kind: 'table',
rowCount: count,
propertyNames: propertyNames,
propertyTypes: propertyTypes,
getProperty: function (name) { return propertyColumns.get(name); }
});
}
function parseListElement(state, spec) {
var elements = state.elements, tokenizer = state.tokenizer;
var count = spec.count, property = spec.property;
// initial tokens size assumes triangle index data
var tokens = tokenizer_1.TokenBuilder.create(tokenizer.data, count * 2 * 3);
var offsets = new Uint32Array(count + 1);
var entryCount = 0;
for (var i = 0, il = count; i < il; ++i) {
tokenizer_1.Tokenizer.skipWhitespace(tokenizer);
tokenizer_1.Tokenizer.markStart(tokenizer);
while (tokenizer_1.Tokenizer.skipWhitespace(tokenizer) !== 10) {
++entryCount;
tokenizer_1.Tokenizer.markStart(tokenizer);
tokenizer_1.Tokenizer.eatValue(tokenizer);
tokenizer_1.TokenBuilder.addToken(tokens, tokenizer);
}
offsets[i + 1] = entryCount;
}
/** holds row value entries transiently */
var listValue = {
entries: [],
count: 0
};
var column = (0, token_1.TokenColumn)(tokens, getColumnSchema(property.dataType));
elements.push({
kind: 'list',
rowCount: count,
name: property.name,
type: property.dataType,
value: function (row) {
var offset = offsets[row] + 1;
var count = column.value(offset - 1);
for (var i = offset, il = offset + count; i < il; ++i) {
listValue.entries[i - offset] = column.value(i);
}
listValue.count = count;
return listValue;
}
});
}
function parseInternal(data, ctx) {
return (0, tslib_1.__awaiter)(this, void 0, void 0, function () {
var state, elements, elementSpecs, comments, elementNames, result;
return (0, tslib_1.__generator)(this, function (_a) {
state = State(data, ctx);
ctx.update({ message: 'Parsing...', current: 0, max: data.length });
parseHeader(state);
parseElements(state);
elements = state.elements, elementSpecs = state.elementSpecs, comments = state.comments;
elementNames = elementSpecs.map(function (s) { return s.name; });
result = (0, schema_1.PlyFile)(elements, elementNames, comments);
return [2 /*return*/, result_1.ReaderResult.success(result)];
});
});
}
function parsePly(data) {
var _this = this;
return mol_task_1.Task.create('Parse PLY', function (ctx) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () {
return (0, tslib_1.__generator)(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, parseInternal(data, ctx)];
case 1: return [2 /*return*/, _a.sent()];
}
});
}); });
}
exports.parsePly = parsePly;
//# sourceMappingURL=parser.js.map
;