molstar
Version:
A comprehensive macromolecular library.
362 lines (361 loc) • 20.5 kB
JavaScript
"use strict";
/**
* Copyright (c) 2018-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Alexander Rose <alexander.rose@weirdbyte.de>
* @author David Sehnal <david.sehnal@gmail.com>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.dihedralLabel = exports.angleLabel = exports.distanceLabel = exports.elementLabel = exports._bundleLabel = exports.bundleLabel = exports.bondLabel = exports.structureElementLociLabelMany = exports.structureElementStatsLabel = exports.lociLabel = exports.DefaultLabelOptions = void 0;
var tslib_1 = require("tslib");
var structure_1 = require("../mol-model/structure");
var loci_1 = require("../mol-model/loci");
var int_1 = require("../mol-data/int");
var string_1 = require("../mol-util/string");
var linear_algebra_1 = require("../mol-math/linear-algebra");
var misc_1 = require("../mol-math/misc");
var volume_1 = require("../mol-model/volume");
exports.DefaultLabelOptions = {
granularity: 'element',
condensed: false,
reverse: false,
countsOnly: false,
hidePrefix: false,
htmlStyling: true,
};
function lociLabel(loci, options) {
if (options === void 0) { options = {}; }
switch (loci.kind) {
case 'structure-loci':
return loci.structure.models.map(function (m) { return m.entry; }).filter(function (l) { return !!l; }).join(', ');
case 'element-loci':
return structureElementStatsLabel(structure_1.StructureElement.Stats.ofLoci(loci), options);
case 'bond-loci':
var bond = loci.bonds[0];
return bond ? bondLabel(bond) : '';
case 'shape-loci':
return loci.shape.name;
case 'group-loci':
var g = loci.groups[0];
return g ? loci.shape.getLabel(int_1.OrderedSet.start(g.ids), g.instance) : '';
case 'every-loci':
return 'Everything';
case 'empty-loci':
return 'Nothing';
case 'data-loci':
return loci.getLabel();
case 'volume-loci':
return loci.volume.label || 'Volume';
case 'isosurface-loci':
return [
"".concat(loci.volume.label || 'Volume'),
"Isosurface at ".concat(volume_1.Volume.IsoValue.toString(loci.isoValue))
].join(' | ');
case 'cell-loci':
var size = int_1.OrderedSet.size(loci.indices);
var start = int_1.OrderedSet.start(loci.indices);
var absVal = volume_1.Volume.IsoValue.absolute(loci.volume.grid.cells.data[start]);
var relVal = volume_1.Volume.IsoValue.toRelative(absVal, loci.volume.grid.stats);
var label = [
"".concat(loci.volume.label || 'Volume'),
"".concat(size === 1 ? "Cell #".concat(start) : "".concat(size, " Cells"))
];
if (size === 1) {
label.push("".concat(volume_1.Volume.IsoValue.toString(absVal), " (").concat(volume_1.Volume.IsoValue.toString(relVal), ")"));
}
return label.join(' | ');
}
}
exports.lociLabel = lociLabel;
function countLabel(count, label) {
return count === 1 ? "1 ".concat(label) : "".concat(count, " ").concat(label, "s");
}
function otherLabel(count, location, granularity, hidePrefix, reverse, condensed) {
return "".concat(elementLabel(location, { granularity: granularity, hidePrefix: hidePrefix, reverse: reverse, condensed: condensed }), " <small>[+ ").concat(countLabel(count - 1, "other ".concat((0, string_1.capitalize)(granularity))), "]</small>");
}
/** Gets residue count of the model chain segments the unit is a subset of */
function getResidueCount(unit) {
var elements = unit.elements, model = unit.model;
var _a = model.atomicHierarchy, chainAtomSegments = _a.chainAtomSegments, residueAtomSegments = _a.residueAtomSegments;
var elementStart = chainAtomSegments.offsets[chainAtomSegments.index[elements[0]]];
var elementEnd = chainAtomSegments.offsets[chainAtomSegments.index[elements[elements.length - 1]] + 1] - 1;
return residueAtomSegments.index[elementEnd] - residueAtomSegments.index[elementStart] + 1;
}
function structureElementStatsLabel(stats, options) {
if (options === void 0) { options = {}; }
var o = tslib_1.__assign(tslib_1.__assign({}, exports.DefaultLabelOptions), options);
var label = _structureElementStatsLabel(stats, o.countsOnly, o.hidePrefix, o.condensed, o.reverse);
return o.htmlStyling ? label : (0, string_1.stripTags)(label);
}
exports.structureElementStatsLabel = structureElementStatsLabel;
function structureElementLociLabelMany(locis, options) {
if (options === void 0) { options = {}; }
var stats = structure_1.StructureElement.Stats.create();
for (var _i = 0, locis_1 = locis; _i < locis_1.length; _i++) {
var l = locis_1[_i];
structure_1.StructureElement.Stats.add(stats, stats, structure_1.StructureElement.Stats.ofLoci(l));
}
return structureElementStatsLabel(stats, options);
}
exports.structureElementLociLabelMany = structureElementLociLabelMany;
function _structureElementStatsLabel(stats, countsOnly, hidePrefix, condensed, reverse) {
if (countsOnly === void 0) { countsOnly = false; }
if (hidePrefix === void 0) { hidePrefix = false; }
if (condensed === void 0) { condensed = false; }
if (reverse === void 0) { reverse = false; }
var structureCount = stats.structureCount, chainCount = stats.chainCount, residueCount = stats.residueCount, conformationCount = stats.conformationCount, elementCount = stats.elementCount;
if (!countsOnly && elementCount === 1 && residueCount === 0 && chainCount === 0) {
return elementLabel(stats.firstElementLoc, { hidePrefix: hidePrefix, condensed: condensed, granularity: 'element', reverse: reverse });
}
else if (!countsOnly && elementCount === 0 && residueCount === 1 && chainCount === 0) {
return elementLabel(stats.firstResidueLoc, { hidePrefix: hidePrefix, condensed: condensed, granularity: 'residue', reverse: reverse });
}
else if (!countsOnly && elementCount === 0 && residueCount === 0 && chainCount === 1) {
var unit = stats.firstChainLoc.unit;
var granularity = (structure_1.Unit.isAtomic(unit) && getResidueCount(unit) === 1)
? 'residue' : structure_1.Unit.Traits.is(unit.traits, 1 /* Unit.Trait.MultiChain */)
? 'residue' : 'chain';
return elementLabel(stats.firstChainLoc, { hidePrefix: hidePrefix, condensed: condensed, granularity: granularity, reverse: reverse });
}
else if (!countsOnly) {
var label = [];
if (structureCount > 0) {
label.push(structureCount === 1 ? elementLabel(stats.firstStructureLoc, { hidePrefix: hidePrefix, condensed: condensed, granularity: 'structure', reverse: reverse }) : otherLabel(structureCount, stats.firstStructureLoc, 'structure', hidePrefix, reverse, condensed));
}
if (chainCount > 0) {
label.push(chainCount === 1 ? elementLabel(stats.firstChainLoc, { condensed: condensed, granularity: 'chain', hidePrefix: hidePrefix, reverse: reverse }) : otherLabel(chainCount, stats.firstChainLoc, 'chain', hidePrefix, reverse, condensed));
hidePrefix = true;
}
if (residueCount > 0) {
label.push(residueCount === 1 ? elementLabel(stats.firstResidueLoc, { condensed: condensed, granularity: 'residue', hidePrefix: hidePrefix, reverse: reverse }) : otherLabel(residueCount, stats.firstResidueLoc, 'residue', hidePrefix, reverse, condensed));
hidePrefix = true;
}
if (conformationCount > 0) {
label.push(conformationCount === 1 ? elementLabel(stats.firstConformationLoc, { condensed: condensed, granularity: 'conformation', hidePrefix: hidePrefix, reverse: reverse }) : otherLabel(conformationCount, stats.firstConformationLoc, 'conformation', hidePrefix, reverse, condensed));
hidePrefix = true;
}
if (elementCount > 0) {
label.push(elementCount === 1 ? elementLabel(stats.firstElementLoc, { condensed: condensed, granularity: 'element', hidePrefix: hidePrefix, reverse: reverse }) : otherLabel(elementCount, stats.firstElementLoc, 'element', hidePrefix, reverse, condensed));
}
return label.join('<small> + </small>');
}
else {
var label = [];
if (structureCount > 0)
label.push(countLabel(structureCount, 'Structure'));
if (chainCount > 0)
label.push(countLabel(chainCount, 'Chain'));
if (residueCount > 0)
label.push(countLabel(residueCount, 'Residue'));
if (conformationCount > 0)
label.push(countLabel(conformationCount, 'Conformation'));
if (elementCount > 0)
label.push(countLabel(elementCount, 'Element'));
return label.join('<small> + </small>');
}
}
function bondLabel(bond, options) {
if (options === void 0) { options = {}; }
return bundleLabel({ loci: [
structure_1.StructureElement.Loci(bond.aStructure, [{ unit: bond.aUnit, indices: int_1.OrderedSet.ofSingleton(bond.aIndex) }]),
structure_1.StructureElement.Loci(bond.bStructure, [{ unit: bond.bUnit, indices: int_1.OrderedSet.ofSingleton(bond.bIndex) }])
] }, options);
}
exports.bondLabel = bondLabel;
function bundleLabel(bundle, options) {
if (options === void 0) { options = {}; }
var o = tslib_1.__assign(tslib_1.__assign({}, exports.DefaultLabelOptions), options);
var label = _bundleLabel(bundle, o);
return o.htmlStyling ? label : (0, string_1.stripTags)(label);
}
exports.bundleLabel = bundleLabel;
function _bundleLabel(bundle, options) {
var granularity = options.granularity, hidePrefix = options.hidePrefix, reverse = options.reverse, condensed = options.condensed;
var isSingleElements = true;
for (var _i = 0, _a = bundle.loci; _i < _a.length; _i++) {
var l = _a[_i];
if (!structure_1.StructureElement.Loci.is(l) || structure_1.StructureElement.Loci.size(l) !== 1) {
isSingleElements = false;
break;
}
}
if (isSingleElements) {
var locations = bundle.loci.map(function (l) {
var _a = l.elements[0], unit = _a.unit, indices = _a.indices;
return structure_1.StructureElement.Location.create(l.structure, unit, unit.elements[int_1.OrderedSet.start(indices)]);
});
var labels = locations.map(function (l) { return _elementLabel(l, granularity, hidePrefix, reverse || condensed); });
if (condensed) {
return labels.map(function (l) { return l[0].replace(/\[.*\]/g, '').trim(); }).filter(function (l) { return !!l; }).join(' \u2014 ');
}
var offset = 0;
for (var i = 0, il = Math.min.apply(Math, labels.map(function (l) { return l.length; })) - 1; i < il; ++i) {
var areIdentical = true;
for (var j = 1, jl = labels.length; j < jl; ++j) {
if (labels[0][i] !== labels[j][i]) {
areIdentical = false;
break;
}
}
if (areIdentical)
offset += 1;
else
break;
}
if (offset > 0) {
var offsetLabels = [labels[0].join(' | ')];
for (var j = 1, jl = labels.length; j < jl; ++j) {
offsetLabels.push(labels[j].slice(offset).filter(function (l) { return !!l; }).join(' | '));
}
return offsetLabels.join(' \u2014 ');
}
else {
return labels.map(function (l) { return l.filter(function (l) { return !!l; }).join(' | '); }).filter(function (l) { return !!l; }).join('</br>');
}
}
else {
var labels = bundle.loci.map(function (l) { return lociLabel(l, options); });
return labels.filter(function (l) { return !!l; }).join(condensed ? ' \u2014 ' : '</br>');
}
}
exports._bundleLabel = _bundleLabel;
function elementLabel(location, options) {
var _a, _b;
if (options === void 0) { options = {}; }
var o = tslib_1.__assign(tslib_1.__assign({}, exports.DefaultLabelOptions), options);
var _label = _elementLabel(location, o.granularity, o.hidePrefix, o.reverse || o.condensed);
// TODO: condensed label for single atom structure returns empty label.. handle this case here?
var label = o.condensed ? (_b = (_a = _label[0]) === null || _a === void 0 ? void 0 : _a.replace(/\[.*\]/g, '').trim()) !== null && _b !== void 0 ? _b : '' : _label.filter(function (l) { return !!l; }).join(' | ');
return o.htmlStyling ? label : (0, string_1.stripTags)(label);
}
exports.elementLabel = elementLabel;
function _elementLabel(location, granularity, hidePrefix, reverse) {
if (granularity === void 0) { granularity = 'element'; }
if (hidePrefix === void 0) { hidePrefix = false; }
if (reverse === void 0) { reverse = false; }
var label = [];
if (!hidePrefix) {
var entry = location.unit.model.entry;
if (entry.length > 30)
entry = entry.substr(0, 27) + '\u2026'; // ellipsis
label.push("<small>".concat(entry, "</small>")); // entry
if (granularity !== 'structure') {
label.push("<small>Model ".concat(location.unit.model.modelNum, "</small>")); // model
label.push("<small>Instance ".concat(location.unit.conformation.operator.name, "</small>")); // instance
}
}
if (structure_1.Unit.isAtomic(location.unit)) {
label.push.apply(label, _atomicElementLabel(location, granularity, reverse));
}
else if (structure_1.Unit.isCoarse(location.unit)) {
label.push.apply(label, _coarseElementLabel(location, granularity));
}
else {
label.push('Unknown');
}
return reverse ? label.reverse() : label;
}
function _atomicElementLabel(location, granularity, hideOccupancy) {
if (hideOccupancy === void 0) { hideOccupancy = false; }
var rI = structure_1.StructureElement.Location.residueIndex(location);
var label_asym_id = structure_1.StructureProperties.chain.label_asym_id(location);
var auth_asym_id = structure_1.StructureProperties.chain.auth_asym_id(location);
var has_label_seq_id = location.unit.model.atomicHierarchy.residues.label_seq_id.valueKind(rI) === 0 /* Column.ValueKind.Present */;
var label_seq_id = structure_1.StructureProperties.residue.label_seq_id(location);
var auth_seq_id = structure_1.StructureProperties.residue.auth_seq_id(location);
var ins_code = structure_1.StructureProperties.residue.pdbx_PDB_ins_code(location);
var comp_id = structure_1.StructureProperties.atom.label_comp_id(location);
var atom_id = structure_1.StructureProperties.atom.label_atom_id(location);
var alt_id = structure_1.StructureProperties.atom.label_alt_id(location);
var occupancy = structure_1.StructureProperties.atom.occupancy(location);
var microHetCompIds = structure_1.StructureProperties.residue.microheterogeneityCompIds(location);
var compId = granularity === 'residue' && microHetCompIds.length > 1 ?
"(".concat(microHetCompIds.join('|'), ")") : comp_id;
var label = [];
switch (granularity) {
case 'element':
label.push("<b>".concat(atom_id, "</b>").concat(alt_id ? "%".concat(alt_id) : ''));
case 'conformation':
if (granularity === 'conformation' && alt_id) {
label.push("<small>Conformation</small> <b>".concat(alt_id, "</b>"));
}
case 'residue':
var seq_id = label_seq_id === auth_seq_id || !has_label_seq_id ? auth_seq_id : label_seq_id;
label.push("<b>".concat(compId, " ").concat(seq_id, "</b>").concat(seq_id !== auth_seq_id ? " <small>[auth</small> <b>".concat(auth_seq_id, "</b><small>]</small>") : '', "<b>").concat(ins_code ? ins_code : '', "</b>"));
case 'chain':
if (label_asym_id === auth_asym_id) {
label.push("<b>".concat(label_asym_id, "</b>"));
}
else {
if (granularity === 'chain' && structure_1.Unit.Traits.is(location.unit.traits, 1 /* Unit.Trait.MultiChain */)) {
label.push("<small>[auth</small> <b>".concat(auth_asym_id, "</b><small>]</small>"));
}
else {
label.push("<b>".concat(label_asym_id, "</b> <small>[auth</small> <b>").concat(auth_asym_id, "</b><small>]</small>"));
}
}
}
if (label.length > 0 && occupancy !== 1 && !hideOccupancy) {
label[0] = "".concat(label[0], " <small>[occupancy</small> <b>").concat(Math.round(100 * occupancy) / 100, "</b><small>]</small>");
}
return label.reverse();
}
function _coarseElementLabel(location, granularity) {
var asym_id = structure_1.StructureProperties.coarse.asym_id(location);
var seq_id_begin = structure_1.StructureProperties.coarse.seq_id_begin(location);
var seq_id_end = structure_1.StructureProperties.coarse.seq_id_end(location);
var label = [];
switch (granularity) {
case 'element':
case 'conformation':
case 'residue':
if (seq_id_begin === seq_id_end) {
var entityIndex = structure_1.StructureProperties.coarse.entityKey(location);
var seq = location.unit.model.sequence.byEntityKey[entityIndex];
var comp_id = seq.sequence.compId.value(seq_id_begin - 1); // 1-indexed
label.push("<b>".concat(comp_id, " ").concat(seq_id_begin, "</b>"));
}
else {
label.push("<b>".concat(seq_id_begin, "-").concat(seq_id_end, "</b>"));
}
case 'chain':
label.push("<b>".concat(asym_id, "</b>"));
}
return label.reverse();
}
//
function distanceLabel(pair, options) {
if (options === void 0) { options = {}; }
var o = tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({}, exports.DefaultLabelOptions), { measureOnly: false, unitLabel: '\u212B' }), options);
var _a = pair.loci.map(function (l) { return loci_1.Loci.getCenter(l); }), cA = _a[0], cB = _a[1];
var distance = "".concat(linear_algebra_1.Vec3.distance(cA, cB).toFixed(2), " ").concat(o.unitLabel);
if (o.measureOnly)
return distance;
var label = bundleLabel(pair, o);
return o.condensed ? "".concat(distance, " | ").concat(label) : "Distance ".concat(distance, "</br>").concat(label);
}
exports.distanceLabel = distanceLabel;
function angleLabel(triple, options) {
if (options === void 0) { options = {}; }
var o = tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({}, exports.DefaultLabelOptions), { measureOnly: false }), options);
var _a = triple.loci.map(function (l) { return loci_1.Loci.getCenter(l); }), cA = _a[0], cB = _a[1], cC = _a[2];
var vAB = linear_algebra_1.Vec3.sub((0, linear_algebra_1.Vec3)(), cA, cB);
var vCB = linear_algebra_1.Vec3.sub((0, linear_algebra_1.Vec3)(), cC, cB);
var angle = "".concat((0, misc_1.radToDeg)(linear_algebra_1.Vec3.angle(vAB, vCB)).toFixed(2), "\u00B0");
if (o.measureOnly)
return angle;
var label = bundleLabel(triple, o);
return o.condensed ? "".concat(angle, " | ").concat(label) : "Angle ".concat(angle, "</br>").concat(label);
}
exports.angleLabel = angleLabel;
function dihedralLabel(quad, options) {
if (options === void 0) { options = {}; }
var o = tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({}, exports.DefaultLabelOptions), { measureOnly: false }), options);
var _a = quad.loci.map(function (l) { return loci_1.Loci.getCenter(l); }), cA = _a[0], cB = _a[1], cC = _a[2], cD = _a[3];
var dihedral = "".concat((0, misc_1.radToDeg)(linear_algebra_1.Vec3.dihedralAngle(cA, cB, cC, cD)).toFixed(2), "\u00B0");
if (o.measureOnly)
return dihedral;
var label = bundleLabel(quad, o);
return o.condensed ? "".concat(dihedral, " | ").concat(label) : "Dihedral ".concat(dihedral, "</br>").concat(label);
}
exports.dihedralLabel = dihedralLabel;