diff --git a/src/mol-model-formats/structure/mmcif/secondary-structure.ts b/src/mol-model-formats/structure/mmcif/secondary-structure.ts index bb3f4fabb11dfd8059e404c3424b876b9d861bf6..ca06a9b65d98a6b060fee36fff52c5af6bc1a35d 100644 --- a/src/mol-model-formats/structure/mmcif/secondary-structure.ts +++ b/src/mol-model-formats/structure/mmcif/secondary-structure.ts @@ -19,14 +19,17 @@ export function getSecondaryStructure(data: mmCIF_Database, hierarchy: AtomicHie // must add Helices 1st because of 'key' value assignment. addSheets(data.struct_sheet_range, map, data.struct_conf._rowCount, elements); + const n = hierarchy.residues._rowCount + const getIndex = (rI: ResidueIndex) => rI + const secStruct: SecondaryStructureData = { - type: new Int32Array(hierarchy.residues._rowCount) as any, - key: new Int32Array(hierarchy.residues._rowCount) as any, + type: new Int32Array(n) as any, + key: new Int32Array(n) as any, elements }; if (map.size > 0) assignSecondaryStructureRanges(hierarchy, map, secStruct); - return SecondaryStructure(secStruct.type, secStruct.key, secStruct.elements); + return SecondaryStructure(secStruct.type, secStruct.key, secStruct.elements, getIndex); } type SecondaryStructureEntry = { diff --git a/src/mol-model-props/computed/secondary-structure.ts b/src/mol-model-props/computed/secondary-structure.ts index d45ff69526b169a47f8f2ca816b8d2ae6819b013..784be59d3bb50e1e18b2579893b795077af66f8d 100644 --- a/src/mol-model-props/computed/secondary-structure.ts +++ b/src/mol-model-props/computed/secondary-structure.ts @@ -59,7 +59,8 @@ export type SecondaryStructureComputationProps = PD.Values<SecondaryStructureCom async function computeSecondaryStructure(structure: Structure, params: Partial<SecondaryStructureComputationProps>): Promise<ComputedSecondaryStructure.Property> { const p = { ...PD.getDefaultValues(SecondaryStructureComputationParams), params } - // TODO take inter-unit hbonds into account + // TODO take inter-unit hbonds into account for bridge, ladder, sheet assignment + // TODO store unit-only secStruc as custom unit property??? // TODO use Zhang-Skolnik for CA alpha only parts or for coarse parts with per-residue elements const map = new Map<number, SecondaryStructure>() for (let i = 0, il = structure.unitSymmetryGroups.length; i < il; ++i) { diff --git a/src/mol-model-props/computed/secondary-structure/dssp.ts b/src/mol-model-props/computed/secondary-structure/dssp.ts index 0d9504586afd71321e3e0bc29d752c2aa156e686..7838724f516b5b1e989fa8e3a56929078016e7d6 100644 --- a/src/mol-model-props/computed/secondary-structure/dssp.ts +++ b/src/mol-model-props/computed/secondary-structure/dssp.ts @@ -20,6 +20,8 @@ import { calculateUnitDihedralAngles } from './dssp/dihedral-angles'; import { calcUnitProteinTraceLookup3D } from './dssp/trace-lookup'; import { Unit } from 'mol-model/structure'; import { getUnitProteinInfo } from './dssp/protein-info'; +import { ResidueIndex } from 'mol-model/structure/model'; +import { SortedArray } from 'mol-data/int'; /** * TODO bugs to fix: @@ -37,7 +39,6 @@ export type DSSPComputationParams = typeof DSSPComputationParams export type DSSPComputationProps = PD.Values<DSSPComputationParams> export async function computeUnitDSSP(unit: Unit.Atomic, params: DSSPComputationProps): Promise<SecondaryStructure> { - const proteinInfo = getUnitProteinInfo(unit) const { residueIndices } = proteinInfo const lookup3d = calcUnitProteinTraceLookup3D(unit, residueIndices) @@ -83,20 +84,20 @@ export async function computeUnitDSSP(unit: Unit.Atomic, params: DSSPComputation const type = new Uint32Array(residueCount) as unknown as SecondaryStructureType[] const keys: number[] = [] const elements: SecondaryStructure.Element[] = [] + const getIndex = (rI: ResidueIndex) => SortedArray.indexOf(residueIndices, rI) for (let i = 0, il = residueIndices.length; i < il; ++i) { const assign = assignment[i] - type[residueIndices[i]] = assign + type[i] = assign const flag = getResidueFlag(flags[i]) + // console.log(i, SortedArray.indexOf(residueIndices, i), getFlagName(flags[i])) // TODO is this expected behavior? elements will be strictly split depending on 'winning' flag if (elements.length === 0 /* would fail at very start */ || flag !== (elements[elements.length - 1] as SecondaryStructure.Helix | SecondaryStructure.Sheet | SecondaryStructure.Turn).flags /* flag changed */) { elements[elements.length] = createElement(mapToKind(assign), flags[i], getResidueFlag) } keys[i] = elements.length - 1 } - // TODO expose model to unit residueIndex mapping - - return SecondaryStructure(type, keys, elements) + return SecondaryStructure(type, keys, elements, getIndex) } function createElement(kind: string, flag: DSSPType.Flag, getResidueFlag: (f: DSSPType) => SecondaryStructureType): SecondaryStructure.Element { diff --git a/src/mol-model-props/computed/secondary-structure/dssp/trace-lookup.ts b/src/mol-model-props/computed/secondary-structure/dssp/trace-lookup.ts index bfda77c3636db80ba6470b34680fdeb67a2480a5..e889ddf8c0ccdd0a5bec9e3d4f750b7bce5c3d3f 100644 --- a/src/mol-model-props/computed/secondary-structure/dssp/trace-lookup.ts +++ b/src/mol-model-props/computed/secondary-structure/dssp/trace-lookup.ts @@ -14,7 +14,7 @@ export function calcUnitProteinTraceLookup3D(unit: Unit.Atomic, unitProteinResid const { traceElementIndex } = unit.model.atomicHierarchy.derived.residue const indices = new Uint32Array(unitProteinResidues.length) for (let i = 0, il = unitProteinResidues.length; i < il; ++i) { - indices[indices.length] = traceElementIndex[unitProteinResidues[i]] + indices[i] = traceElementIndex[unitProteinResidues[i]] } return GridLookup3D({ x, y, z, indices: SortedArray.ofSortedArray(indices) }); } \ No newline at end of file diff --git a/src/mol-model/structure/model/properties/seconday-structure.ts b/src/mol-model/structure/model/properties/seconday-structure.ts index 64fcb890f2b7087001dc98dee3a86138b06c9737..82a44214d6ded72fed3d4f11d776994d9c1523ea 100644 --- a/src/mol-model/structure/model/properties/seconday-structure.ts +++ b/src/mol-model/structure/model/properties/seconday-structure.ts @@ -5,6 +5,7 @@ */ import { SecondaryStructureType } from '../types'; +import { ResidueIndex } from '../indexing'; /** Secondary structure "indexed" by residues. */ interface SecondaryStructure { @@ -13,10 +14,12 @@ interface SecondaryStructure { readonly key: ArrayLike<number>, /** indexed by key */ readonly elements: ReadonlyArray<SecondaryStructure.Element> + /** mapping from residue index */ + readonly getIndex: (rI: ResidueIndex) => number, } -function SecondaryStructure(type: SecondaryStructure['type'], key: SecondaryStructure['key'], elements: SecondaryStructure['elements']) { - return { type, key, elements } +function SecondaryStructure(type: SecondaryStructure['type'], key: SecondaryStructure['key'], elements: SecondaryStructure['elements'], getIndex: SecondaryStructure['getIndex']) { + return { type, key, elements, getIndex } } namespace SecondaryStructure { diff --git a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts b/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts index e0ae05d72aac4c9a3a2a9e222ab6cdc86f21c6da..68b46ca5e576fc9b5205d9b36fd109f9c11ca41a 100644 --- a/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts +++ b/src/mol-repr/structure/visual/util/polymer/trace-iterator.ts @@ -14,6 +14,7 @@ import { CoarseSphereConformation, CoarseGaussianConformation } from 'mol-model/ import { getPolymerRanges } from '../polymer'; import { AtomicConformation } from 'mol-model/structure/model/properties/atomic'; import { ComputedSecondaryStructure } from 'mol-model-props/computed/secondary-structure'; +import { SecondaryStructure } from 'mol-model/structure/model/properties/seconday-structure'; /** * Iterates over individual residues/coarse elements in polymers of a unit while @@ -69,7 +70,8 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> private residueIt: Segmentation.SegmentIterator<ResidueIndex> private polymerSegment: Segmentation.Segment<ResidueIndex> private cyclicPolymerMap: Map<ResidueIndex, ResidueIndex> - private secondaryStructureType: ArrayLike<SecondaryStructureType> + private secondaryStructureType: SecondaryStructure['type'] + private secondaryStructureGetIndex: SecondaryStructure['getIndex'] private residueSegmentMin: ResidueIndex private residueSegmentMax: ResidueIndex private prevSecStrucType: SecondaryStructureType @@ -133,8 +135,12 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> return residueIndex as ResidueIndex } + private getSecStruc(residueIndex: number) { + return this.secondaryStructureType[this.secondaryStructureGetIndex(residueIndex as ResidueIndex)] + } + private setControlPoint(out: Vec3, p1: Vec3, p2: Vec3, p3: Vec3, residueIndex: ResidueIndex) { - const ss = this.secondaryStructureType[residueIndex] + const ss = this.getSecStruc(residueIndex) if (SecondaryStructureType.is(ss, SecondaryStructureType.Flag.Beta)) { Vec3.scale(out, Vec3.add(out, p1, Vec3.add(out, p3, Vec3.add(out, p2, p2))), 1/4) } else { @@ -153,7 +159,7 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> if (residueIt.hasNext) { this.state = AtomicPolymerTraceIteratorState.nextResidue this.currSecStrucType = SecStrucTypeNA - this.nextSecStrucType = this.secondaryStructureType[this.residueSegmentMin] + this.nextSecStrucType = this.getSecStruc(this.residueSegmentMin) this.currCoarseBackbone = false this.nextCoarseBackbone = this.directionElementIndex[this.residueSegmentMin] === -1 break @@ -165,7 +171,7 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> const { index: residueIndex } = residueIt.move(); this.prevSecStrucType = this.currSecStrucType this.currSecStrucType = this.nextSecStrucType - this.nextSecStrucType = residueIt.hasNext ? this.secondaryStructureType[residueIndex + 1] : SecStrucTypeNA + this.nextSecStrucType = residueIt.hasNext ? this.getSecStruc(residueIndex + 1) : SecStrucTypeNA this.prevCoarseBackbone = this.currCoarseBackbone this.currCoarseBackbone = this.nextCoarseBackbone @@ -250,10 +256,14 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement> this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext this.secondaryStructureType = unit.model.properties.secondaryStructure.type + this.secondaryStructureGetIndex = unit.model.properties.secondaryStructure.getIndex const computedSecondaryStructure = ComputedSecondaryStructure.get(structure) if (computedSecondaryStructure) { const secondaryStructure = computedSecondaryStructure.map.get(unit.invariantId) - if (secondaryStructure) this.secondaryStructureType = secondaryStructure.type + if (secondaryStructure) { + this.secondaryStructureType = secondaryStructure.type + this.secondaryStructureGetIndex = secondaryStructure.getIndex + } } } } diff --git a/src/mol-theme/color/secondary-structure.ts b/src/mol-theme/color/secondary-structure.ts index 50d77eab4d88d9c543308770843be5b7daaa0a2b..9f1034f342614a6445a617adcbfd8e285ea07ca1 100644 --- a/src/mol-theme/color/secondary-structure.ts +++ b/src/mol-theme/color/secondary-structure.ts @@ -47,7 +47,7 @@ export function secondaryStructureColor(unit: Unit, element: ElementIndex, compu secStrucType = unit.model.properties.secondaryStructure.type[unit.residueIndex[element]] if (computedSecondaryStructure) { const secondaryStructure = computedSecondaryStructure.map.get(unit.invariantId) - if (secondaryStructure) secStrucType = secondaryStructure.type[unit.residueIndex[element]] + if (secondaryStructure) secStrucType = secondaryStructure.type[secondaryStructure.getIndex(unit.residueIndex[element])] } }