UNPKG

molstar

Version:

A comprehensive macromolecular library.

128 lines (127 loc) 5.12 kB
/** * Copyright (c) 2023 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Adam Midlik <midlik@gmail.com> */ import { SortedArray } from '../../../mol-data/int'; import { arrayExtend, range } from '../../../mol-util/array'; export const AtomRanges = { /** Return the number of disjoined ranges in a `AtomRanges` object */ count(ranges) { return ranges.from.length; }, /** Create new `AtomRanges` without any atoms */ empty() { return { from: [], to: [] }; }, /** Create new `AtomRanges` containing a single range of atoms `[from, to)` */ single(from, to) { return { from: [from], to: [to] }; }, /** Add a range of atoms `[from, to)` to existing `AtomRanges` and return the modified original. * The added range must start after the end of the last existing range * (if it starts just on the next atom, these two ranges will get merged). */ add(ranges, from, to) { const n = AtomRanges.count(ranges); if (n > 0) { const lastTo = ranges.to[n - 1]; if (from < lastTo) throw new Error('Overlapping ranges not allowed'); if (from === lastTo) { ranges.to[n - 1] = to; } else { ranges.from.push(from); ranges.to.push(to); } } else { ranges.from.push(from); ranges.to.push(to); } return ranges; }, /** Apply function `func` to each range in `ranges` */ foreach(ranges, func) { const n = AtomRanges.count(ranges); for (let i = 0; i < n; i++) func(ranges.from[i], ranges.to[i]); }, /** Apply function `func` to each range in `ranges` and return an array with results */ map(ranges, func) { const n = AtomRanges.count(ranges); const result = new Array(n); for (let i = 0; i < n; i++) result[i] = func(ranges.from[i], ranges.to[i]); return result; }, /** Compute the set union of multiple `AtomRanges` objects (as sets of atoms) */ union(ranges) { const concat = AtomRanges.empty(); for (const r of ranges) { arrayExtend(concat.from, r.from); arrayExtend(concat.to, r.to); } const indices = range(concat.from.length).sort((i, j) => concat.from[i] - concat.from[j]); // sort by start of range const result = AtomRanges.empty(); let last = -1; for (const i of indices) { const from = concat.from[i]; const to = concat.to[i]; if (last >= 0 && from <= result.to[last]) { if (to > result.to[last]) { result.to[last] = to; } } else { result.from.push(from); result.to.push(to); last++; } } return result; }, /** Return a sorted subset of `atoms` which lie in any of `ranges` (i.e. set intersection of `atoms` and `ranges`). * If `out` is provided, use it to store the result (clear any old contents). * If `outFirstAtomIndex` is provided, fill `outFirstAtomIndex.value` with the index of the first selected atom (if any). */ selectAtomsInRanges(atoms, ranges, out, outFirstAtomIndex = {}) { var _a, _b; out !== null && out !== void 0 ? out : (out = []); out.length = 0; outFirstAtomIndex.value = undefined; const nAtoms = atoms.length; const nRanges = AtomRanges.count(ranges); if (nAtoms <= nRanges) { // Implementation 1 (more efficient when there are fewer atoms) let iRange = SortedArray.findPredecessorIndex(SortedArray.ofSortedArray(ranges.to), atoms[0] + 1); for (let iAtom = 0; iAtom < nAtoms; iAtom++) { const a = atoms[iAtom]; while (iRange < nRanges && ranges.to[iRange] <= a) iRange++; const qualifies = iRange < nRanges && ranges.from[iRange] <= a; if (qualifies) { out.push(a); (_a = outFirstAtomIndex.value) !== null && _a !== void 0 ? _a : (outFirstAtomIndex.value = iAtom); } } } else { // Implementation 2 (more efficient when there are fewer ranges) for (let iRange = 0; iRange < nRanges; iRange++) { const from = ranges.from[iRange]; const to = ranges.to[iRange]; for (let iAtom = SortedArray.findPredecessorIndex(atoms, from); iAtom < nAtoms; iAtom++) { const a = atoms[iAtom]; if (a < to) { out.push(a); (_b = outFirstAtomIndex.value) !== null && _b !== void 0 ? _b : (outFirstAtomIndex.value = iAtom); } else { break; } } } } return out; }, };