diff --git a/src/mol-model/loci.ts b/src/mol-model/loci.ts index 5e2b277730e48b756fef3388b3f3fe4d6b8649e9..f6b55d3339fd98619034506b3246f08965bac15b 100644 --- a/src/mol-model/loci.ts +++ b/src/mol-model/loci.ts @@ -222,7 +222,12 @@ namespace Loci { 'element': (loci: Loci) => loci, 'residue': (loci: Loci) => { return StructureElement.Loci.is(loci) - ? StructureElement.Loci.extendToWholeResidues(loci, true) + ? StructureElement.Loci.extendToWholeResidues(loci, 1, true) + : loci; + }, + 'two-residues': (loci: Loci) => { + return StructureElement.Loci.is(loci) + ? StructureElement.Loci.extendToWholeResidues(loci, 2, true) : loci; }, 'chain': (loci: Loci) => { @@ -261,7 +266,7 @@ namespace Loci { }, 'residueInstances': (loci: Loci) => { return StructureElement.Loci.is(loci) - ? StructureElement.Loci.extendToAllInstances(StructureElement.Loci.extendToWholeResidues(loci, true)) + ? StructureElement.Loci.extendToAllInstances(StructureElement.Loci.extendToWholeResidues(loci, 1, true)) : loci; }, 'chainInstances': (loci: Loci) => { diff --git a/src/mol-model/structure/structure/element/loci.ts b/src/mol-model/structure/structure/element/loci.ts index 9fefd8d8f0c17592ce0c242e97c0b2fdc047b775..455d0a3bd6532d51f7a7e16f10e5ea94a22115f1 100644 --- a/src/mol-model/structure/structure/element/loci.ts +++ b/src/mol-model/structure/structure/element/loci.ts @@ -124,7 +124,7 @@ export namespace Loci { export function firstResidue(loci: Loci): Loci { if (isEmpty(loci)) return loci; - return extendToWholeResidues(firstElement(loci)); + return extendToWholeResidues(firstElement(loci), 1); } export function firstChain(loci: Loci): Loci { @@ -281,7 +281,7 @@ export namespace Loci { } } - export function extendToWholeResidues(loci: Loci, restrictToConformation?: boolean): Loci { + export function extendToWholeResidues(loci: Loci, count: number, restrictToConformation?: boolean): Loci { const elements: Loci['elements'][0][] = []; const residueAltIds = new Set<string>(); @@ -305,16 +305,20 @@ export namespace Loci { residueAltIds.clear(); const eI = unitElements[OrderedSet.getAt(indices, i)]; const rI = residueIndex[eI]; + const rIEnd = rI + count >= residueIndex.length ? residueIndex.length - 1 : rI + count; residueAltIds.add(label_alt_id.value(eI)); i++; while (i < len) { const eI = unitElements[OrderedSet.getAt(indices, i)]; - if (residueIndex[eI] !== rI) break; + const _rI = residueIndex[eI]; + if (_rI < rI || _rI > rIEnd) { + break; + } residueAltIds.add(label_alt_id.value(eI)); i++; } const hasSharedAltId = residueAltIds.has(''); - for (let j = residueOffsets[rI], _j = residueOffsets[rI + 1]; j < _j; j++) { + for (let j = residueOffsets[rI], _j = residueOffsets[rIEnd]; j < _j; j++) { const idx = OrderedSet.indexOf(unitElements, j); if (idx >= 0) { const altId = label_alt_id.value(j); diff --git a/src/mol-plugin-ui/structure/focus.tsx b/src/mol-plugin-ui/structure/focus.tsx index f64af65c64e97ba528166335c394a612fcf11da0..db4c8b7f48394fb1215f18ef6c69dfb918de8485 100644 --- a/src/mol-plugin-ui/structure/focus.tsx +++ b/src/mol-plugin-ui/structure/focus.tsx @@ -24,14 +24,21 @@ interface StructureFocusControlsState { showAction: boolean } -function addSymmetryGroupEntries(entries: Map<string, FocusEntry[]>, location: StructureElement.Location, unitSymmetryGroup: Unit.SymmetryGroup, granularity: 'residue' | 'chain') { +function addSymmetryGroupEntries(entries: Map<string, FocusEntry[]>, location: StructureElement.Location, unitSymmetryGroup: Unit.SymmetryGroup, granularity: 'residue' | 'two-residues' | 'chain') { const idx = SortedArray.indexOf(location.unit.elements, location.element) as UnitIndex; const base = StructureElement.Loci(location.structure, [ { unit: location.unit, indices: OrderedSet.ofSingleton(idx) } ]); - const extended = granularity === 'residue' - ? StructureElement.Loci.extendToWholeResidues(base) - : StructureElement.Loci.extendToWholeChains(base); + const extended = (() => { + switch (granularity) { + case 'residue': + return StructureElement.Loci.extendToWholeResidues(base, 1); + case 'two-residues': + return StructureElement.Loci.extendToWholeResidues(base, 2); + default: + return StructureElement.Loci.extendToWholeChains(base); + } + })(); const name = StructureProperties.entity.pdbx_description(location).join(', '); for (const u of unitSymmetryGroup.units) { diff --git a/src/mol-plugin/behavior/dynamic/selection/structure-focus-representation.ts b/src/mol-plugin/behavior/dynamic/selection/structure-focus-representation.ts index 344b5b1bbf0b30f65b1e99c2fe3ca860b5c4bf7d..eda773be8d2df73a99914d1350b8363649b6d27a 100644 --- a/src/mol-plugin/behavior/dynamic/selection/structure-focus-representation.ts +++ b/src/mol-plugin/behavior/dynamic/selection/structure-focus-representation.ts @@ -160,7 +160,7 @@ class StructureFocusRepresentationBehavior extends PluginBehavior.WithSubscriber this.currentSource = sourceLoci; const loci = StructureElement.Loci.remap(sourceLoci, parent.obj!.data); - const residueLoci = StructureElement.Loci.extendToWholeResidues(loci); + const residueLoci = StructureElement.Loci.extendToWholeResidues(loci, 1); const residueBundle = StructureElement.Bundle.fromLoci(residueLoci); const target = StructureElement.Bundle.toExpression(residueBundle); diff --git a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts index 1050783faf4dac3ea5437eb5c619f754c2f97dd3..deaff2473cd52b5df435f6017be52b8c04f23e2e 100644 --- a/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts +++ b/src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts @@ -340,7 +340,7 @@ export namespace VolumeStreaming { const transform = GlobalModelTransformInfo.get(root.obj?.data.models[0]!); if (transform) Mat4.invert(this._invTransform, transform); - const extendedLoci = StructureElement.Loci.extendToWholeResidues(loci); + const extendedLoci = StructureElement.Loci.extendToWholeResidues(loci, 1); const box = StructureElement.Loci.getBoundary(extendedLoci, transform && !Number.isNaN(this._invTransform[0]) ? this._invTransform : void 0).box; if (StructureElement.Loci.size(extendedLoci) === 1) { diff --git a/src/mol-theme/label.ts b/src/mol-theme/label.ts index f04c8f65830a5eb544d21d8857e13a574276b5f6..1eae651ecd0eda7601ab3320f92bd9bb5db2ab92 100644 --- a/src/mol-theme/label.ts +++ b/src/mol-theme/label.ts @@ -14,7 +14,7 @@ import { Vec3 } from '../mol-math/linear-algebra'; import { radToDeg } from '../mol-math/misc'; import { Volume } from '../mol-model/volume'; -export type LabelGranularity = 'element' | 'conformation' | 'residue' | 'chain' | 'structure' +export type LabelGranularity = 'element' | 'conformation' | 'residue' | 'two-residues' | 'chain' | 'structure' export const DefaultLabelOptions = { granularity: 'element' as LabelGranularity,