diff --git a/CHANGELOG.md b/CHANGELOG.md index 1801012459b3b81255433dfeba1f3e6385e5fabd..c47d5cdfbe45a567a952746590a676ee0e97103f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Note that since we don't clearly distinguish between a public and private interf - Add custom labels to Confal pyramids - Improve naming of some internal types in Confal pyramids extension coordinate - Add example mmCIF file with categories necessary to display Confal pyramids +- Change the lookup logic of NtC steps from residues ## [v3.13.0] - 2022-07-24 diff --git a/src/extensions/dnatco/confal-pyramids/property.ts b/src/extensions/dnatco/confal-pyramids/property.ts index dc141990ba2db5304821a795ac5ceaa494df05f8..5ca7bada8ce41ae7c0ceb7c51291a891ce06ab8d 100644 --- a/src/extensions/dnatco/confal-pyramids/property.ts +++ b/src/extensions/dnatco/confal-pyramids/property.ts @@ -16,7 +16,7 @@ import { PropertyWrapper } from '../../../mol-model-props/common/wrapper'; import { ParamDefinition as PD } from '../../../mol-util/param-definition'; import { MmcifFormat } from '../../../mol-model-formats/structure/mmcif'; -export type ConfalPyramids = PropertyWrapper<CPT.StepsData | undefined>; +export type ConfalPyramids = PropertyWrapper<CPT.Steps | undefined>; export namespace ConfalPyramids { export const Schema = { @@ -105,13 +105,13 @@ export const ConfalPyramidsProvider: CustomModelProperty.Provider<ConfalPyramids type StepsSummaryTable = Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step_summary>; -function createPyramidsFromCif(model: Model, +function createPyramidsFromCif( + model: Model, cifSteps: Table<typeof ConfalPyramids.Schema.ndb_struct_ntc_step>, - stepsSummary: StepsSummaryTable): CPT.StepsData { + stepsSummary: StepsSummaryTable +): CPT.Steps { const steps = new Array<CPT.Step>(); - const names = new Map<string, number>(); - const halfPyramids = new Array<CPT.HalfPyramid>(); - let hasMultipleModels = false; + const mapping = new Array<CPT.MappedChains>(); const { id, PDB_model_number, name, @@ -123,21 +123,24 @@ function createPyramidsFromCif(model: Model, if (_rowCount !== stepsSummary._rowCount) throw new Error('Inconsistent mmCIF data'); for (let i = 0; i < _rowCount; i++) { - const model_num = PDB_model_number.value(i); - if (model_num !== model.modelNum) - hasMultipleModels = true; - const { NtC, confal_score, rmsd } = getSummaryData(id.value(i), i, stepsSummary); + const modelNum = PDB_model_number.value(i); + const chainId = auth_asym_id_1.value(i); + const seqId = auth_seq_id_1.value(i); + const modelIdx = modelNum - 1; + + if (mapping.length <= modelIdx || !mapping[modelIdx]) + mapping[modelIdx] = new Map<string, CPT.MappedResidues>(); const step = { - PDB_model_number: model_num, + PDB_model_number: modelNum, name: name.value(i), - auth_asym_id_1: auth_asym_id_1.value(i), - auth_seq_id_1: auth_seq_id_1.value(i), + auth_asym_id_1: chainId, + auth_seq_id_1: seqId, label_comp_id_1: label_comp_id_1.value(i), label_alt_id_1: label_alt_id_1.value(i), PDB_ins_code_1: PDB_ins_code_1.value(i), @@ -152,13 +155,18 @@ function createPyramidsFromCif(model: Model, }; steps.push(step); - names.set(step.name, steps.length - 1); - halfPyramids.push({ step, isLower: false }); - halfPyramids.push({ step, isLower: true }); + const mappedChains = mapping[modelIdx]; + const residuesOnChain = mappedChains.get(chainId) ?? new Map<number, number[]>(); + const stepsForResidue = residuesOnChain.get(seqId) ?? []; + stepsForResidue.push(steps.length - 1); + + residuesOnChain.set(seqId, stepsForResidue); + mappedChains.set(chainId, residuesOnChain); + mapping[modelIdx] = mappedChains; } - return { steps, names, halfPyramids, hasMultipleModels }; + return { steps, mapping }; } function getSummaryData(id: number, i: number, stepsSummary: StepsSummaryTable) { diff --git a/src/extensions/dnatco/confal-pyramids/representation.ts b/src/extensions/dnatco/confal-pyramids/representation.ts index d8194e8b4b37b8fbbb99817feed798cb31053ca3..1c7d4f95b1e05aaa06298becd8a4d7f89ecba911 100644 --- a/src/extensions/dnatco/confal-pyramids/representation.ts +++ b/src/extensions/dnatco/confal-pyramids/representation.ts @@ -6,7 +6,7 @@ */ import { ConfalPyramids, ConfalPyramidsProvider } from './property'; -import { ConfalPyramidsUtil } from './util'; +import { ConfalPyramidsIterator } from './util'; import { ConfalPyramidsTypes as CPT } from './types'; import { Interval } from '../../../mol-data/int'; import { Mesh } from '../../../mol-geo/geometry/mesh/mesh'; @@ -32,6 +32,12 @@ const t = Mat4.identity(); const w = Vec3.zero(); const mp = Vec3.zero(); +const posO3 = Vec3(); +const posP = Vec3(); +const posOP1 = Vec3(); +const posOP2 = Vec3(); +const posO5 = Vec3(); + function calcMidpoint(mp: Vec3, v: Vec3, w: Vec3) { Vec3.sub(mp, v, w); Vec3.scale(mp, mp, 0.5); @@ -53,64 +59,76 @@ function createConfalPyramidsIterator(structureGroup: StructureGroup): LocationI const { structure, group } = structureGroup; const instanceCount = group.units.length; - const prop = ConfalPyramidsProvider.get(structure.model).value; - if (prop === undefined || prop.data === undefined) { - return LocationIterator(0, 1, 1, () => NullLocation); - } + const data = ConfalPyramidsProvider.get(structure.model)?.value?.data; + if (!data) return LocationIterator(0, 1, 1, () => NullLocation); - const { halfPyramids } = prop.data; + const halfPyramidsCount = data.steps.length * 2; const getLocation = (groupIndex: number, instanceIndex: number) => { - if (halfPyramids.length <= groupIndex) return NullLocation; - return CPT.Location(halfPyramids[groupIndex]); + if (halfPyramidsCount <= groupIndex) return NullLocation; + const idx = Math.floor(groupIndex / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation + return CPT.Location(data.steps[idx], groupIndex % 2 === 1); }; - return LocationIterator(halfPyramids.length, instanceCount, 1, getLocation); + return LocationIterator(halfPyramidsCount, instanceCount, 1, getLocation); } function createConfalPyramidsMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<ConfalPyramidsMeshParams>, mesh?: Mesh) { if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh); - const prop = ConfalPyramidsProvider.get(structure.model).value; - if (prop === undefined || prop.data === undefined) return Mesh.createEmpty(mesh); + const data = ConfalPyramidsProvider.get(structure.model)?.value?.data; + if (!data) return Mesh.createEmpty(mesh); - const { steps } = prop.data; + const { steps, mapping } = data; if (steps.length === 0) return Mesh.createEmpty(mesh); - - const mb = MeshBuilder.createState(512, 512, mesh); - - const handler = (step: CPT.Step, first: ConfalPyramidsUtil.FirstResidueAtoms, second: ConfalPyramidsUtil.SecondResidueAtoms, firsLocIndex: number, secondLocIndex: number) => { - if (firsLocIndex === -1 || secondLocIndex === -1) - throw new Error('Invalid location index'); - - const scale = (step.confal_score - 20.0) / 100.0; - const O3 = first.O3.pos; - const OP1 = second.OP1.pos; const OP2 = second.OP2.pos; const O5 = second.O5.pos; const P = second.P.pos; - - shiftVertex(O3, P, scale); - shiftVertex(OP1, P, scale); - shiftVertex(OP2, P, scale); - shiftVertex(O5, P, scale); - calcMidpoint(mp, O3, O5); - - mb.currentGroup = firsLocIndex; - let pb = PrimitiveBuilder(3); - /* Upper part (for first residue in step) */ - pb.add(O3, OP1, OP2); - pb.add(O3, mp, OP1); - pb.add(O3, OP2, mp); - MeshBuilder.addPrimitive(mb, t, pb.getPrimitive()); - - /* Lower part (for second residue in step */ - mb.currentGroup = secondLocIndex; - pb = PrimitiveBuilder(3); - pb.add(mp, O5, OP1); - pb.add(mp, OP2, O5); - pb.add(O5, OP2, OP1); - MeshBuilder.addPrimitive(mb, t, pb.getPrimitive()); - }; - - const walker = new ConfalPyramidsUtil.UnitWalker(structure, unit, handler); - walker.walk(); + const vertexCount = (6 * steps.length) / mapping.length; + + const mb = MeshBuilder.createState(vertexCount, vertexCount / 10, mesh); + + const it = new ConfalPyramidsIterator(structure, unit); + while (it.hasNext) { + const allPoints = it.move(); + + for (const points of allPoints) { + const { O3, P, OP1, OP2, O5, confalScore } = points; + const scale = (confalScore - 20.0) / 100.0; + // Steps can be drawn in a different order than they are stored. + // To make sure that we can get from the drawn pyramid back to the step in represents, + // we need to use an appropriate groupId. The stepIdx passed from the iterator + // is an index into the array of all steps in the structure. + // Since a step is drawn as two "half-pyramids" we need two ids to map to a single step. + // To do that, we just multiply the index by 2. idx*2 marks the "upper" half-pyramid, + // (idx*2)+1 the "lower" half-pyramid. + const groupIdx = points.stepIdx * 2; + + unit.conformation.invariantPosition(O3, posO3); + unit.conformation.invariantPosition(P, posP); + unit.conformation.invariantPosition(OP1, posOP1); + unit.conformation.invariantPosition(OP2, posOP2); + unit.conformation.invariantPosition(O5, posO5); + + shiftVertex(posO3, posP, scale); + shiftVertex(posOP1, posP, scale); + shiftVertex(posOP2, posP, scale); + shiftVertex(posO5, posP, scale); + calcMidpoint(mp, posO3, posO5); + + mb.currentGroup = groupIdx; + let pb = PrimitiveBuilder(3); + /* Upper part (for first residue in step) */ + pb.add(posO3, posOP1, posOP2); + pb.add(posO3, mp, posOP1); + pb.add(posO3, posOP2, mp); + MeshBuilder.addPrimitive(mb, t, pb.getPrimitive()); + + /* Lower part (for second residue in step) */ + mb.currentGroup = groupIdx + 1; + pb = PrimitiveBuilder(3); + pb.add(mp, posO5, posOP1); + pb.add(mp, posOP2, posO5); + pb.add(posO5, posOP2, posOP1); + MeshBuilder.addPrimitive(mb, t, pb.getPrimitive()); + } + } return MeshBuilder.getMesh(mb); } @@ -124,15 +142,17 @@ function getConfalPyramidLoci(pickingId: PickingId, structureGroup: StructureGro const unit = structureGroup.group.units[instanceId]; if (!Unit.isAtomic(unit)) return EmptyLoci; - const prop = ConfalPyramidsProvider.get(structure.model).value; - if (prop === undefined || prop.data === undefined) return EmptyLoci; + const data = ConfalPyramidsProvider.get(structure.model)?.value?.data; + if (!data) return EmptyLoci; + + const halfPyramidsCount = data.steps.length * 2; - const { halfPyramids } = prop.data; + if (halfPyramidsCount <= groupId) return EmptyLoci; - if (halfPyramids.length <= groupId) return EmptyLoci; - const hp = halfPyramids[groupId]; + const idx = Math.floor(groupId / 2); // Map groupIndex to a step, see createConfalPyramidsMesh() for full explanation + const step = data.steps[idx]; - return CPT.Loci(hp, [{}]); + return CPT.Loci({ step, isLower: groupId % 2 === 1 }, [{}]); } function eachConfalPyramid(loci: Loci, structureGroup: StructureGroup, apply: (interval: Interval) => boolean) { diff --git a/src/extensions/dnatco/confal-pyramids/types.ts b/src/extensions/dnatco/confal-pyramids/types.ts index 0e59626927f632392491125ad3f6452bf93aa809..f2b898822333d901e9d99789e1a93fd1ef4f4cf2 100644 --- a/src/extensions/dnatco/confal-pyramids/types.ts +++ b/src/extensions/dnatco/confal-pyramids/types.ts @@ -30,11 +30,12 @@ export namespace ConfalPyramidsTypes { rmsd: number, } - export interface StepsData { + export type MappedChains = Map<string, MappedResidues>; + export type MappedResidues = Map<number, number[]>; + + export interface Steps { steps: Array<Step>, - names: Map<string, number>, - halfPyramids: Array<HalfPyramid>, - hasMultipleModels: boolean + mapping: MappedChains[], } export interface HalfPyramid { @@ -44,8 +45,8 @@ export namespace ConfalPyramidsTypes { export interface Location extends DataLocation<HalfPyramid, {}> {} - export function Location(halfPyramid: HalfPyramid) { - return DataLocation(DataTag, halfPyramid, {}); + export function Location(step: Step, isLower: boolean) { + return DataLocation(DataTag, { step, isLower }, {}); } export function isLocation(x: any): x is Location { diff --git a/src/extensions/dnatco/confal-pyramids/util.ts b/src/extensions/dnatco/confal-pyramids/util.ts index 698a34e6d83fe295da1ce6454d788905be90e864..af4e9793f259d202a52ccb0b896c7156bea87ae4 100644 --- a/src/extensions/dnatco/confal-pyramids/util.ts +++ b/src/extensions/dnatco/confal-pyramids/util.ts @@ -8,280 +8,120 @@ import { ConfalPyramidsProvider } from './property'; import { ConfalPyramidsTypes as CPT } from './types'; import { Segmentation } from '../../../mol-data/int'; -import { Vec3 } from '../../../mol-math/linear-algebra'; import { ChainIndex, ElementIndex, ResidueIndex, Structure, StructureElement, StructureProperties, Unit } from '../../../mol-model/structure'; -export namespace ConfalPyramidsUtil { - type Residue = Segmentation.Segment<ResidueIndex>; +type Residue = Segmentation.Segment<ResidueIndex>; - export type AtomInfo = { - pos: Vec3, - index: ElementIndex, - fakeAltId: string, - }; +export type Pyramid = { + O3: ElementIndex, + P: ElementIndex, + OP1: ElementIndex, + OP2: ElementIndex, + O5: ElementIndex, + confalScore: number, + stepIdx: number, +}; - export type FirstResidueAtoms = { - O3: AtomInfo, - }; +const EmptyStepIndices = new Array<number>(); - export type SecondResidueAtoms = { - OP1: AtomInfo, - OP2: AtomInfo, - O5: AtomInfo, - P: AtomInfo, - }; - - type ResidueInfo = { - PDB_model_num: number, - asym_id: string, - auth_asym_id: string, - seq_id: number, - auth_seq_id: number, - comp_id: string, - alt_id: string, - ins_code: string, - }; +function copyResidue(r?: Residue) { + return r ? { index: r.index, start: r.start, end: r.end } : void 0; +} - export type Handler = (pyramid: CPT.Step, first: FirstResidueAtoms, second: SecondResidueAtoms, firstLocIndex: number, secondLocIndex: number) => void; +function getAtomIndex(loc: StructureElement.Location, residue: Residue, names: string[], altId: string): ElementIndex { + for (let eI = residue.start; eI < residue.end; eI++) { + loc.element = loc.unit.elements[eI]; + const elName = StructureProperties.atom.label_atom_id(loc); + const elAltId = StructureProperties.atom.label_alt_id(loc); - function residueInfoFromLocation(loc: StructureElement.Location): ResidueInfo { - return { - PDB_model_num: StructureProperties.unit.model_num(loc), - asym_id: StructureProperties.chain.label_asym_id(loc), - auth_asym_id: StructureProperties.chain.auth_asym_id(loc), - seq_id: StructureProperties.residue.label_seq_id(loc), - auth_seq_id: StructureProperties.residue.auth_seq_id(loc), - comp_id: StructureProperties.atom.label_comp_id(loc), - alt_id: StructureProperties.atom.label_alt_id(loc), - ins_code: StructureProperties.residue.pdbx_PDB_ins_code(loc) - }; + if (names.includes(elName) && (elAltId === altId || elAltId.length === 0)) + return loc.element; } - export function hasMultipleModels(unit: Unit.Atomic): boolean { - const prop = ConfalPyramidsProvider.get(unit.model).value; - if (prop === undefined || prop.data === undefined) throw new Error('No custom properties data'); - return prop.data.hasMultipleModels; - } + return -1 as ElementIndex; +} - function getPossibleAltIds(residue: Residue, structure: Structure, unit: Unit.Atomic): string[] { - const possibleAltIds: string[] = []; +function getPyramid(loc: StructureElement.Location, one: Residue, two: Residue, altIdOne: string, altIdTwo: string, confalScore: number, stepIdx: number): Pyramid { + const O3 = getAtomIndex(loc, one, ['O3\'', 'O3*'], altIdOne); + const P = getAtomIndex(loc, two, ['P'], altIdTwo); + const OP1 = getAtomIndex(loc, two, ['OP1'], altIdTwo); + const OP2 = getAtomIndex(loc, two, ['OP2'], altIdTwo); + const O5 = getAtomIndex(loc, two, ['O5\'', 'O5*'], altIdTwo); - const loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex); - for (let rI = residue.start; rI <= residue.end - 1; rI++) { - loc.element = unit.elements[rI]; - const altId = StructureProperties.atom.label_alt_id(loc); - if (altId !== '' && !possibleAltIds.includes(altId)) possibleAltIds.push(altId); - } + return { O3, P, OP1, OP2, O5, confalScore, stepIdx }; +} - return possibleAltIds; +export class ConfalPyramidsIterator { + private chainIt: Segmentation.SegmentIterator<ChainIndex>; + private residueIt: Segmentation.SegmentIterator<ResidueIndex>; + private residueOne?: Residue; + private residueTwo: Residue; + private data?: CPT.Steps; + private loc: StructureElement.Location; + + private getStepIndices(r: Residue) { + this.loc.element = this.loc.unit.elements[r.start]; + + const modelIdx = StructureProperties.unit.model_num(this.loc) - 1; + const chainId = StructureProperties.chain.auth_asym_id(this.loc); + const seqId = StructureProperties.residue.auth_seq_id(this.loc); + + const chains = this.data!.mapping[modelIdx]; + if (!chains) return EmptyStepIndices; + const residues = chains.get(chainId); + if (!residues) return EmptyStepIndices; + return residues.get(seqId) ?? EmptyStepIndices; } - class Utility { - protected getPyramidByName(name: string): { pyramid: CPT.Step | undefined, index: number } { - const index = this.data.names.get(name); - if (index === undefined) return { pyramid: undefined, index: -1 }; - - return { pyramid: this.data.steps[index], index }; - } - - protected stepToName(entry_id: string, modelNum: number, locFirst: StructureElement.Location, locSecond: StructureElement.Location, fakeAltId_1: string, fakeAltId_2: string) { - const first = residueInfoFromLocation(locFirst); - const second = residueInfoFromLocation(locSecond); - const model_id = this.hasMultipleModels ? `-m${modelNum}` : ''; - const alt_id_1 = fakeAltId_1 !== '' ? `.${fakeAltId_1}` : (first.alt_id.length ? `.${first.alt_id}` : ''); - const alt_id_2 = fakeAltId_2 !== '' ? `.${fakeAltId_2}` : (second.alt_id.length ? `.${second.alt_id}` : ''); - const ins_code_1 = first.ins_code.length ? `.${first.ins_code}` : ''; - const ins_code_2 = second.ins_code.length ? `.${second.ins_code}` : ''; - - return `${entry_id}${model_id}_${first.auth_asym_id}_${first.comp_id}${alt_id_1}_${first.auth_seq_id}${ins_code_1}_${second.comp_id}${alt_id_2}_${second.auth_seq_id}${ins_code_2}`; - } - - constructor(unit: Unit.Atomic) { - const prop = ConfalPyramidsProvider.get(unit.model).value; - if (prop === undefined || prop.data === undefined) throw new Error('No custom properties data'); - - this.data = prop.data; - this.hasMultipleModels = hasMultipleModels(unit); + private moveStep() { + this.residueOne = copyResidue(this.residueTwo); + this.residueTwo = copyResidue(this.residueIt.move())!; - this.entryId = unit.model.entryId.toLowerCase(); - this.modelNum = unit.model.modelNum; - } - - protected readonly data: CPT.StepsData; - protected readonly hasMultipleModels: boolean; - protected readonly entryId: string; - protected readonly modelNum: number; + return this.toPyramids(this.residueOne!, this.residueTwo); } - export class UnitWalker extends Utility { - private getAtomIndices(names: string[], residue: Residue): ElementIndex[] { - const indices: ElementIndex[] = []; - - const loc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex); - for (let rI = residue.start; rI <= residue.end - 1; rI++) { - loc.element = this.unit.elements[rI]; - const thisName = StructureProperties.atom.label_atom_id(loc); - if (names.includes(thisName)) indices.push(loc.element); - } - - if (indices.length === 0) { - let namesStr = ''; - for (const n of names) - namesStr += `${n} `; + private toPyramids(one: Residue, two: Residue) { + const indices = this.getStepIndices(one); - throw new Error(`Element [${namesStr}] not found on residue ${residue.index}`); - } - - return indices; + const points = []; + for (const idx of indices) { + const step = this.data!.steps[idx]; + points.push(getPyramid(this.loc, one, two, step.label_alt_id_1, step.label_alt_id_2, step.confal_score, idx)); } - private getAtomPositions(indices: ElementIndex[]): Vec3[] { - const pos = this.unit.conformation.invariantPosition; - const positions: Vec3[] = []; - - for (const eI of indices) { - const v = Vec3.zero(); - pos(eI, v); - positions.push(v); - } - - return positions; - } - - private handleStep(firstAtoms: FirstResidueAtoms[], secondAtoms: SecondResidueAtoms[]) { - const modelNum = this.hasMultipleModels ? this.modelNum : -1; - let ok = false; - - const firstLoc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex); - const secondLoc = StructureElement.Location.create(this.structure, this.unit, -1 as ElementIndex); - for (let i = 0; i < firstAtoms.length; i++) { - const first = firstAtoms[i]; - for (let j = 0; j < secondAtoms.length; j++) { - const second = secondAtoms[j]; - firstLoc.element = first.O3.index; - secondLoc.element = second.OP1.index; - - const name = this.stepToName(this.entryId, modelNum, firstLoc, secondLoc, first.O3.fakeAltId, second.OP1.fakeAltId); - const { pyramid, index } = this.getPyramidByName(name); - if (pyramid !== undefined) { - const locIndex = index * 2; - this.handler(pyramid, first, second, locIndex, locIndex + 1); - ok = true; - } - } - } - - if (!ok) throw new Error('Bogus step'); - } - - private processFirstResidue(residue: Residue, possibleAltIds: string[]) { - const indO3 = this.getAtomIndices(['O3\'', 'O3*'], residue); - const posO3 = this.getAtomPositions(indO3); - - const altPos: FirstResidueAtoms[] = [ - { O3: { pos: posO3[0], index: indO3[0], fakeAltId: '' } } - ]; - - for (let i = 1; i < indO3.length; i++) { - altPos.push({ O3: { pos: posO3[i], index: indO3[i], fakeAltId: '' } }); - } - - if (altPos.length === 1 && possibleAltIds.length > 1) { - /* We have some alternate positions on the residue but O3 does not have any - fake them */ - altPos[0].O3.fakeAltId = possibleAltIds[0]; - - for (let i = 1; i < possibleAltIds.length; i++) - altPos.push({ O3: { pos: posO3[0], index: indO3[0], fakeAltId: possibleAltIds[i] } }); - } - - return altPos; - } - - private processSecondResidue(residue: Residue, possibleAltIds: string[]) { - const indOP1 = this.getAtomIndices(['OP1'], residue); - const indOP2 = this.getAtomIndices(['OP2'], residue); - const indO5 = this.getAtomIndices(['O5\'', 'O5*'], residue); - const indP = this.getAtomIndices(['P'], residue); - - const posOP1 = this.getAtomPositions(indOP1); - const posOP2 = this.getAtomPositions(indOP2); - const posO5 = this.getAtomPositions(indO5); - const posP = this.getAtomPositions(indP); - - const infoOP1: AtomInfo[] = []; - /* We use OP1 as "pivotal" atom. There is no specific reason - * to pick OP1, it is as good a choice as any other atom - */ - if (indOP1.length === 1 && possibleAltIds.length > 1) { - /* No altIds on OP1, fake them */ - for (const altId of possibleAltIds) - infoOP1.push({ pos: posOP1[0], index: indOP1[0], fakeAltId: altId }); - } else { - for (let i = 0; i < indOP1.length; i++) - infoOP1.push({ pos: posOP1[i], index: indOP1[i], fakeAltId: '' }); - } - - const mkInfo = (i: number, indices: ElementIndex[], positions: Vec3[], altId: string) => { - if (i >= indices.length) { - const last = indices.length - 1; - return { pos: positions[last], index: indices[last], fakeAltId: altId }; - } - - return { pos: positions[i], index: indices[i], fakeAltId: altId }; - }; - - const altPos: SecondResidueAtoms[] = []; - for (let i = 0; i < infoOP1.length; i++) { - const altId = infoOP1[i].fakeAltId; - - const OP2 = mkInfo(i, indOP2, posOP2, altId); - const O5 = mkInfo(i, indO5, posO5, altId); - const P = mkInfo(i, indP, posP, altId); - - altPos.push({ OP1: infoOP1[i], OP2, O5, P }); - } - - return altPos; - } - - private step(residue: Residue): { firstAtoms: FirstResidueAtoms[], secondAtoms: SecondResidueAtoms[] } { - const firstPossibleAltIds = getPossibleAltIds(residue, this.structure, this.unit); - const firstAtoms = this.processFirstResidue(residue, firstPossibleAltIds); + return points; + } - residue = this.residueIt.move(); + constructor(structure: Structure, unit: Unit) { + this.chainIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements); + this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements); - const secondPossibleAltIds = getPossibleAltIds(residue, this.structure, this.unit); - const secondAtoms = this.processSecondResidue(residue, secondPossibleAltIds); + const prop = ConfalPyramidsProvider.get(unit.model).value; + this.data = prop?.data; - return { firstAtoms, secondAtoms }; + if (this.chainIt.hasNext) { + this.residueIt.setSegment(this.chainIt.move()); + if (this.residueIt.hasNext) + this.residueTwo = this.residueIt.move(); } - walk() { - while (this.chainIt.hasNext) { - this.residueIt.setSegment(this.chainIt.move()); - - let residue = this.residueIt.move(); - while (this.residueIt.hasNext) { - try { - const { firstAtoms, secondAtoms } = this.step(residue); - - this.handleStep(firstAtoms, secondAtoms); - } catch (error) { - /* Skip and move along */ - residue = this.residueIt.move(); - } - } - } - } + this.loc = StructureElement.Location.create(structure, unit, -1 as ElementIndex); + } - constructor(private structure: Structure, private unit: Unit.Atomic, private handler: Handler) { - super(unit); + get hasNext() { + if (!this.data) + return false; + return this.residueIt.hasNext + ? true + : this.chainIt.hasNext; + } - this.chainIt = Segmentation.transientSegments(unit.model.atomicHierarchy.chainAtomSegments, unit.elements); - this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements); + move() { + if (this.residueIt.hasNext) { + return this.moveStep(); + } else { + this.residueIt.setSegment(this.chainIt.move()); + return this.moveStep(); } - - private chainIt: Segmentation.SegmentIterator<ChainIndex>; - private residueIt: Segmentation.SegmentIterator<ResidueIndex>; } }