molstar
Version:
A comprehensive macromolecular library.
385 lines • 20.7 kB
JavaScript
"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