UNPKG

molstar

Version:

A comprehensive macromolecular library.

359 lines 15.7 kB
/** * Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { __assign } from "tslib"; import { ParamDefinition as PD } from '../../../mol-util/param-definition'; import { AssemblySymmetryProvider, AssemblySymmetry } from './prop'; import { MeshBuilder } from '../../../mol-geo/geometry/mesh/mesh-builder'; import { Vec3, Mat4, Mat3 } from '../../../mol-math/linear-algebra'; import { addCylinder } from '../../../mol-geo/geometry/mesh/builder/cylinder'; import { Mesh } from '../../../mol-geo/geometry/mesh/mesh'; import { Shape } from '../../../mol-model/shape'; import { ColorNames } from '../../../mol-util/color/names'; import { ShapeRepresentation } from '../../../mol-repr/shape/representation'; import { MarkerActions } from '../../../mol-util/marker-action'; import { Prism, PrismCage } from '../../../mol-geo/primitive/prism'; import { Wedge, WedgeCage } from '../../../mol-geo/primitive/wedge'; import { transformPrimitive } from '../../../mol-geo/primitive/primitive'; import { memoize1 } from '../../../mol-util/memoize'; import { polygon } from '../../../mol-geo/primitive/polygon'; import { ColorMap } from '../../../mol-util/color'; import { TableLegend } from '../../../mol-util/legend'; import { Representation } from '../../../mol-repr/representation'; import { transformCage, cloneCage } from '../../../mol-geo/primitive/cage'; import { OctahedronCage } from '../../../mol-geo/primitive/octahedron'; import { TetrahedronCage } from '../../../mol-geo/primitive/tetrahedron'; import { IcosahedronCage } from '../../../mol-geo/primitive/icosahedron'; import { degToRad, radToDeg } from '../../../mol-math/misc'; import { equalEps } from '../../../mol-math/linear-algebra/3d/common'; import { isInteger } from '../../../mol-util/number'; import { Sphere3D } from '../../../mol-math/geometry'; var OrderColors = ColorMap({ '2': ColorNames.deepskyblue, '3': ColorNames.lime, 'N': ColorNames.red, }); var OrderColorsLegend = TableLegend(Object.keys(OrderColors).map(function (name) { return [name, OrderColors[name]]; })); function axesColorHelp(value) { return value.name === 'byOrder' ? { description: 'Color axes by their order', legend: OrderColorsLegend } : {}; } var SharedParams = __assign(__assign({}, Mesh.Params), { scale: PD.Numeric(2, { min: 0.1, max: 5, step: 0.1 }) }); var AxesParams = __assign(__assign({}, SharedParams), { axesColor: PD.MappedStatic('byOrder', { byOrder: PD.EmptyGroup(), uniform: PD.Group({ colorValue: PD.Color(ColorNames.orange), }, { isFlat: true }) }, { help: axesColorHelp }) }); var CageParams = __assign(__assign({}, SharedParams), { cageColor: PD.Color(ColorNames.orange) }); var AssemblySymmetryVisuals = { // cage should come before 'axes' so that the representative loci uses the cage shape 'cage': function (ctx, getParams) { return ShapeRepresentation(getCageShape, Mesh.Utils, { modifyState: function (s) { return (__assign(__assign({}, s), { markerActions: MarkerActions.Highlighting })); } }); }, 'axes': function (ctx, getParams) { return ShapeRepresentation(getAxesShape, Mesh.Utils, { modifyState: function (s) { return (__assign(__assign({}, s), { markerActions: MarkerActions.Highlighting })); } }); }, }; export var AssemblySymmetryParams = __assign(__assign(__assign({}, AxesParams), CageParams), { visuals: PD.MultiSelect(['axes', 'cage'], PD.objectToOptions(AssemblySymmetryVisuals)) }); // function getAssemblyName(s) { var _a; var id = ((_a = s.units[0].conformation.operator.assembly) === null || _a === void 0 ? void 0 : _a.id) || ''; return isInteger(id) ? "Assembly " + id : id; } var t = Mat4.identity(); var tmpV = Vec3(); var tmpCenter = Vec3(); var tmpScale = Vec3(); var getOrderPrimitive = memoize1(function (order) { if (order < 2) { return Prism(polygon(48, false)); } else if (order === 2) { var lens = Prism(polygon(48, false)); var m = Mat4.identity(); Mat4.scale(m, m, Vec3.create(1, 0.35, 1)); transformPrimitive(lens, m); return lens; } else if (order === 3) { return Wedge(); } else { return Prism(polygon(order, false)); } }); function getAxesMesh(data, props, mesh) { var scale = props.scale; var rotation_axes = data.rotation_axes; if (!AssemblySymmetry.isRotationAxes(rotation_axes)) return Mesh.createEmpty(mesh); var _a = rotation_axes[0], start = _a.start, end = _a.end; var radius = (Vec3.distance(start, end) / 500) * scale; Vec3.set(tmpScale, radius * 7, radius * 7, radius * 0.4); var cylinderProps = { radiusTop: radius, radiusBottom: radius }; var builderState = MeshBuilder.createState(256, 128, mesh); builderState.currentGroup = 0; Vec3.scale(tmpCenter, Vec3.add(tmpCenter, start, end), 0.5); for (var i = 0, il = rotation_axes.length; i < il; ++i) { var _b = rotation_axes[i], order = _b.order, start_1 = _b.start, end_1 = _b.end; builderState.currentGroup = i; addCylinder(builderState, start_1, end_1, 1, cylinderProps); var primitive = getOrderPrimitive(order); if (primitive) { Vec3.scale(tmpCenter, Vec3.add(tmpCenter, start_1, end_1), 0.5); if (Vec3.dot(Vec3.unitY, Vec3.sub(tmpV, start_1, tmpCenter)) === 0) { Mat4.targetTo(t, start_1, tmpCenter, Vec3.unitY); } else { Mat4.targetTo(t, start_1, tmpCenter, Vec3.unitX); } Mat4.scale(t, t, tmpScale); Mat4.setTranslation(t, start_1); MeshBuilder.addPrimitive(builderState, t, primitive); Mat4.setTranslation(t, end_1); MeshBuilder.addPrimitive(builderState, t, primitive); } } return MeshBuilder.getMesh(builderState); } function getAxesShape(ctx, data, props, shape) { var assemblySymmetry = AssemblySymmetryProvider.get(data).value; var geo = getAxesMesh(assemblySymmetry, props, shape && shape.geometry); var getColor = function (groupId) { var _a; if (props.axesColor.name === 'byOrder') { var rotation_axes = assemblySymmetry.rotation_axes; var order = (_a = rotation_axes[groupId]) === null || _a === void 0 ? void 0 : _a.order; if (order === 2) return OrderColors[2]; else if (order === 3) return OrderColors[3]; else return OrderColors.N; } else { return props.axesColor.params.colorValue; } }; var getLabel = function (groupId) { var _a; var type = assemblySymmetry.type, symbol = assemblySymmetry.symbol, kind = assemblySymmetry.kind, rotation_axes = assemblySymmetry.rotation_axes; var order = (_a = rotation_axes[groupId]) === null || _a === void 0 ? void 0 : _a.order; return [ "<small>" + data.model.entryId + "</small>", "<small>" + getAssemblyName(data) + "</small>", "Axis " + (groupId + 1) + " with Order " + order + " of " + type + " " + kind + " (" + symbol + ")" ].join(' | '); }; return Shape.create('Axes', data, geo, getColor, function () { return 1; }, getLabel); } // var getSymbolCage = memoize1(function (symbol) { if (symbol.startsWith('D') || symbol.startsWith('C')) { // z axis is prism axis, x/y axes cut through edge midpoints var fold = parseInt(symbol.substr(1)); var cage = void 0; if (fold === 2) { cage = PrismCage(polygon(4, false)); } else if (fold === 3) { cage = WedgeCage(); } else if (fold > 3) { cage = PrismCage(polygon(fold, false)); } else { return; } if (fold % 2 === 0) { return cage; } else { var m = Mat4.identity(); Mat4.rotate(m, m, 1 / fold * Math.PI / 2, Vec3.unitZ); return transformCage(cloneCage(cage), m); } } else if (symbol === 'O') { // x/y/z axes cut through order 4 vertices return OctahedronCage(); } else if (symbol === 'I') { // z axis cut through order 5 vertex // x axis cut through edge midpoint var cage = IcosahedronCage(); var m = Mat4.identity(); Mat4.rotate(m, m, degToRad(31.7), Vec3.unitX); return transformCage(cloneCage(cage), m); } else if (symbol === 'T') { // x/y/z axes cut through edge midpoints return TetrahedronCage(); } }); function getSymbolScale(symbol) { if (symbol.startsWith('D') || symbol.startsWith('C')) { return 0.75; } else if (symbol === 'O') { return 1.2; } else if (symbol === 'I') { return 0.25; } else if (symbol === 'T') { return 0.8; } return 1; } function setSymbolTransform(t, symbol, axes, size, structure) { var eye = Vec3(); var target = Vec3(); var dir = Vec3(); var up = Vec3(); var pair = undefined; if (symbol.startsWith('C')) { pair = [axes[0]]; } else if (symbol.startsWith('D')) { var fold_1 = parseInt(symbol.substr(1)); if (fold_1 === 2) { pair = axes.filter(function (a) { return a.order === 2; }); } else if (fold_1 >= 3) { var aN = axes.filter(function (a) { return a.order === fold_1; })[0]; var a2 = axes.filter(function (a) { return a.order === 2; })[1]; pair = [aN, a2]; } } else if (symbol === 'O') { pair = axes.filter(function (a) { return a.order === 4; }); } else if (symbol === 'I') { var a5 = axes.filter(function (a) { return a.order === 5; })[0]; var a5dir = Vec3.sub(Vec3(), a5.end, a5.start); pair = [a5]; for (var _i = 0, _a = axes.filter(function (a) { return a.order === 3; }); _i < _a.length; _i++) { var a = _a[_i]; var d = radToDeg(Vec3.angle(Vec3.sub(up, a.end, a.start), a5dir)); if (!pair[1] && (equalEps(d, 100.81, 0.1) || equalEps(d, 79.19, 0.1))) { pair[1] = a; break; } } } else if (symbol === 'T') { pair = axes.filter(function (a) { return a.order === 2; }); } Mat4.setIdentity(t); if (pair) { var aA = pair[0], aB = pair[1]; Vec3.scale(eye, Vec3.add(eye, aA.end, aA.start), 0.5); Vec3.copy(target, aA.end); if (aB) { Vec3.sub(up, aB.end, aB.start); Vec3.sub(dir, eye, target); if (Vec3.dot(dir, up) < 0) Vec3.negate(up, up); Mat4.targetTo(t, eye, target, up); if (symbol.startsWith('D')) { var sphere = structure.lookup3d.boundary.sphere; var sizeXY = (sphere.radius * 2) * 0.8; // fallback for missing extrema if (Sphere3D.hasExtrema(sphere)) { var n_1 = Mat3.directionTransform(Mat3(), t); var dirs = unitCircleDirections.map(function (d) { return Vec3.transformMat3(Vec3(), d, n_1); }); sizeXY = getMaxProjectedDistance(sphere.extrema, dirs, sphere.center) * 1.6; } Mat4.scale(t, t, Vec3.create(sizeXY, sizeXY, Vec3.distance(aA.start, aA.end) * 0.9)); } else { Mat4.scaleUniformly(t, t, size * getSymbolScale(symbol)); } } else { if (Vec3.dot(Vec3.unitY, Vec3.sub(tmpV, aA.end, aA.start)) === 0) { Vec3.copy(up, Vec3.unitY); } else { Vec3.copy(up, Vec3.unitX); } Mat4.targetTo(t, eye, target, up); var sphere = structure.lookup3d.boundary.sphere; var sizeXY = (sphere.radius * 2) * 0.8; // fallback for missing extrema if (Sphere3D.hasExtrema(sphere)) { var n_2 = Mat3.directionTransform(Mat3(), t); var dirs = unitCircleDirections.map(function (d) { return Vec3.transformMat3(Vec3(), d, n_2); }); sizeXY = getMaxProjectedDistance(sphere.extrema, dirs, sphere.center); } Mat4.scale(t, t, Vec3.create(sizeXY, sizeXY, size * 0.9)); } } } var unitCircleDirections = (function () { var dirs = []; var circle = polygon(12, false, 1); for (var i = 0, il = circle.length; i < il; i += 3) { dirs.push(Vec3.fromArray(Vec3(), circle, i)); } return dirs; })(); var tmpProj = Vec3(); function getMaxProjectedDistance(points, directions, center) { var maxDist = 0; for (var _i = 0, points_1 = points; _i < points_1.length; _i++) { var p = points_1[_i]; for (var _a = 0, directions_1 = directions; _a < directions_1.length; _a++) { var d = directions_1[_a]; Vec3.projectPointOnVector(tmpProj, p, d, center); var dist = Vec3.distance(tmpProj, center); if (dist > maxDist) maxDist = dist; } } return maxDist; } function getCageMesh(data, props, mesh) { var assemblySymmetry = AssemblySymmetryProvider.get(data).value; var scale = props.scale; var rotation_axes = assemblySymmetry.rotation_axes, symbol = assemblySymmetry.symbol; if (!AssemblySymmetry.isRotationAxes(rotation_axes)) return Mesh.createEmpty(mesh); var structure = AssemblySymmetry.getStructure(data, assemblySymmetry); var cage = getSymbolCage(symbol); if (!cage) return Mesh.createEmpty(mesh); var _a = rotation_axes[0], start = _a.start, end = _a.end; var size = Vec3.distance(start, end); var radius = (size / 500) * scale; var builderState = MeshBuilder.createState(256, 128, mesh); builderState.currentGroup = 0; setSymbolTransform(t, symbol, rotation_axes, size, structure); Vec3.scale(tmpCenter, Vec3.add(tmpCenter, start, end), 0.5); Mat4.setTranslation(t, tmpCenter); MeshBuilder.addCage(builderState, t, cage, radius, 1, 8); return MeshBuilder.getMesh(builderState); } function getCageShape(ctx, data, props, shape) { var assemblySymmetry = AssemblySymmetryProvider.get(data).value; var geo = getCageMesh(data, props, shape && shape.geometry); var getColor = function (groupId) { return props.cageColor; }; var getLabel = function (groupId) { var type = assemblySymmetry.type, symbol = assemblySymmetry.symbol, kind = assemblySymmetry.kind; data.model.entryId; return [ "<small>" + data.model.entryId + "</small>", "<small>" + getAssemblyName(data) + "</small>", "Cage of " + type + " " + kind + " (" + symbol + ")" ].join(' | '); }; return Shape.create('Cage', data, geo, getColor, function () { return 1; }, getLabel); } export function AssemblySymmetryRepresentation(ctx, getParams) { return Representation.createMulti('Assembly Symmetry', ctx, getParams, Representation.StateBuilder, AssemblySymmetryVisuals); } //# sourceMappingURL=representation.js.map