diff --git a/src/mol-plugin-ui/structure/focus.tsx b/src/mol-plugin-ui/structure/focus.tsx index dd15ea1149b3475ce47baaeaf964b72259699298..da266adf54460ae2f279bc8ee1e4e3cd81bbc2d4 100644 --- a/src/mol-plugin-ui/structure/focus.tsx +++ b/src/mol-plugin-ui/structure/focus.tsx @@ -8,7 +8,7 @@ import * as React from 'react'; import { PluginUIComponent } from '../base'; import { ToggleButton, IconButton, Button } from '../controls/common'; import { ActionMenu } from '../controls/action-menu'; -import { StructureElement, StructureProperties, Structure } from '../../mol-model/structure'; +import { StructureElement, StructureProperties, Structure, Unit } from '../../mol-model/structure'; import { OrderedSet, SortedArray } from '../../mol-data/int'; import { UnitIndex } from '../../mol-model/structure/structure/element/element'; import { FocusEntry } from '../../mol-plugin-state/manager/structure/focus'; @@ -22,6 +22,31 @@ interface StructureFocusControlsState { showAction: boolean } +function addSymmetryGroupEntries(entries: Map<string, FocusEntry[]>, location: StructureElement.Location, unitSymmetryGroup: Unit.SymmetryGroup) { + const idx = SortedArray.indexOf(location.unit.elements, location.element) as UnitIndex + const base = StructureElement.Loci.extendToWholeResidues( + StructureElement.Loci(location.structure, [ + { unit: location.unit, indices: OrderedSet.ofSingleton(idx) } + ]) + ) + const name = StructureProperties.entity.pdbx_description(location).join(', ') + + for (const u of unitSymmetryGroup.units) { + const loci = StructureElement.Loci(base.structure, [ + { unit: u, indices: base.elements[0].indices } + ]) + + let label = lociLabel(loci, { reverse: true, hidePrefix: true, htmlStyling: false, granularity: 'residue' }) + if (unitSymmetryGroup.units.length > 1) { + label += ` | ${loci.elements[0].unit.conformation.operator.name}` + } + const item: FocusEntry = { label, category: name, loci } + + if (entries.has(name)) entries.get(name)!.push(item) + else entries.set(name, [item]) + } +} + function getFocusEntries(structure: Structure) { const entityEntries = new Map<string, FocusEntry[]>() const l = StructureElement.Location.create(structure) @@ -29,28 +54,26 @@ function getFocusEntries(structure: Structure) { for (const ug of structure.unitSymmetryGroups) { l.unit = ug.units[0] l.element = ug.elements[0] - const et = StructureProperties.entity.type(l) - if (et === 'non-polymer') { - for (const u of ug.units) { - l.unit = u - const idx = SortedArray.indexOf(u.elements, l.element) as UnitIndex - const loci = StructureElement.Loci.extendToWholeResidues( - StructureElement.Loci(structure, [ - { unit: l.unit, indices: OrderedSet.ofSingleton(idx) } - ]) - ) - let label = lociLabel(loci, { reverse: true, hidePrefix: true, htmlStyling: false, granularity: 'residue' }) - if (ug.units.length > 1) { - label += ` | ${u.conformation.operator.name}` + const entityType = StructureProperties.entity.type(l) + const isMultiChain = Unit.Traits.is(l.unit.traits, Unit.Trait.MultiChain) + const isPolymer = entityType === 'non-polymer' + const isBranched = entityType === 'branched' + + if (isPolymer && !isMultiChain) { + addSymmetryGroupEntries(entityEntries, l, ug) + } else if (isBranched || (isPolymer && isMultiChain)) { + const u = l.unit + const { index: residueIndex } = u.model.atomicHierarchy.residueAtomSegments + let prev = -1 + for (let i = 0, il = u.elements.length; i < il; ++i) { + const eI = u.elements[i] + const rI = residueIndex[eI] + if(rI !== prev) { + l.element = eI + addSymmetryGroupEntries(entityEntries, l, ug) + prev = rI } - const name = StructureProperties.entity.pdbx_description(l).join(', ') - const item: FocusEntry = { label, category: name, loci } - - if (entityEntries.has(name)) entityEntries.get(name)!.push(item) - else entityEntries.set(name, [item]) } - } else if (et === 'branched') { - // TODO split into residues } }