diff --git a/src/apps/rednatco/index.tsx b/src/apps/rednatco/index.tsx index 9216dc3c1984cabfc8764e8be188f101b547690d..560639472edd520d7521e47ad5da2693e98a7d44 100644 --- a/src/apps/rednatco/index.tsx +++ b/src/apps/rednatco/index.tsx @@ -181,14 +181,14 @@ export class ReDNATCOMsp extends React.Component<ReDNATCOMsp.Props, State> { this.currentFilter = cmd.filter; ReDNATCOMspApi.event(Api.Events.FilterApplied()); } else if (cmd.type === 'select-step') { - const ret = await this.viewer.actionSelectStep(cmd.step, cmd.prev, cmd.next, this.state.display); - if (!ret) { + const success = await this.viewer.actionSelectStep(cmd.step, cmd.prev, cmd.next, this.state.display); + if (!success) { ReDNATCOMspApi.event(Api.Events.StepSelectedFail()); return; } - this.viewer.focusOnSelectedStep(); this.selectedStep = cmd.step; + this.viewer.focusOnSelectedStep(); ReDNATCOMspApi.event(Api.Events.StepSelectedOk(this.selectedStep.name)); } else if (cmd.type === 'switch-model') { diff --git a/src/apps/rednatco/reference-conformers.ts b/src/apps/rednatco/reference-conformers.ts index 9de0cdcb2dd2a36a4ea24bb924ecf0bdb607ce25..47eeb698076398194929b3752917ba0dbcde55f8 100644 --- a/src/apps/rednatco/reference-conformers.ts +++ b/src/apps/rednatco/reference-conformers.ts @@ -1,5 +1,7 @@ /* eslint-disable array-bracket-spacing, no-multi-spaces, indent */ +import {ObjectKeys} from "../../mol-util/type-helpers"; + export type Ring = 'purine'|'pyrimidine'|'PSU'; export const ReferenceCompounds: Record<string, [string, string]> = { @@ -432,18 +434,30 @@ export const BaseAtomsKinds: Record<string, Ring> = { 'PSU': 'PSU' }; +const _BaseAtomKindKeys = ObjectKeys(BaseAtomsKinds); +function isKnownResidue(compId: string): compId is keyof typeof BaseAtomsKinds { + return _BaseAtomKindKeys.includes(compId); +} + + export const BackboneAtoms = { first: ["C5'", "C4'", "O4'", "C3'", "O3'", "C1'"], /* eslint-disable @typescript-eslint/quotes */ second: ["P", "O5'", "C5'", "C4'", "O4'", "C3'", "O3'", "C1'"], }; -export const BaseAtoms: Record<keyof typeof BaseAtomsKinds, string[]> = { +export const BaseAtoms: Record<Ring, string[]> = { purine: ['N9', 'C4'], pyrimidine: ['N1', 'C2'], PSU: ['C5', 'C4'], }; -export function referenceAtoms(compId: keyof typeof BaseAtomsKinds, order: 'first'|'second') { - return [...BackboneAtoms[order], ...BaseAtoms[compId]]; +export function referenceAtoms(compId: string, order: keyof typeof BackboneAtoms) { + if (!isKnownResidue(compId)) + return []; + + return [ + ...BackboneAtoms[order], + ...BaseAtoms[BaseAtomsKinds[compId]] + ]; } diff --git a/src/apps/rednatco/viewer.ts b/src/apps/rednatco/viewer.ts index 32007e713e99a8af1c5811c31885bc986d1207ee..8540db32c74cd3f96b45ce793febe8b7f4cf4c5f 100644 --- a/src/apps/rednatco/viewer.ts +++ b/src/apps/rednatco/viewer.ts @@ -71,7 +71,7 @@ type StepInfo = { model: number; } -function dinucleotideBackbone(loci: StructureElement.Loci) { +function superpositionAtomsIndices(loci: StructureElement.Loci) { const es = loci.elements[0]; const loc = Location.create(loci.structure, es.unit, es.unit.elements[OrderedSet.getAt(es.indices, 0)]); const len = OrderedSet.size(es.indices); @@ -98,32 +98,38 @@ function dinucleotideBackbone(loci: StructureElement.Loci) { }; // Find split between first and second residue - const resNo1 = StructureProperties.residue.auth_seq_id(loc); + const resNo1 = StructureProperties.residue.label_seq_id(loc); let secondIdx = -1; for (let idx = 0; idx < len; idx++) { loc.element = es.unit.elements[OrderedSet.getAt(es.indices, idx)]; - const resNo = StructureProperties.residue.auth_seq_id(loc); + const resNo = StructureProperties.residue.label_seq_id(loc); if (resNo !== resNo1) { secondIdx = idx; break; } } - if (secondIdx === -1) + if (secondIdx === -1) { + console.log('No first/second residue split'); return []; + } // Gather element indices for the first residue loc.element = es.unit.elements[OrderedSet.getAt(es.indices, 0)]; const compId1 = StructureProperties.atom.label_comp_id(loc); const atoms1 = RefCfmr.referenceAtoms(compId1.toUpperCase(), 'first'); - if (!gather(atoms1, 0, secondIdx)) + if (!gather(atoms1, 0, secondIdx)) { + console.log('No ref atoms for first'); return []; + } // Gather element indices for the second residue loc.element = es.unit.elements[OrderedSet.getAt(es.indices, secondIdx)]; const compId2 = StructureProperties.atom.label_comp_id(loc); const atoms2 = RefCfmr.referenceAtoms(compId2.toUpperCase(), 'second'); - if (!gather(atoms2, secondIdx, len)) + if (!gather(atoms2, secondIdx, len)) { + console.log('No ref atoms for second'); return []; + } return indices; } @@ -270,13 +276,6 @@ export class ReDNATCOMspViewer { Vec3.scale(u, u, sphere.radius * 8); Vec3.add(v, u, v); - console.log( - 'Cam', - 'Center', sphere.center, - 'Radius', sphere.radius, - 'Position', v - ); - snapshot.target = sphere.center; snapshot.position = v; @@ -390,8 +389,8 @@ export class ReDNATCOMspViewer { } private superpose(reference: StructureElement.Loci, stru: StructureElement.Loci) { - const refElems = dinucleotideBackbone(reference); - const struElems = dinucleotideBackbone(stru); + const refElems = superpositionAtomsIndices(reference); + const struElems = superpositionAtomsIndices(stru); return Superpose.superposition( { elements: refElems, conformation: reference.elements[0].unit.conformation }, @@ -654,7 +653,6 @@ export class ReDNATCOMspViewer { } focusOnSelectedStep() { - // Focus camera on the selection const sel = this.plugin.state.data.cells.get(IDs.ID('superposition', '', NtCSupSel)); const prev = this.plugin.state.data.cells.get(IDs.ID('superposition', '', NtCSupPrev)); const next = this.plugin.state.data.cells.get(IDs.ID('superposition', '', NtCSupNext)); @@ -1004,10 +1002,10 @@ export class ReDNATCOMspViewer { this.resetCameraRadius(); } - async actionSelectStep(stepSel: Api.Payloads.StepSelection, prevSel: Api.Payloads.StepSelection|undefined, nextSel: Api.Payloads.StepSelection|undefined, display: Display): Promise<{ rmsd: number }|undefined> { + async actionSelectStep(stepSel: Api.Payloads.StepSelection, prevSel: Api.Payloads.StepSelection|undefined, nextSel: Api.Payloads.StepSelection|undefined, display: Display) { const step = this.stepFromName(stepSel.name); if (!step) - return; + return false; // Switch to a different model if the selected step is from a different model // This is the first thing we need to do @@ -1016,7 +1014,7 @@ export class ReDNATCOMspViewer { const entireStruCell = this.plugin.state.data.cells.get(IDs.ID('structure', 'nucleic', BaseRef)); if (!entireStruCell) - return void 0; + return false; const stru = entireStruCell.obj!.data!; const struLoci = StructureSelection.toLociWithSourceUnits(StructureSelection.Singletons(stru, stru)); @@ -1027,7 +1025,7 @@ export class ReDNATCOMspViewer { struLoci, 'auth' ); if (stepLoci.kind !== 'element-loci') - return; + return false; const prevLoci = prevSel ? this.toStepLoci(prevSel.name, struLoci) : EmptyLoci; const nextLoci = nextSel ? this.toStepLoci(nextSel.name, struLoci) : EmptyLoci; @@ -1083,6 +1081,7 @@ export class ReDNATCOMspViewer { ); await b.commit(); + return true; } async switchModel(display: Partial<Display>) {