UNPKG

molstar

Version:

A comprehensive macromolecular library.

295 lines 15.1 kB
"use strict"; /** * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Schäfer, Marco <marco.schaefer@uni-tuebingen.de> * @author Alexander Rose <alexander.rose@weirdbyte.de> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.shapeFromPly = exports.PlyShapeParams = void 0; var tslib_1 = require("tslib"); var mol_task_1 = require("../../mol-task"); var color_1 = require("../../mol-util/color"); var mesh_builder_1 = require("../../mol-geo/geometry/mesh/mesh-builder"); var mesh_1 = require("../../mol-geo/geometry/mesh/mesh"); var shape_1 = require("../../mol-model/shape"); var util_1 = require("../../mol-data/util"); var array_1 = require("../../mol-util/array"); var db_1 = require("../../mol-data/db"); var param_definition_1 = require("../../mol-util/param-definition"); var names_1 = require("../../mol-util/color/names"); var object_1 = require("../../mol-util/object"); var string_1 = require("../../mol-util/string"); // TODO support 'edge' element, see https://www.mathworks.com/help/vision/ug/the-ply-format.html // TODO support missing face element function createPlyShapeParams(plyFile) { var vertex = plyFile && plyFile.getElement('vertex'); var material = plyFile && plyFile.getElement('material'); var defaultValues = { group: '', vRed: '', vGreen: '', vBlue: '', mRed: '', mGreen: '', mBlue: '' }; var groupOptions = [['', '']]; var colorOptions = [['', '']]; if (vertex) { for (var i = 0, il = vertex.propertyNames.length; i < il; ++i) { var name_1 = vertex.propertyNames[i]; var type = vertex.propertyTypes[i]; if (type === 'uchar' || type === 'uint8' || type === 'ushort' || type === 'uint16' || type === 'uint' || type === 'uint32' || type === 'int') groupOptions.push([name_1, name_1]); if (type === 'uchar' || type === 'uint8') colorOptions.push([name_1, name_1]); } // TODO hardcoded as convenience for data provided by MegaMol if (vertex.propertyNames.includes('atomid')) defaultValues.group = 'atomid'; else if (vertex.propertyNames.includes('material_index')) defaultValues.group = 'material_index'; if (vertex.propertyNames.includes('red')) defaultValues.vRed = 'red'; if (vertex.propertyNames.includes('green')) defaultValues.vGreen = 'green'; if (vertex.propertyNames.includes('blue')) defaultValues.vBlue = 'blue'; } var materialOptions = [['', '']]; if (material) { for (var i = 0, il = material.propertyNames.length; i < il; ++i) { var name_2 = material.propertyNames[i]; var type = material.propertyTypes[i]; if (type === 'uchar' || type === 'uint8') materialOptions.push([name_2, name_2]); } if (material.propertyNames.includes('red')) defaultValues.mRed = 'red'; if (material.propertyNames.includes('green')) defaultValues.mGreen = 'green'; if (material.propertyNames.includes('blue')) defaultValues.mBlue = 'blue'; } var defaultColoring = defaultValues.vRed && defaultValues.vGreen && defaultValues.vBlue ? 'vertex' : defaultValues.mRed && defaultValues.mGreen && defaultValues.mBlue ? 'material' : 'uniform'; return (0, tslib_1.__assign)((0, tslib_1.__assign)({}, mesh_1.Mesh.Params), { coloring: param_definition_1.ParamDefinition.MappedStatic(defaultColoring, { vertex: param_definition_1.ParamDefinition.Group({ red: param_definition_1.ParamDefinition.Select(defaultValues.vRed, colorOptions, { label: 'Red Property' }), green: param_definition_1.ParamDefinition.Select(defaultValues.vGreen, colorOptions, { label: 'Green Property' }), blue: param_definition_1.ParamDefinition.Select(defaultValues.vBlue, colorOptions, { label: 'Blue Property' }), }, { isFlat: true }), material: param_definition_1.ParamDefinition.Group({ red: param_definition_1.ParamDefinition.Select(defaultValues.mRed, materialOptions, { label: 'Red Property' }), green: param_definition_1.ParamDefinition.Select(defaultValues.mGreen, materialOptions, { label: 'Green Property' }), blue: param_definition_1.ParamDefinition.Select(defaultValues.mBlue, materialOptions, { label: 'Blue Property' }), }, { isFlat: true }), uniform: param_definition_1.ParamDefinition.Group({ color: param_definition_1.ParamDefinition.Color(names_1.ColorNames.grey) }, { isFlat: true }) }), grouping: param_definition_1.ParamDefinition.MappedStatic(defaultValues.group ? 'vertex' : 'none', { vertex: param_definition_1.ParamDefinition.Group({ group: param_definition_1.ParamDefinition.Select(defaultValues.group, groupOptions, { label: 'Group Property' }), }, { isFlat: true }), none: param_definition_1.ParamDefinition.Group({}) }) }); } exports.PlyShapeParams = createPlyShapeParams(); function addVerticesRange(begI, endI, state, vertex, groupIds) { var vertices = state.vertices, normals = state.normals, groups = state.groups; var x = vertex.getProperty('x'); var y = vertex.getProperty('y'); var z = vertex.getProperty('z'); if (!x || !y || !z) throw new Error('missing coordinate properties'); var nx = vertex.getProperty('nx'); var ny = vertex.getProperty('ny'); var nz = vertex.getProperty('nz'); var hasNormals = !!nx && !!ny && !!nz; for (var i = begI; i < endI; ++i) { util_1.ChunkedArray.add3(vertices, x.value(i), y.value(i), z.value(i)); if (hasNormals) util_1.ChunkedArray.add3(normals, nx.value(i), ny.value(i), nz.value(i)); util_1.ChunkedArray.add(groups, groupIds[i]); } } function addFacesRange(begI, endI, state, face) { var indices = state.indices; for (var i = begI; i < endI; ++i) { var _a = face.value(i), entries = _a.entries, count = _a.count; if (count === 3) { // triangle util_1.ChunkedArray.add3(indices, entries[0], entries[1], entries[2]); } else if (count === 4) { // quadrilateral util_1.ChunkedArray.add3(indices, entries[2], entries[1], entries[0]); util_1.ChunkedArray.add3(indices, entries[2], entries[0], entries[3]); } } } function getMesh(ctx, vertex, face, groupIds, mesh) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var builderState, x, y, z, nx, ny, nz, hasNormals, updateChunk, i, il, i, il, m; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: builderState = mesh_builder_1.MeshBuilder.createState(vertex.rowCount, vertex.rowCount / 4, mesh); x = vertex.getProperty('x'); y = vertex.getProperty('y'); z = vertex.getProperty('z'); if (!x || !y || !z) throw new Error('missing coordinate properties'); nx = vertex.getProperty('nx'); ny = vertex.getProperty('ny'); nz = vertex.getProperty('nz'); hasNormals = !!nx && !!ny && !!nz; updateChunk = 100000; i = 0, il = vertex.rowCount; _a.label = 1; case 1: if (!(i < il)) return [3 /*break*/, 4]; addVerticesRange(i, Math.min(i + updateChunk, il), builderState, vertex, groupIds); if (!ctx.shouldUpdate) return [3 /*break*/, 3]; return [4 /*yield*/, ctx.update({ message: 'adding ply mesh vertices', current: i, max: il })]; case 2: _a.sent(); _a.label = 3; case 3: i += updateChunk; return [3 /*break*/, 1]; case 4: i = 0, il = face.rowCount; _a.label = 5; case 5: if (!(i < il)) return [3 /*break*/, 8]; addFacesRange(i, Math.min(i + updateChunk, il), builderState, face); if (!ctx.shouldUpdate) return [3 /*break*/, 7]; return [4 /*yield*/, ctx.update({ message: 'adding ply mesh faces', current: i, max: il })]; case 6: _a.sent(); _a.label = 7; case 7: i += updateChunk; return [3 /*break*/, 5]; case 8: m = mesh_builder_1.MeshBuilder.getMesh(builderState); if (!hasNormals) mesh_1.Mesh.computeNormals(m); return [2 /*return*/, m]; } }); }); } var int = db_1.Column.Schema.int; function getGrouping(vertex, props) { var grouping = props.grouping; var rowCount = vertex.rowCount; var column = grouping.name === 'vertex' ? vertex.getProperty(grouping.params.group) : undefined; var label = grouping.name === 'vertex' ? (0, string_1.stringToWords)(grouping.params.group) : 'Vertex'; var ids = column ? column.toArray({ array: Uint32Array }) : (0, array_1.fillSerial)(new Uint32Array(rowCount)); var maxId = column ? (0, array_1.arrayMax)(ids) : rowCount - 1; // assumes uint ids var map = new Uint32Array(maxId + 1); for (var i = 0, il = ids.length; i < il; ++i) map[ids[i]] = i; return { ids: ids, map: map, label: label }; } function getColoring(vertex, material, props) { var coloring = props.coloring; var rowCount = vertex.rowCount; var red, green, blue; if (coloring.name === 'vertex') { red = vertex.getProperty(coloring.params.red) || db_1.Column.ofConst(127, rowCount, int); green = vertex.getProperty(coloring.params.green) || db_1.Column.ofConst(127, rowCount, int); blue = vertex.getProperty(coloring.params.blue) || db_1.Column.ofConst(127, rowCount, int); } else if (coloring.name === 'material') { red = (material && material.getProperty(coloring.params.red)) || db_1.Column.ofConst(127, rowCount, int); green = (material && material.getProperty(coloring.params.green)) || db_1.Column.ofConst(127, rowCount, int); blue = (material && material.getProperty(coloring.params.blue)) || db_1.Column.ofConst(127, rowCount, int); } else { var _a = color_1.Color.toRgb(coloring.params.color), r = _a[0], g = _a[1], b = _a[2]; red = db_1.Column.ofConst(r, rowCount, int); green = db_1.Column.ofConst(g, rowCount, int); blue = db_1.Column.ofConst(b, rowCount, int); } return { kind: coloring.name, red: red, green: green, blue: blue }; } function createShape(plyFile, mesh, coloring, grouping) { var kind = coloring.kind, red = coloring.red, green = coloring.green, blue = coloring.blue; var ids = grouping.ids, map = grouping.map, label = grouping.label; return shape_1.Shape.create('ply-mesh', plyFile, mesh, function (groupId) { var idx = kind === 'material' ? groupId : map[groupId]; return color_1.Color.fromRgb(red.value(idx), green.value(idx), blue.value(idx)); }, function () { return 1; }, // size: constant function (groupId) { return label + " " + ids[groupId]; }); } function makeShapeGetter() { var _this = this; var _plyFile; var _props; var _shape; var _mesh; var _coloring; var _grouping; var getShape = function (ctx, plyFile, props, shape) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () { var vertex, face, material, newMesh, newColor; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: vertex = plyFile.getElement('vertex'); if (!vertex) throw new Error('missing vertex element'); face = plyFile.getElement('face'); if (!face) throw new Error('missing face element'); material = plyFile.getElement('material'); newMesh = false; newColor = false; if (!_plyFile || _plyFile !== plyFile) { newMesh = true; } if (!_props || !param_definition_1.ParamDefinition.isParamEqual(exports.PlyShapeParams.grouping, _props.grouping, props.grouping)) { newMesh = true; } if (!_props || !param_definition_1.ParamDefinition.isParamEqual(exports.PlyShapeParams.coloring, _props.coloring, props.coloring)) { newColor = true; } if (!newMesh) return [3 /*break*/, 2]; _coloring = getColoring(vertex, material, props); _grouping = getGrouping(vertex, props); return [4 /*yield*/, getMesh(ctx, vertex, face, _grouping.ids, shape && shape.geometry)]; case 1: _mesh = _a.sent(); _shape = createShape(plyFile, _mesh, _coloring, _grouping); return [3 /*break*/, 3]; case 2: if (newColor) { _coloring = getColoring(vertex, material, props); _shape = createShape(plyFile, _mesh, _coloring, _grouping); } _a.label = 3; case 3: _plyFile = plyFile; _props = (0, object_1.deepClone)(props); return [2 /*return*/, _shape]; } }); }); }; return getShape; } function shapeFromPly(source, params) { var _this = this; return mol_task_1.Task.create('Shape Provider', function (ctx) { return (0, tslib_1.__awaiter)(_this, void 0, void 0, function () { return (0, tslib_1.__generator)(this, function (_a) { return [2 /*return*/, { label: 'Mesh', data: source, params: createPlyShapeParams(source), getShape: makeShapeGetter(), geometryUtils: mesh_1.Mesh.Utils }]; }); }); }); } exports.shapeFromPly = shapeFromPly; //# sourceMappingURL=ply.js.map