UNPKG

molstar

Version:

A comprehensive macromolecular library.

452 lines (451 loc) 25.5 kB
"use strict"; /** * Copyright (c) 2018-2024 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author Gianluca Tomasello <giagitom@gmail.com> */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CoarsePolymerTraceIterator = exports.AtomicPolymerTraceIterator = void 0; exports.PolymerTraceIterator = PolymerTraceIterator; const structure_1 = require("../../../../../mol-model/structure"); const int_1 = require("../../../../../mol-data/int"); const types_1 = require("../../../../../mol-model/structure/model/types"); const linear_algebra_1 = require("../../../../../mol-math/linear-algebra"); const sorted_ranges_1 = require("../../../../../mol-data/int/sorted-ranges"); const polymer_1 = require("../polymer"); const secondary_structure_1 = require("../../../../../mol-model-props/computed/secondary-structure"); const helix_orientation_1 = require("../../../../../mol-model-props/computed/helix-orientation"); function isHelixSS(ss) { return types_1.SecondaryStructureType.is(ss, 2 /* SecondaryStructureType.Flag.Helix */); } function isSheetSS(ss) { return types_1.SecondaryStructureType.is(ss, 4 /* SecondaryStructureType.Flag.Beta */); } /** * Iterates over individual residues/coarse elements in polymers of a unit while * providing information about the neighbourhood in the underlying model for drawing splines */ function PolymerTraceIterator(unit, structure, options = {}) { switch (unit.kind) { case 0 /* Unit.Kind.Atomic */: return new AtomicPolymerTraceIterator(unit, structure, options); case 1 /* Unit.Kind.Spheres */: case 2 /* Unit.Kind.Gaussians */: return new CoarsePolymerTraceIterator(unit, structure); } } const SecStrucTypeNA = types_1.SecondaryStructureType.create(536870912 /* SecondaryStructureType.Flag.NA */); function createPolymerTraceElement(structure, unit) { return { center: structure_1.StructureElement.Location.create(structure, unit), centerPrev: structure_1.StructureElement.Location.create(structure, unit), centerNext: structure_1.StructureElement.Location.create(structure, unit), first: false, last: false, initial: false, final: false, secStrucFirst: false, secStrucLast: false, secStrucType: SecStrucTypeNA, moleculeType: 0 /* MoleculeType.Unknown */, coarseBackboneFirst: false, coarseBackboneLast: false, p0: (0, linear_algebra_1.Vec3)(), p1: (0, linear_algebra_1.Vec3)(), p2: (0, linear_algebra_1.Vec3)(), p3: (0, linear_algebra_1.Vec3)(), p4: (0, linear_algebra_1.Vec3)(), d12: (0, linear_algebra_1.Vec3)(), d23: (0, linear_algebra_1.Vec3)() }; } const tmpDir = (0, linear_algebra_1.Vec3)(); const tmpVecA = (0, linear_algebra_1.Vec3)(); const tmpVecB = (0, linear_algebra_1.Vec3)(); class AtomicPolymerTraceIterator { atomicPos(target, index) { if (index !== -1) { target[0] = this.atomicConformation.x[index]; target[1] = this.atomicConformation.y[index]; target[2] = this.atomicConformation.z[index]; } } pos(target, residueIndex, ss) { const index = this.traceElementIndex[residueIndex]; if (this.helixOrientationCenters && isHelixSS(ss)) { linear_algebra_1.Vec3.fromArray(target, this.helixOrientationCenters, residueIndex * 3); } else { this.atomicPos(target, index); } } updateResidueSegmentRange(polymerSegment) { const { index } = this.residueAtomSegments; this.residueSegmentMin = index[this.polymerRanges[polymerSegment.index * 2]]; this.residueSegmentMax = index[this.polymerRanges[polymerSegment.index * 2 + 1]]; } getResidueIndex(residueIndex) { if (residueIndex < this.residueSegmentMin) { const cyclicIndex = this.cyclicPolymerMap.get(this.residueSegmentMin); if (cyclicIndex !== undefined) { residueIndex = cyclicIndex - (this.residueSegmentMin - residueIndex - 1); } else { residueIndex = this.residueSegmentMin; } } else if (residueIndex > this.residueSegmentMax) { const cyclicIndex = this.cyclicPolymerMap.get(this.residueSegmentMax); if (cyclicIndex !== undefined) { residueIndex = cyclicIndex + (residueIndex - this.residueSegmentMax - 1); } else { residueIndex = this.residueSegmentMax; } } return residueIndex; } getSecStruc(residueIndex) { if (this.secondaryStructure) { const { type, getIndex } = this.secondaryStructure; const ss = type[getIndex(residueIndex)]; // normalize helix-type return isHelixSS(ss) ? 2 /* SecondaryStructureType.Flag.Helix */ : ss; } else { return SecStrucTypeNA; } } setControlPoint(out, p1, p2, p3, ss) { if (isSheetSS(ss) || (this.helixOrientationCenters && isHelixSS(ss))) { linear_algebra_1.Vec3.scale(out, linear_algebra_1.Vec3.add(out, p1, linear_algebra_1.Vec3.add(out, p3, linear_algebra_1.Vec3.add(out, p2, p2))), 1 / 4); } else { linear_algebra_1.Vec3.copy(out, p2); } } setFromToVector(out, residueIndex, ss) { if (this.helixOrientationCenters && isHelixSS(ss)) { linear_algebra_1.Vec3.set(out, 1, 0, 0); } else { this.atomicPos(tmpVecA, this.directionFromElementIndex[residueIndex]); this.atomicPos(tmpVecB, this.directionToElementIndex[residueIndex]); linear_algebra_1.Vec3.sub(out, tmpVecB, tmpVecA); } } setDirection(out, v1, v2, v3) { linear_algebra_1.Vec3.matchDirection(tmpVecA, v1, v2); linear_algebra_1.Vec3.matchDirection(tmpVecB, v3, v2); linear_algebra_1.Vec3.scale(out, linear_algebra_1.Vec3.add(out, tmpVecA, linear_algebra_1.Vec3.add(out, tmpVecB, linear_algebra_1.Vec3.add(out, v2, v2))), 1 / 4); } move() { const { residueIt, polymerIt, value } = this; if (this.state === 0 /* AtomicPolymerTraceIteratorState.nextPolymer */) { while (polymerIt.hasNext) { this.polymerSegment = polymerIt.move(); residueIt.setSegment(this.polymerSegment); this.updateResidueSegmentRange(this.polymerSegment); if (residueIt.hasNext) { this.state = 1 /* AtomicPolymerTraceIteratorState.nextResidue */; const residueIndexBeg = this.residueAtomSegments.index[this.unit.elements[this.polymerSegment.start]]; const residueIndexBegPrev = this.getResidueIndex(residueIndexBeg - 1); this.currSecStrucType = residueIndexBeg === residueIndexBegPrev ? SecStrucTypeNA : this.getSecStruc(residueIndexBegPrev); this.nextSecStrucType = this.getSecStruc(residueIndexBeg); this.currCoarseBackbone = this.directionFromElementIndex[residueIndexBegPrev] === -1 || this.directionToElementIndex[residueIndexBegPrev] === -1; this.nextCoarseBackbone = this.directionFromElementIndex[residueIndexBeg] === -1 || this.directionToElementIndex[residueIndexBeg] === -1; break; } } } if (this.state === 1 /* AtomicPolymerTraceIteratorState.nextResidue */) { const { index: residueIndex } = residueIt.move(); const residueIndexPrev3 = this.getResidueIndex(residueIndex - 3); const residueIndexPrev2 = this.getResidueIndex(residueIndex - 2); const residueIndexPrev1 = this.getResidueIndex(residueIndex - 1); const residueIndexNext1 = this.getResidueIndex(residueIndex + 1); const residueIndexNext2 = this.getResidueIndex(residueIndex + 2); const residueIndexNext3 = this.getResidueIndex(residueIndex + 3); this.prevSecStrucType = this.getSecStruc(residueIndexPrev1); this.currSecStrucType = this.getSecStruc(residueIndex); this.nextSecStrucType = residueIndex === residueIndexNext1 ? SecStrucTypeNA : this.getSecStruc(residueIndexNext1); this.prevCoarseBackbone = this.currCoarseBackbone; this.currCoarseBackbone = this.nextCoarseBackbone; this.nextCoarseBackbone = this.directionFromElementIndex[residueIndexNext1] === -1 || this.directionToElementIndex[residueIndexNext1] === -1; value.secStrucType = this.currSecStrucType; value.secStrucFirst = this.prevSecStrucType !== this.currSecStrucType; value.secStrucLast = this.currSecStrucType !== this.nextSecStrucType; value.coarseBackboneFirst = this.prevCoarseBackbone !== this.currCoarseBackbone; value.coarseBackboneLast = this.currCoarseBackbone !== this.nextCoarseBackbone; value.first = residueIndex === this.residueSegmentMin; value.last = residueIndex === this.residueSegmentMax; value.moleculeType = this.moleculeType[residueIndex]; value.initial = residueIndex === residueIndexPrev1; value.final = residueIndex === residueIndexNext1; value.centerPrev.element = this.traceElementIndex[residueIndexPrev1]; value.center.element = this.traceElementIndex[residueIndex]; value.centerNext.element = this.traceElementIndex[residueIndexNext1]; const ssPrev3 = this.getSecStruc(residueIndexPrev3); const ssPrev2 = this.getSecStruc(residueIndexPrev2); const ssPrev1 = this.getSecStruc(residueIndexPrev1); const ss = this.getSecStruc(residueIndex); const ssNext1 = this.getSecStruc(residueIndexNext1); const ssNext2 = this.getSecStruc(residueIndexNext2); const ssNext3 = this.getSecStruc(residueIndexNext3); this.pos(this.p0, residueIndexPrev3, ssPrev3); this.pos(this.p1, residueIndexPrev2, ssPrev2); this.pos(this.p2, residueIndexPrev1, ssPrev1); this.pos(this.p3, residueIndex, ss); this.pos(this.p4, residueIndexNext1, ssNext1); this.pos(this.p5, residueIndexNext2, ssNext2); this.pos(this.p6, residueIndexNext3, ssNext3); const isHelixPrev3 = isHelixSS(ssPrev3); const isHelixPrev2 = isHelixSS(ssPrev2); const isHelixPrev1 = isHelixSS(ssPrev1); const isHelix = isHelixSS(ss); const isHelixNext1 = isHelixSS(ssNext1); const isHelixNext2 = isHelixSS(ssNext2); const isHelixNext3 = isHelixSS(ssNext3); // handle positions for tubular helices if (this.helixOrientationCenters && !(isHelix && value.secStrucFirst && value.secStrucLast)) { if (isHelix !== isHelixPrev1) { if (isHelix) { linear_algebra_1.Vec3.copy(this.p0, this.p3); linear_algebra_1.Vec3.copy(this.p1, this.p3); linear_algebra_1.Vec3.copy(this.p2, this.p3); } else if (isHelixPrev1) { linear_algebra_1.Vec3.scale(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p2, this.p3), 2); linear_algebra_1.Vec3.add(this.p2, this.p3, tmpDir); linear_algebra_1.Vec3.add(this.p1, this.p2, tmpDir); linear_algebra_1.Vec3.add(this.p0, this.p1, tmpDir); } } else if (isHelix !== isHelixPrev2) { if (isHelix) { linear_algebra_1.Vec3.copy(this.p0, this.p2); linear_algebra_1.Vec3.copy(this.p1, this.p2); } else if (isHelixPrev2) { linear_algebra_1.Vec3.scale(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p1, this.p2), 2); linear_algebra_1.Vec3.add(this.p1, this.p2, tmpDir); linear_algebra_1.Vec3.add(this.p0, this.p1, tmpDir); } } else if (isHelix !== isHelixPrev3) { if (isHelix) { linear_algebra_1.Vec3.copy(this.p0, this.p1); } else if (isHelixPrev3) { linear_algebra_1.Vec3.scale(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p0, this.p1), 2); linear_algebra_1.Vec3.add(this.p0, this.p1, tmpDir); } } if (isHelix !== isHelixNext1) { if (isHelix) { linear_algebra_1.Vec3.copy(this.p4, this.p3); linear_algebra_1.Vec3.copy(this.p5, this.p3); linear_algebra_1.Vec3.copy(this.p6, this.p3); } else if (isHelixNext1) { linear_algebra_1.Vec3.scale(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p4, this.p3), 2); linear_algebra_1.Vec3.add(this.p4, this.p3, tmpDir); linear_algebra_1.Vec3.add(this.p5, this.p4, tmpDir); linear_algebra_1.Vec3.add(this.p6, this.p5, tmpDir); } } else if (isHelix !== isHelixNext2) { if (isHelix) { linear_algebra_1.Vec3.copy(this.p5, this.p4); linear_algebra_1.Vec3.copy(this.p6, this.p4); } else if (isHelixNext2) { linear_algebra_1.Vec3.scale(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p5, this.p4), 2); linear_algebra_1.Vec3.add(this.p5, this.p4, tmpDir); linear_algebra_1.Vec3.add(this.p6, this.p5, tmpDir); } } else if (isHelix !== isHelixNext3) { if (isHelix) { linear_algebra_1.Vec3.copy(this.p6, this.p5); } else if (isHelixNext3) { linear_algebra_1.Vec3.scale(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p6, this.p5), 2); linear_algebra_1.Vec3.add(this.p6, this.p5, tmpDir); } } } if (this.currCoarseBackbone) { linear_algebra_1.Vec3.triangleNormal(this.d01, this.p1, this.p2, this.p3); linear_algebra_1.Vec3.triangleNormal(this.d12, this.p2, this.p3, this.p4); linear_algebra_1.Vec3.triangleNormal(this.d23, this.p3, this.p4, this.p5); linear_algebra_1.Vec3.triangleNormal(this.d34, this.p4, this.p5, this.p6); } else { this.setFromToVector(this.d01, residueIndexPrev1, ssPrev1); this.setFromToVector(this.d12, residueIndex, ss); this.setFromToVector(this.d23, residueIndexNext1, ssNext1); this.setFromToVector(this.d34, residueIndexNext2, ssNext2); } const helixFlag = isHelix && this.helixOrientationCenters; // extend termini const f = 1.5; if (residueIndex === residueIndexPrev1 || (ss !== ssPrev1 && helixFlag)) { linear_algebra_1.Vec3.setMagnitude(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p3, this.p4), f); linear_algebra_1.Vec3.add(this.p2, this.p3, tmpDir); linear_algebra_1.Vec3.add(this.p1, this.p2, tmpDir); linear_algebra_1.Vec3.add(this.p0, this.p1, tmpDir); } else if (residueIndexPrev1 === residueIndexPrev2 || (ss !== ssPrev2 && helixFlag)) { linear_algebra_1.Vec3.setMagnitude(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p2, this.p3), f); linear_algebra_1.Vec3.add(this.p1, this.p2, tmpDir); linear_algebra_1.Vec3.add(this.p0, this.p1, tmpDir); } else if (residueIndexPrev2 === residueIndexPrev3 || (ss !== ssPrev3 && helixFlag)) { linear_algebra_1.Vec3.setMagnitude(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p1, this.p2), f); linear_algebra_1.Vec3.add(this.p0, this.p1, tmpDir); } if (residueIndex === residueIndexNext1 || (ss !== ssNext1 && helixFlag)) { linear_algebra_1.Vec3.setMagnitude(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p3, this.p2), f); linear_algebra_1.Vec3.add(this.p4, this.p3, tmpDir); linear_algebra_1.Vec3.add(this.p5, this.p4, tmpDir); linear_algebra_1.Vec3.add(this.p6, this.p5, tmpDir); } else if (residueIndexNext1 === residueIndexNext2 || (ss !== ssNext2 && helixFlag)) { linear_algebra_1.Vec3.setMagnitude(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p4, this.p3), f); linear_algebra_1.Vec3.add(this.p5, this.p4, tmpDir); linear_algebra_1.Vec3.add(this.p6, this.p5, tmpDir); } else if (residueIndexNext2 === residueIndexNext3 || (ss !== ssNext3 && helixFlag)) { linear_algebra_1.Vec3.setMagnitude(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.p5, this.p4), f); linear_algebra_1.Vec3.add(this.p6, this.p5, tmpDir); } this.setControlPoint(value.p0, this.p0, this.p1, this.p2, ssPrev2); this.setControlPoint(value.p1, this.p1, this.p2, this.p3, ssPrev1); this.setControlPoint(value.p2, this.p2, this.p3, this.p4, ss); this.setControlPoint(value.p3, this.p3, this.p4, this.p5, ssNext1); this.setControlPoint(value.p4, this.p4, this.p5, this.p6, ssNext2); this.setDirection(value.d12, this.d01, this.d12, this.d23); this.setDirection(value.d23, this.d12, this.d23, this.d34); if (!residueIt.hasNext) { this.state = 0 /* AtomicPolymerTraceIteratorState.nextPolymer */; } } this.hasNext = residueIt.hasNext || polymerIt.hasNext; return this.value; } constructor(unit, structure, options = {}) { var _a; this.unit = unit; this.state = 0 /* AtomicPolymerTraceIteratorState.nextPolymer */; this.p0 = (0, linear_algebra_1.Vec3)(); this.p1 = (0, linear_algebra_1.Vec3)(); this.p2 = (0, linear_algebra_1.Vec3)(); this.p3 = (0, linear_algebra_1.Vec3)(); this.p4 = (0, linear_algebra_1.Vec3)(); this.p5 = (0, linear_algebra_1.Vec3)(); this.p6 = (0, linear_algebra_1.Vec3)(); this.d01 = (0, linear_algebra_1.Vec3)(); this.d12 = (0, linear_algebra_1.Vec3)(); this.d23 = (0, linear_algebra_1.Vec3)(); this.d34 = (0, linear_algebra_1.Vec3)(); this.hasNext = false; this.atomicConformation = unit.model.atomicConformation; this.residueAtomSegments = unit.model.atomicHierarchy.residueAtomSegments; this.polymerRanges = unit.model.atomicRanges.polymerRanges; this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex; // can assume it won't be -1 for polymer residues this.directionFromElementIndex = unit.model.atomicHierarchy.derived.residue.directionFromElementIndex; this.directionToElementIndex = unit.model.atomicHierarchy.derived.residue.directionToElementIndex; this.moleculeType = unit.model.atomicHierarchy.derived.residue.moleculeType; this.cyclicPolymerMap = unit.model.atomicRanges.cyclicPolymerMap; this.polymerIt = sorted_ranges_1.SortedRanges.transientSegments(this.polymerRanges, unit.elements); this.residueIt = int_1.Segmentation.transientSegments(this.residueAtomSegments, unit.elements); this.value = createPolymerTraceElement(structure, unit); this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext; if (!options.ignoreSecondaryStructure) { this.secondaryStructure = (_a = secondary_structure_1.SecondaryStructureProvider.get(structure).value) === null || _a === void 0 ? void 0 : _a.get(unit.invariantId); } if (options.useHelixOrientation) { const helixOrientation = helix_orientation_1.HelixOrientationProvider.get(unit.model).value; if (!helixOrientation) throw new Error('missing helix-orientation'); this.helixOrientationCenters = helixOrientation.centers; } } } exports.AtomicPolymerTraceIterator = AtomicPolymerTraceIterator; class CoarsePolymerTraceIterator { getElementIndex(elementIndex) { return Math.min(Math.max(this.polymerSegment.start, elementIndex), this.polymerSegment.end - 1); } pos(target, elementIndex) { const index = this.unit.elements[elementIndex]; target[0] = this.conformation.x[index]; target[1] = this.conformation.y[index]; target[2] = this.conformation.z[index]; } move() { if (this.state === 0 /* CoarsePolymerTraceIteratorState.nextPolymer */) { while (this.polymerIt.hasNext) { this.polymerSegment = this.polymerIt.move(); this.elementIndex = this.polymerSegment.start; if (this.elementIndex < this.polymerSegment.end) { this.state = 1 /* CoarsePolymerTraceIteratorState.nextElement */; break; } } } if (this.state === 1 /* CoarsePolymerTraceIteratorState.nextElement */) { const elementIndexPrev2 = this.getElementIndex(this.elementIndex - 2); const elementIndexPrev1 = this.getElementIndex(this.elementIndex - 1); const elementIndexNext1 = this.getElementIndex(this.elementIndex + 1); const elementIndexNext2 = this.getElementIndex(this.elementIndex + 2); this.value.centerPrev.element = this.value.center.unit.elements[elementIndexPrev1]; this.value.center.element = this.value.center.unit.elements[this.elementIndex]; this.value.centerNext.element = this.value.center.unit.elements[elementIndexNext1]; this.pos(this.value.p0, elementIndexPrev2); this.pos(this.value.p1, elementIndexPrev1); this.pos(this.value.p2, this.elementIndex); this.pos(this.value.p3, elementIndexNext1); this.pos(this.value.p4, elementIndexNext2); // extend termini const f = 0.5; if (this.elementIndex === elementIndexPrev1) { linear_algebra_1.Vec3.scale(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.value.p2, this.value.p3), f); linear_algebra_1.Vec3.add(this.value.p1, this.value.p2, tmpDir); linear_algebra_1.Vec3.add(this.value.p0, this.value.p1, tmpDir); } else if (elementIndexPrev1 === elementIndexPrev2) { linear_algebra_1.Vec3.scale(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.value.p1, this.value.p2), f); linear_algebra_1.Vec3.add(this.value.p0, this.value.p1, tmpDir); } if (this.elementIndex === elementIndexNext1) { linear_algebra_1.Vec3.scale(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.value.p2, this.value.p1), f); linear_algebra_1.Vec3.add(this.value.p3, this.value.p2, tmpDir); linear_algebra_1.Vec3.add(this.value.p4, this.value.p3, tmpDir); } else if (elementIndexNext1 === elementIndexNext2) { linear_algebra_1.Vec3.scale(tmpDir, linear_algebra_1.Vec3.sub(tmpDir, this.value.p3, this.value.p2), f); linear_algebra_1.Vec3.add(this.value.p4, this.value.p3, tmpDir); } this.value.first = this.elementIndex === this.polymerSegment.start; this.value.last = this.elementIndex === this.polymerSegment.end - 1; if (this.elementIndex + 1 >= this.polymerSegment.end) { this.state = 0 /* CoarsePolymerTraceIteratorState.nextPolymer */; } } this.hasNext = this.elementIndex + 1 < this.polymerSegment.end || this.polymerIt.hasNext; this.elementIndex += 1; return this.value; } constructor(unit, structure) { this.unit = unit; this.state = 0 /* CoarsePolymerTraceIteratorState.nextPolymer */; this.hasNext = false; this.polymerIt = sorted_ranges_1.SortedRanges.transientSegments((0, polymer_1.getPolymerRanges)(unit), unit.elements); this.value = createPolymerTraceElement(structure, unit); linear_algebra_1.Vec3.set(this.value.d12, 1, 0, 0); linear_algebra_1.Vec3.set(this.value.d23, 1, 0, 0); switch (unit.kind) { case 1 /* Unit.Kind.Spheres */: this.conformation = unit.model.coarseConformation.spheres; break; case 2 /* Unit.Kind.Gaussians */: this.conformation = unit.model.coarseConformation.gaussians; break; } this.hasNext = this.polymerIt.hasNext; } } exports.CoarsePolymerTraceIterator = CoarsePolymerTraceIterator;