UNPKG

molstar

Version:

A comprehensive macromolecular library.

385 lines 20.7 kB
"use strict"; /** * Copyright (c) 2021 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Sukolsak Sakshuwong <sukolsak@stanford.edu> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.MeshExporter = void 0; var tslib_1 = require("tslib"); var util_1 = require("../../mol-data/util"); var color_smoothing_1 = require("../../mol-geo/geometry/mesh/color-smoothing"); var mesh_1 = require("../../mol-geo/geometry/mesh/mesh"); var mesh_builder_1 = require("../../mol-geo/geometry/mesh/mesh-builder"); var sphere_1 = require("../../mol-geo/geometry/mesh/builder/sphere"); var cylinder_1 = require("../../mol-geo/geometry/mesh/builder/cylinder"); var size_data_1 = require("../../mol-geo/geometry/size-data"); var linear_algebra_1 = require("../../mol-math/linear-algebra"); var color_1 = require("../../mol-util/color/color"); var float_packing_1 = require("../../mol-util/float-packing"); var GeoExportName = 'geo-export'; // avoiding namespace lookup improved performance in Chrome (Aug 2020) var v3fromArray = linear_algebra_1.Vec3.fromArray; var MeshExporter = /** @class */ (function () { function MeshExporter() { } MeshExporter.getSizeFromTexture = function (tSize, i) { var r = tSize.array[i * 3]; var g = tSize.array[i * 3 + 1]; var b = tSize.array[i * 3 + 2]; return (0, float_packing_1.decodeFloatRGB)(r, g, b) / size_data_1.sizeDataFactor; }; MeshExporter.getSize = function (values, instanceIndex, group) { var tSize = values.tSize.ref.value; var size = 0; switch (values.dSizeType.ref.value) { case 'uniform': size = values.uSize.ref.value; break; case 'instance': size = MeshExporter.getSizeFromTexture(tSize, instanceIndex); break; case 'group': size = MeshExporter.getSizeFromTexture(tSize, group); break; case 'groupInstance': var groupCount = values.uGroupCount.ref.value; size = MeshExporter.getSizeFromTexture(tSize, instanceIndex * groupCount + group); break; } return size * values.uSizeFactor.ref.value; }; MeshExporter.getGroup = function (groups, i) { var i4 = i * 4; var r = groups[i4]; var g = groups[i4 + 1]; var b = groups[i4 + 2]; if (groups instanceof Float32Array) { return (0, float_packing_1.decodeFloatRGB)(r * 255 + 0.5, g * 255 + 0.5, b * 255 + 0.5); } return (0, float_packing_1.decodeFloatRGB)(r, g, b); }; MeshExporter.getInterpolatedColors = function (vertices, vertexCount, values, stride, colorType, webgl) { var colorGridTransform = values.uColorGridTransform.ref.value; var colorGridDim = values.uColorGridDim.ref.value; var colorTexDim = values.uColorTexDim.ref.value; var aTransform = values.aTransform.ref.value; var instanceCount = values.uInstanceCount.ref.value; if (!webgl.namedFramebuffers[GeoExportName]) { webgl.namedFramebuffers[GeoExportName] = webgl.resources.framebuffer(); } var framebuffer = webgl.namedFramebuffers[GeoExportName]; var width = colorTexDim[0], height = colorTexDim[1]; var colorGrid = new Uint8Array(width * height * 4); framebuffer.bind(); values.tColorGrid.ref.value.attachFramebuffer(framebuffer, 0); webgl.readPixels(0, 0, width, height, colorGrid); var interpolated = (0, color_smoothing_1.getTrilinearlyInterpolated)({ vertexCount: vertexCount, instanceCount: instanceCount, transformBuffer: aTransform, positionBuffer: vertices, colorType: colorType, grid: colorGrid, gridDim: colorGridDim, gridTexDim: colorTexDim, gridTransform: colorGridTransform, vertexStride: stride, colorStride: 4 }); return interpolated.array; }; MeshExporter.quantizeColors = function (colorArray, vertexCount) { if (vertexCount <= 1024) return; var rgb = (0, linear_algebra_1.Vec3)(); var min = (0, linear_algebra_1.Vec3)(); var max = (0, linear_algebra_1.Vec3)(); var sum = (0, linear_algebra_1.Vec3)(); var colorMap = new Map(); var colorComparers = [ function (colors, i, j) { return (color_1.Color.toVec3(rgb, colors[i])[0] - color_1.Color.toVec3(rgb, colors[j])[0]); }, function (colors, i, j) { return (color_1.Color.toVec3(rgb, colors[i])[1] - color_1.Color.toVec3(rgb, colors[j])[1]); }, function (colors, i, j) { return (color_1.Color.toVec3(rgb, colors[i])[2] - color_1.Color.toVec3(rgb, colors[j])[2]); }, ]; var medianCut = function (colors, l, r, depth) { if (l > r) return; if (l === r || depth >= 10) { // Find the average color. linear_algebra_1.Vec3.set(sum, 0, 0, 0); for (var i = l; i <= r; ++i) { color_1.Color.toVec3(rgb, colors[i]); linear_algebra_1.Vec3.add(sum, sum, rgb); } linear_algebra_1.Vec3.round(rgb, linear_algebra_1.Vec3.scale(rgb, sum, 1 / (r - l + 1))); var averageColor = color_1.Color.fromArray(rgb, 0); for (var i = l; i <= r; ++i) colorMap.set(colors[i], averageColor); return; } // Find the color channel with the greatest range. linear_algebra_1.Vec3.set(min, 255, 255, 255); linear_algebra_1.Vec3.set(max, 0, 0, 0); for (var i = l; i <= r; ++i) { color_1.Color.toVec3(rgb, colors[i]); for (var j = 0; j < 3; ++j) { linear_algebra_1.Vec3.min(min, min, rgb); linear_algebra_1.Vec3.max(max, max, rgb); } } var k = 0; if (max[1] - min[1] > max[k] - min[k]) k = 1; if (max[2] - min[2] > max[k] - min[k]) k = 2; (0, util_1.sort)(colors, l, r + 1, colorComparers[k], util_1.arraySwap); var m = (l + r) >> 1; medianCut(colors, l, m, depth + 1); medianCut(colors, m + 1, r, depth + 1); }; // Create an array of unique colors and use the median cut algorithm. var colorSet = new Set(); for (var i = 0; i < vertexCount; ++i) { colorSet.add(color_1.Color.fromArray(colorArray, i * 3)); } var colors = Array.from(colorSet); medianCut(colors, 0, colors.length - 1, 0); // Map actual colors to quantized colors. for (var i = 0; i < vertexCount; ++i) { var color = colorMap.get(color_1.Color.fromArray(colorArray, i * 3)); color_1.Color.toArray(color, colorArray, i * 3); } }; MeshExporter.getInstance = function (input, instanceIndex) { var mesh = input.mesh, meshes = input.meshes; if (mesh !== undefined) { return mesh; } else { var mesh_2 = meshes[instanceIndex]; return { vertices: mesh_2.vertexBuffer.ref.value, normals: mesh_2.normalBuffer.ref.value, indices: mesh_2.indexBuffer.ref.value, groups: mesh_2.groupBuffer.ref.value, vertexCount: mesh_2.vertexCount, drawCount: mesh_2.triangleCount * 3 }; } }; MeshExporter.getColor = function (values, groups, vertexCount, instanceIndex, isGeoTexture, interpolatedColors, vertexIndex) { var groupCount = values.uGroupCount.ref.value; var colorType = values.dColorType.ref.value; var uColor = values.uColor.ref.value; var tColor = values.tColor.ref.value.array; var dOverpaint = values.dOverpaint.ref.value; var tOverpaint = values.tOverpaint.ref.value.array; var color; switch (colorType) { case 'uniform': color = color_1.Color.fromNormalizedArray(uColor, 0); break; case 'instance': color = color_1.Color.fromArray(tColor, instanceIndex * 3); break; case 'group': { var group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex]; color = color_1.Color.fromArray(tColor, group * 3); break; } case 'groupInstance': { var group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex]; color = color_1.Color.fromArray(tColor, (instanceIndex * groupCount + group) * 3); break; } case 'vertex': color = color_1.Color.fromArray(tColor, vertexIndex * 3); break; case 'vertexInstance': color = color_1.Color.fromArray(tColor, (instanceIndex * vertexCount + vertexIndex) * 3); break; case 'volume': color = color_1.Color.fromArray(interpolatedColors, vertexIndex * 3); break; case 'volumeInstance': color = color_1.Color.fromArray(interpolatedColors, (instanceIndex * vertexCount + vertexIndex) * 3); break; default: throw new Error('Unsupported color type.'); } if (dOverpaint) { var group = isGeoTexture ? MeshExporter.getGroup(groups, vertexIndex) : groups[vertexIndex]; var overpaintColor = color_1.Color.fromArray(tOverpaint, (instanceIndex * groupCount + group) * 4); var overpaintAlpha = tOverpaint[(instanceIndex * groupCount + group) * 4 + 3] / 255; color = color_1.Color.interpolate(color, overpaintColor, overpaintAlpha); } return color; }; MeshExporter.prototype.addMesh = function (values, webgl, ctx) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var aPosition, aNormal, aGroup, originalData, indices, vertexCount, drawCount; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: aPosition = values.aPosition.ref.value; aNormal = values.aNormal.ref.value; aGroup = values.aGroup.ref.value; originalData = mesh_1.Mesh.getOriginalData(values); if (originalData) { indices = originalData.indexBuffer; vertexCount = originalData.vertexCount; drawCount = originalData.triangleCount * 3; } else { indices = values.elements.ref.value; vertexCount = values.uVertexCount.ref.value; drawCount = values.drawCount.ref.value; } return [4 /*yield*/, this.addMeshWithColors({ mesh: { vertices: aPosition, normals: aNormal, indices: indices, groups: aGroup, vertexCount: vertexCount, drawCount: drawCount }, meshes: undefined, values: values, isGeoTexture: false, webgl: webgl, ctx: ctx })]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; MeshExporter.prototype.addLines = function (values, webgl, ctx) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { return (0, tslib_1.__generator)(this, function (_a) { return [2 /*return*/]; }); }); }; MeshExporter.prototype.addPoints = function (values, webgl, ctx) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { return (0, tslib_1.__generator)(this, function (_a) { return [2 /*return*/]; }); }); }; MeshExporter.prototype.addSpheres = function (values, webgl, ctx) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var center, aPosition, aGroup, instanceCount, vertexCount, meshes, sphereCount, detail, instanceIndex, state, i, group, radius; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: center = (0, linear_algebra_1.Vec3)(); aPosition = values.aPosition.ref.value; aGroup = values.aGroup.ref.value; instanceCount = values.instanceCount.ref.value; vertexCount = values.uVertexCount.ref.value; meshes = []; sphereCount = vertexCount / 4 * instanceCount; if (sphereCount < 2000) detail = 3; else if (sphereCount < 20000) detail = 2; else detail = 1; for (instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) { state = mesh_builder_1.MeshBuilder.createState(512, 256); for (i = 0; i < vertexCount; i += 4) { v3fromArray(center, aPosition, i * 3); group = aGroup[i]; radius = MeshExporter.getSize(values, instanceIndex, group); state.currentGroup = group; (0, sphere_1.addSphere)(state, center, radius, detail); } meshes.push(mesh_builder_1.MeshBuilder.getMesh(state)); } return [4 /*yield*/, this.addMeshWithColors({ mesh: undefined, meshes: meshes, values: values, isGeoTexture: false, webgl: webgl, ctx: ctx })]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; MeshExporter.prototype.addCylinders = function (values, webgl, ctx) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var start, end, aStart, aEnd, aScale, aCap, aGroup, instanceCount, vertexCount, meshes, cylinderCount, radialSegments, instanceIndex, state, i, group, radius, cap, topCap, bottomCap, cylinderProps; return (0, tslib_1.__generator)(this, function (_a) { switch (_a.label) { case 0: start = (0, linear_algebra_1.Vec3)(); end = (0, linear_algebra_1.Vec3)(); aStart = values.aStart.ref.value; aEnd = values.aEnd.ref.value; aScale = values.aScale.ref.value; aCap = values.aCap.ref.value; aGroup = values.aGroup.ref.value; instanceCount = values.instanceCount.ref.value; vertexCount = values.uVertexCount.ref.value; meshes = []; cylinderCount = vertexCount / 6 * instanceCount; if (cylinderCount < 2000) radialSegments = 36; else if (cylinderCount < 20000) radialSegments = 24; else radialSegments = 12; for (instanceIndex = 0; instanceIndex < instanceCount; ++instanceIndex) { state = mesh_builder_1.MeshBuilder.createState(512, 256); for (i = 0; i < vertexCount; i += 6) { v3fromArray(start, aStart, i * 3); v3fromArray(end, aEnd, i * 3); group = aGroup[i]; radius = MeshExporter.getSize(values, instanceIndex, group) * aScale[i]; cap = aCap[i]; topCap = cap === 1 || cap === 3; bottomCap = cap >= 2; cylinderProps = { radiusTop: radius, radiusBottom: radius, topCap: topCap, bottomCap: bottomCap, radialSegments: radialSegments }; state.currentGroup = aGroup[i]; (0, cylinder_1.addCylinder)(state, start, end, 1, cylinderProps); } meshes.push(mesh_builder_1.MeshBuilder.getMesh(state)); } return [4 /*yield*/, this.addMeshWithColors({ mesh: undefined, meshes: meshes, values: values, isGeoTexture: false, webgl: webgl, ctx: ctx })]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; MeshExporter.prototype.addTextureMesh = function (values, webgl, ctx) { return (0, tslib_1.__awaiter)(this, void 0, void 0, function () { var framebuffer, _a, width, height, vertices, normals, groups, vertexCount, drawCount; return (0, tslib_1.__generator)(this, function (_b) { switch (_b.label) { case 0: if (!webgl.namedFramebuffers[GeoExportName]) { webgl.namedFramebuffers[GeoExportName] = webgl.resources.framebuffer(); } framebuffer = webgl.namedFramebuffers[GeoExportName]; _a = values.uGeoTexDim.ref.value, width = _a[0], height = _a[1]; vertices = new Float32Array(width * height * 4); normals = new Float32Array(width * height * 4); groups = webgl.isWebGL2 ? new Uint8Array(width * height * 4) : new Float32Array(width * height * 4); framebuffer.bind(); values.tPosition.ref.value.attachFramebuffer(framebuffer, 0); webgl.readPixels(0, 0, width, height, vertices); values.tNormal.ref.value.attachFramebuffer(framebuffer, 0); webgl.readPixels(0, 0, width, height, normals); values.tGroup.ref.value.attachFramebuffer(framebuffer, 0); webgl.readPixels(0, 0, width, height, groups); vertexCount = values.uVertexCount.ref.value; drawCount = values.drawCount.ref.value; return [4 /*yield*/, this.addMeshWithColors({ mesh: { vertices: vertices, normals: normals, indices: undefined, groups: groups, vertexCount: vertexCount, drawCount: drawCount }, meshes: undefined, values: values, isGeoTexture: true, webgl: webgl, ctx: ctx })]; case 1: _b.sent(); return [2 /*return*/]; } }); }); }; MeshExporter.prototype.add = function (renderObject, webgl, ctx) { if (!renderObject.state.visible) return; switch (renderObject.type) { case 'mesh': return this.addMesh(renderObject.values, webgl, ctx); case 'lines': return this.addLines(renderObject.values, webgl, ctx); case 'points': return this.addPoints(renderObject.values, webgl, ctx); case 'spheres': return this.addSpheres(renderObject.values, webgl, ctx); case 'cylinders': return this.addCylinders(renderObject.values, webgl, ctx); case 'texture-mesh': return this.addTextureMesh(renderObject.values, webgl, ctx); } }; return MeshExporter; }()); exports.MeshExporter = MeshExporter; //# sourceMappingURL=mesh-exporter.js.map