diff --git a/src/mol-model/structure/structure/element.ts b/src/mol-model/structure/structure/element.ts index 9a0eaee3b780e90947eb3133b40ddc4001c81cc3..47c9101f45ccaf17499d6b386adc3f61ea69a702 100644 --- a/src/mol-model/structure/structure/element.ts +++ b/src/mol-model/structure/structure/element.ts @@ -196,30 +196,33 @@ namespace StructureElement { const elements: Loci['elements'][0][] = []; for (const lociElement of loci.elements) { - if (lociElement.unit.kind !== Unit.Kind.Atomic) elements[elements.length] = lociElement; + if (lociElement.unit.kind === Unit.Kind.Atomic) { + const unitElements = lociElement.unit.elements; + const h = lociElement.unit.model.atomicHierarchy; - const unitElements = lociElement.unit.elements; - const h = lociElement.unit.model.atomicHierarchy; - - const { index: residueIndex, offsets: residueOffsets } = h.residueAtomSegments; + const { index: residueIndex, offsets: residueOffsets } = h.residueAtomSegments; - const newIndices: UnitIndex[] = []; - const indices = lociElement.indices, len = OrderedSet.size(indices); - let i = 0; - while (i < len) { - const rI = residueIndex[unitElements[OrderedSet.getAt(indices, i)]]; - i++; - while (i < len && residueIndex[unitElements[OrderedSet.getAt(indices, i)]] === rI) { + const newIndices: UnitIndex[] = []; + const indices = lociElement.indices, len = OrderedSet.size(indices); + let i = 0; + while (i < len) { + const rI = residueIndex[unitElements[OrderedSet.getAt(indices, i)]]; i++; + while (i < len && residueIndex[unitElements[OrderedSet.getAt(indices, i)]] === rI) { + i++; + } + + for (let j = residueOffsets[rI], _j = residueOffsets[rI + 1]; j < _j; j++) { + const idx = OrderedSet.indexOf(unitElements, j); + if (idx >= 0) newIndices[newIndices.length] = idx as UnitIndex; + } } - for (let j = residueOffsets[rI], _j = residueOffsets[rI + 1]; j < _j; j++) { - const idx = OrderedSet.indexOf(unitElements, j); - if (idx >= 0) newIndices[newIndices.length] = idx as UnitIndex; - } + elements[elements.length] = { unit: lociElement.unit, indices: SortedArray.ofSortedArray(newIndices) }; + } else { + // coarse elements are already by-residue + elements[elements.length] = lociElement; } - - elements[elements.length] = { unit: lociElement.unit, indices: SortedArray.ofSortedArray(newIndices) }; } return Loci(loci.structure, elements); diff --git a/src/mol-model/structure/structure/unit/links.ts b/src/mol-model/structure/structure/unit/links.ts index ecefe98af02b59f283ad3a0de0e146808e425053..f0d13d128a6e8f2f675a178674b8a1a22fc8c9d4 100644 --- a/src/mol-model/structure/structure/unit/links.ts +++ b/src/mol-model/structure/structure/unit/links.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017-2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -8,6 +8,7 @@ import { Unit, StructureElement } from '../../structure' import Structure from '../structure'; import { LinkType } from '../../model/types'; +import { SortedArray } from '../../../../mol-data/int'; export * from './links/data' export * from './links/intra-compute' @@ -61,6 +62,34 @@ namespace Link { return true } + // TODO + export function toStructureElementLoci(loci: Loci): StructureElement.Loci { + const elements: StructureElement.Loci['elements'][0][] = [] + const map = new Map<number, number[]>() + + for (const lociLink of loci.links) { + const { aIndex, aUnit, bIndex, bUnit } = lociLink + if (aUnit === bUnit) { + if (map.has(aUnit.id)) map.get(aUnit.id)!.push(aIndex, bIndex) + else map.set(aUnit.id, [aIndex, bIndex]) + } else { + if (map.has(aUnit.id)) map.get(aUnit.id)!.push(aIndex) + else map.set(aUnit.id, [aIndex]) + if (map.has(bUnit.id)) map.get(bUnit.id)!.push(bIndex) + else map.set(bUnit.id, [bIndex]) + } + } + + map.forEach((indices: number[], id: number) => { + elements.push({ + unit: loci.structure.unitMap.get(id)!, + indices: SortedArray.deduplicate(SortedArray.ofUnsortedArray(indices)) + }) + }) + + return StructureElement.Loci(loci.structure, elements); + } + export function getType(structure: Structure, link: Location<Unit.Atomic>): LinkType { if (link.aUnit === link.bUnit) { const links = link.aUnit.links; diff --git a/src/mol-plugin/util/interactivity.ts b/src/mol-plugin/util/interactivity.ts index a334354816865b80f5b40c9832d383b4d128feb1..4b32e676df50e605ff1ec7d683bd06de4dccaa2c 100644 --- a/src/mol-plugin/util/interactivity.ts +++ b/src/mol-plugin/util/interactivity.ts @@ -8,7 +8,7 @@ import { Loci as ModelLoci, EmptyLoci } from '../../mol-model/loci'; import { ModifiersKeys, ButtonsType } from '../../mol-util/input/input-observer'; import { Representation } from '../../mol-repr/representation'; -import { StructureElement } from '../../mol-model/structure'; +import { StructureElement, Link } from '../../mol-model/structure'; import { MarkerAction } from '../../mol-util/marker-action'; import { StructureElementSelectionManager } from './structure-element-selection'; import { PluginContext } from '../context'; @@ -90,8 +90,13 @@ namespace Interactivity { // TODO clear, then re-apply remaining providers } - expandLoci(loci: ModelLoci) { - return LociExpansion[this.props.lociExpansion](loci) + normalizedLoci(interactivityLoci: Loci) { + let { loci, repr } = interactivityLoci + if (this.props.lociExpansion !== 'none' && Link.isLoci(loci)) { + loci = Link.toStructureElementLoci(loci) + } + loci = LociExpansion[this.props.lociExpansion](loci) + return { loci, repr } } protected mark(current: Loci<ModelLoci>, action: MarkerAction) { @@ -111,22 +116,23 @@ namespace Interactivity { apply(e: HighlightEvent) { const { current, modifiers } = e - const expanded: Loci<ModelLoci> = { loci: this.expandLoci(current.loci), repr: current.repr } - if (StructureElement.isLoci(expanded.loci)) { - let loci: StructureElement.Loci = expanded.loci; + + const normalized: Loci<ModelLoci> = this.normalizedLoci(current) + if (StructureElement.isLoci(normalized.loci)) { + let loci: StructureElement.Loci = normalized.loci; if (modifiers && modifiers.shift) { loci = this.sel.tryGetRange(loci) || loci; } this.mark(this.prev, MarkerAction.RemoveHighlight); - const toHighlight = { loci, repr: expanded.repr }; + const toHighlight = { loci, repr: normalized.repr }; this.mark(toHighlight, MarkerAction.Highlight); this.prev = toHighlight; } else { - if (!Loci.areEqual(this.prev, expanded)) { + if (!Loci.areEqual(this.prev, normalized)) { this.mark(this.prev, MarkerAction.RemoveHighlight); - this.mark(expanded, MarkerAction.Highlight); - this.prev = expanded; + this.mark(normalized, MarkerAction.Highlight); + this.prev = normalized; } } } @@ -150,34 +156,34 @@ namespace Interactivity { apply(e: ClickEvent) { const { current, buttons, modifiers } = e - const expanded: Loci<ModelLoci> = { loci: this.expandLoci(current.loci), repr: current.repr } - if (expanded.loci.kind === 'empty-loci') { + const normalized: Loci<ModelLoci> = this.normalizedLoci(current) + if (normalized.loci.kind === 'empty-loci') { if (modifiers.control && buttons === ButtonsType.Flag.Secondary) { // clear the selection on Ctrl + Right-Click on empty const sels = this.sel.clear(); for (const s of sels) this.mark({ loci: s }, MarkerAction.Deselect); } - } else if (StructureElement.isLoci(expanded.loci)) { + } else if (StructureElement.isLoci(normalized.loci)) { if (modifiers.control && buttons === ButtonsType.Flag.Secondary) { // select only the current element on Ctrl + Right-Click - const old = this.sel.get(expanded.loci.structure); + const old = this.sel.get(normalized.loci.structure); this.mark({ loci: old }, MarkerAction.Deselect); - this.sel.set(expanded.loci); - this.mark(expanded, MarkerAction.Select); + this.sel.set(normalized.loci); + this.mark(normalized, MarkerAction.Select); } else if (modifiers.control && buttons === ButtonsType.Flag.Primary) { // toggle current element on Ctrl + Left-Click - this.toggleSel(expanded as Representation.Loci<StructureElement.Loci>); + this.toggleSel(normalized as Representation.Loci<StructureElement.Loci>); } else if (modifiers.shift && buttons === ButtonsType.Flag.Primary) { // try to extend sequence on Shift + Left-Click - let loci: StructureElement.Loci = expanded.loci; + let loci: StructureElement.Loci = normalized.loci; if (modifiers && modifiers.shift) { loci = this.sel.tryGetRange(loci) || loci; } - this.toggleSel({ loci, repr: expanded.repr }); + this.toggleSel({ loci, repr: normalized.repr }); } } else { if (!ButtonsType.has(buttons, ButtonsType.Flag.Secondary)) return; - for (let p of this.providers) p(expanded, MarkerAction.Toggle); + for (let p of this.providers) p(normalized, MarkerAction.Toggle); } }