UNPKG

molstar

Version:

A comprehensive macromolecular library.

177 lines (176 loc) 8.98 kB
/** * 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); }, };