molstar
Version:
A comprehensive macromolecular library.
177 lines (176 loc) • 8.98 kB
JavaScript
/**
* Copyright (c) 2023-2025 mol* contributors, licensed under MIT, See LICENSE file for more info.
*
* @author Adam Midlik <midlik@gmail.com>
*/
import { Column } from '../../../mol-data/db.js';
import { SortedArray } from '../../../mol-data/int.js';
import { filterInPlace, range, sortIfNeeded } from '../../../mol-util/array.js';
import { MultiMap, NumberMap } from './utils.js';
export const IndicesAndSortings = {
/** Get `IndicesAndSortings` for a model (use a cached value or create if not available yet) */
get(model) {
var _a;
var _b;
return (_a = (_b = model._dynamicPropertyData)['indices-and-sortings']) !== null && _a !== void 0 ? _a : (_b['indices-and-sortings'] = this.create(model));
},
/** Create `IndicesAndSortings` for a model */
create(model) {
return {
atomic: createAtomicIndicesAndSortings(model),
spheres: createCoarseIndicesAndSortings(model.coarseHierarchy.spheres),
gaussians: createCoarseIndicesAndSortings(model.coarseHierarchy.gaussians),
};
},
};
/** Create `AtomicIndicesAndSortings` for a model */
function createAtomicIndicesAndSortings(model) {
const h = model.atomicHierarchy;
const nAtoms = h.atoms._rowCount;
if (nAtoms === 0)
return undefined;
const nChains = h.chains._rowCount;
const { label_entity_id, label_asym_id, auth_asym_id } = h.chains;
const { label_seq_id, auth_seq_id, pdbx_PDB_ins_code } = h.residues;
const { label_comp_id, auth_comp_id } = h.atoms;
const { Present } = Column.ValueKind;
const chainsByLabelEntityId = new MultiMap();
const chainsByLabelAsymId = new MultiMap();
const chainsByAuthAsymId = new MultiMap();
const residuesSortedByLabelSeqId = new Map();
const residuesSortedByAuthSeqId = new Map();
const residuesSortedBySourceIndex = new Map();
const residuesByInsCode = new Map();
const residuesByLabelCompId = new Map();
let residuesByLabelCompIdIsPure = true;
const residuesByAuthCompId = new Map();
let residuesByAuthCompIdIsPure = true;
const atomsById = new NumberMap(nAtoms + 1);
const atomsBySourceIndex = new NumberMap(nAtoms);
const _labelCompIdSet = new Set();
const _authCompIdSet = new Set();
for (let iChain = 0; iChain < nChains; iChain++) {
chainsByLabelEntityId.add(label_entity_id.value(iChain), iChain);
chainsByLabelAsymId.add(label_asym_id.value(iChain), iChain);
chainsByAuthAsymId.add(auth_asym_id.value(iChain), iChain);
const iResFrom = h.residueAtomSegments.index[h.chainAtomSegments.offsets[iChain]];
const iResTo = h.residueAtomSegments.index[h.chainAtomSegments.offsets[iChain + 1] - 1] + 1;
const residuesWithLabelSeqId = filterInPlace(range(iResFrom, iResTo), iRes => label_seq_id.valueKind(iRes) === Present);
residuesSortedByLabelSeqId.set(iChain, Sorting.create(residuesWithLabelSeqId, label_seq_id.value));
const residuesWithAuthSeqId = filterInPlace(range(iResFrom, iResTo), iRes => auth_seq_id.valueKind(iRes) === Present);
residuesSortedByAuthSeqId.set(iChain, Sorting.create(residuesWithAuthSeqId, auth_seq_id.value));
const residuesWithSourceIndex = range(iResFrom, iResTo);
residuesSortedBySourceIndex.set(iChain, Sorting.create(residuesWithSourceIndex, h.residueSourceIndex.value));
const residuesHereByInsCode = new MultiMap();
const residuesHereByLabelCompId = new MultiMap();
const residuesHereByAuthCompId = new MultiMap();
for (let iRes = iResFrom; iRes < iResTo; iRes++) {
if (pdbx_PDB_ins_code.valueKind(iRes) === Present) {
residuesHereByInsCode.add(pdbx_PDB_ins_code.value(iRes), iRes);
}
const iAtomFrom = h.residueAtomSegments.offsets[iRes];
const iAtomTo = h.residueAtomSegments.offsets[iRes + 1];
for (let iAtom = iAtomFrom; iAtom < iAtomTo; iAtom++) {
_labelCompIdSet.add(label_comp_id.value(iAtom));
_authCompIdSet.add(auth_comp_id.value(iAtom));
}
if (_labelCompIdSet.size > 1)
residuesByLabelCompIdIsPure = false;
if (_authCompIdSet.size > 1)
residuesByAuthCompIdIsPure = false;
for (const labelCompId of _labelCompIdSet)
residuesHereByLabelCompId.add(labelCompId, iRes);
for (const authCompId of _authCompIdSet)
residuesHereByAuthCompId.add(authCompId, iRes);
_labelCompIdSet.clear();
_authCompIdSet.clear();
}
residuesByInsCode.set(iChain, residuesHereByInsCode);
residuesByLabelCompId.set(iChain, residuesHereByLabelCompId);
residuesByAuthCompId.set(iChain, residuesHereByAuthCompId);
}
const atomId = model.atomicConformation.atomId.value;
const atomIndex = h.atomSourceIndex.value;
for (let iAtom = 0; iAtom < nAtoms; iAtom++) {
atomsById.set(atomId(iAtom), iAtom);
atomsBySourceIndex.set(atomIndex(iAtom), iAtom);
}
return {
chainsByLabelEntityId, chainsByLabelAsymId, chainsByAuthAsymId,
residuesSortedByLabelSeqId, residuesSortedByAuthSeqId, residuesSortedBySourceIndex, residuesByInsCode,
residuesByLabelCompId, residuesByLabelCompIdIsPure, residuesByAuthCompId, residuesByAuthCompIdIsPure,
atomsById, atomsBySourceIndex,
};
}
/** Create `CoarseIndicesAndSortings` for a coarse elements hierarchy */
function createCoarseIndicesAndSortings(coarseElements) {
if (coarseElements.count === 0)
return undefined;
const { entity_id, asym_id, seq_id_begin, seq_id_end, chainElementSegments } = coarseElements;
const { Present } = Column.ValueKind;
const nChains = Math.max(chainElementSegments.count, 0); // chainElementSegments.count is -1 when there are no coarse elements
const chainsByEntityId = new MultiMap();
const chainsByAsymId = new MultiMap();
const elementsSortedBySeqIdBegin = new Map();
const chains = {
count: nChains,
label_entity_id: new Array(nChains),
label_asym_id: new Array(nChains),
};
for (let iChain = 0; iChain < nChains; iChain++) {
const iElemFrom = chainElementSegments.offsets[iChain];
const iElemTo = chainElementSegments.offsets[iChain + 1];
const entityId = entity_id.value(iElemFrom);
const asymId = asym_id.value(iElemFrom);
chains.label_entity_id[iChain] = entityId;
chains.label_asym_id[iChain] = asymId;
chainsByEntityId.add(entityId, iChain);
chainsByAsymId.add(asymId, iChain);
const elementsWithSeqIds = filterInPlace(range(iElemFrom, iElemTo), iElem => seq_id_begin.valueKind(iElem) === Present && seq_id_end.valueKind(iElem) === Present);
const sorting = Sorting.create(elementsWithSeqIds, seq_id_begin.value);
const endBounds = sorting.keys.map(seq_id_end.value);
// Ensure non-decreasing endBounds:
for (let i = 1; i < endBounds.length; i++) {
if (endBounds[i - 1] > endBounds[i]) {
endBounds[i] = endBounds[i - 1];
}
}
elementsSortedBySeqIdBegin.set(iChain, { ...sorting, endUpperBounds: SortedArray.ofSortedArray(endBounds) });
}
return { chains, chainsByEntityId, chainsByAsymId, elementsSortedBySeqIdBegin };
}
export const Sorting = {
/** Create a `Sorting` from an array of keys and a function returning their corresponding values.
* If two keys have the same value, the smaller key will come first.
* This function modifies `keys` - create a copy if you need the original order! */
create(keys, valueFunction) {
sortIfNeeded(keys, (a, b) => valueFunction(a) - valueFunction(b) || a - b);
const values = SortedArray.ofSortedArray(keys.map(valueFunction));
return { keys, values };
},
/** Return a newly allocated array of keys which have value equal to `target`.
* The returned keys are sorted by their value. */
getKeysWithValue(sorting, target) {
return Sorting.getKeysWithValueInRange(sorting, target, target);
},
/** Return a newly allocated array of keys which have value within interval `[min, max]` (inclusive).
* The returned keys are sorted by their value.
* Undefined `min` is interpreted as negative infitity, undefined `max` is interpreted as positive infinity. */
getKeysWithValueInRange(sorting, min, max) {
const { keys, values } = sorting;
if (!keys)
return [];
const n = keys.length;
const from = (min !== undefined) ? SortedArray.findPredecessorIndex(values, min) : 0;
let to;
if (max !== undefined) {
to = from;
while (to < n && values[to] <= max)
to++;
}
else {
to = n;
}
return keys.slice(from, to);
},
};