Skip to content
Snippets Groups Projects
traverse.ts 4.32 KiB
import { Segmentation } from '../../mol-data/int';
import { OrderedSet } from '../../mol-data/int/ordered-set';
import { EmptyLoci } from '../../mol-model/loci';
import { ResidueIndex, Structure, StructureElement, StructureProperties, Unit } from '../../mol-model/structure';
import { structureUnion } from '../../mol-model/structure/query/utils/structure-set';
import { Location } from '../../mol-model/structure/structure/element/location';

export namespace Traverse {
    type Residue = Segmentation.Segment<ResidueIndex>;

    export function residueAltIds(structure: Structure, unit: Unit, residue: Residue) {
        const altIds = new Array<string>();
        const loc = Location.create(structure, unit);
        for (let rI = residue.start; rI < residue.end; rI++) {
            loc.element = OrderedSet.getAt(unit.elements, rI);
            const altId = StructureProperties.atom.label_alt_id(loc);
            if (altId !== '' && !altIds.includes(altId))
                altIds.push(altId);
        }

        return altIds;
    }

    // TODO: We will be able to use a function from DnatcoUtils once it gets upstreamed
    const _loc = StructureElement.Location.create();
    export function findResidue(asymId: string, seqId: number, altId: string | undefined, insCode: string, loci: StructureElement.Loci, source: 'label' | 'auth') {
        _loc.structure = loci.structure;
        for (const e of loci.elements) {
            _loc.unit = e.unit;

            const getAsymId = source === 'label' ? StructureProperties.chain.label_asym_id : StructureProperties.chain.auth_asym_id;
            const getSeqId = source === 'label' ? StructureProperties.residue.label_seq_id : StructureProperties.residue.auth_seq_id;

            // Walk the entire unit and look for the requested residue
            const chainIt = Segmentation.transientSegments(e.unit.model.atomicHierarchy.chainAtomSegments, e.unit.elements);
            const residueIt = Segmentation.transientSegments(e.unit.model.atomicHierarchy.residueAtomSegments, e.unit.elements);

            const elemIndex = (idx: number) => OrderedSet.getAt(e.unit.elements, idx);
            while (chainIt.hasNext) {
                const chain = chainIt.move();
                _loc.element = elemIndex(chain.start);
                const _asymId = getAsymId(_loc);
                if (_asymId !== asymId)
                    continue; // Wrong chain, skip it

                residueIt.setSegment(chain);
                while (residueIt.hasNext) {
                    const residue = residueIt.move();
                    _loc.element = elemIndex(residue.start);

                    const _seqId = getSeqId(_loc);
                    if (_seqId === seqId) {
                        const _insCode = StructureProperties.residue.pdbx_PDB_ins_code(_loc);
                        if (_insCode !== insCode)
                            continue;

                        if (altId) {
                            const _altIds = residueAltIds(loci.structure, e.unit, residue);
                            if (!_altIds.includes(altId))
                                continue;
                        }

                        const start = residue.start as StructureElement.UnitIndex;
                        const end = residue.end as StructureElement.UnitIndex;
                        return StructureElement.Loci(
                            loci.structure,
                            [{ unit: e.unit, indices: OrderedSet.ofBounds(start, end) }]
                        );
                    }
                }
            }
        }

        return EmptyLoci;
    }

    export function findStep(
        asymId: string,
        seqId1: number, altId1: string | undefined, insCode1: string,
        seqId2: number, altId2: string | undefined, insCode2: string,
        loci: StructureElement.Loci, source: 'label' | 'auth'
    ) {
        const first = findResidue(asymId, seqId1, altId1, insCode1, loci, source);
        if (first.kind === 'empty-loci')
            return EmptyLoci;

        const second = findResidue(asymId, seqId2, altId2, insCode2, loci, source);
        if (second.kind === 'empty-loci')
            return EmptyLoci;

        const union = structureUnion(loci.structure, [StructureElement.Loci.toStructure(first), StructureElement.Loci.toStructure(second)]);
        return Structure.toStructureElementLoci(union);
    }
}