diff --git a/src/mol-model/loci.ts b/src/mol-model/loci.ts index 3def8af3a87fb27038b5b6e2999f758e0e342f47..3064ccfb8ca1afa26e0e21686f8b230230413b31 100644 --- a/src/mol-model/loci.ts +++ b/src/mol-model/loci.ts @@ -40,6 +40,9 @@ export function isDataLoci(x: any): x is DataLoci { export function areDataLociEqual(a: DataLoci, b: DataLoci) { return a.data === b.data && a.tag === b.tag && OrderedSet.areEqual(a.indices, b.indices) } +export function isDataLociEmpty(loci: DataLoci) { + return OrderedSet.size(loci.indices) === 0 ? true : false +} export function createDataLoci(data: any, tag: string, indices: OrderedSet<number>): DataLoci { return { kind: 'data-loci', data, tag, indices } } @@ -73,6 +76,29 @@ namespace Loci { return false } + export function isEmpty(loci: Loci) { + if (isEveryLoci(loci)) return false + if (isEmptyLoci(loci)) return true + if (isDataLoci(loci)) return isDataLociEmpty(loci) + if (Structure.isLoci(loci)) return Structure.isLociEmpty(loci) + if (StructureElement.Loci.is(loci)) StructureElement.Loci.isEmpty(loci) + if (Link.isLoci(loci)) Link.isLociEmpty(loci) + if (Shape.isLoci(loci)) return Shape.isLociEmpty(loci) + if (ShapeGroup.isLoci(loci)) return ShapeGroup.isLociEmpty(loci) + return false + } + + export function remap<T>(loci: Loci, data: T) { + if (data instanceof Structure) { + if (StructureElement.Loci.is(loci)) { + loci = StructureElement.Loci.remap(loci, data) + } else if (Link.isLoci(loci)) { + loci = Link.remapLoci(loci, data) + } + } + return loci + } + const sphereHelper = new CentroidHelper(), tempPos = Vec3.zero(); export function getBoundingSphere(loci: Loci, boundingSphere?: Sphere3D): Sphere3D | undefined { diff --git a/src/mol-model/shape/shape.ts b/src/mol-model/shape/shape.ts index 413229c084ba03176421e029f159caf8391cb58f..da3fea7f21bff0a51bf7ea961122ed7bc9a2a803 100644 --- a/src/mol-model/shape/shape.ts +++ b/src/mol-model/shape/shape.ts @@ -50,6 +50,7 @@ export namespace Shape { export function Loci(shape: Shape): Loci { return { kind: 'shape-loci', shape } } export function isLoci(x: any): x is Loci { return !!x && x.kind === 'shape-loci' } export function areLociEqual(a: Loci, b: Loci) { return a.shape === b.shape } + export function isLociEmpty(loci: Loci) { return loci.shape.groupCount === 0 ? true : false } } export namespace ShapeGroup { @@ -96,4 +97,16 @@ export namespace ShapeGroup { } return true } + + export function isLociEmpty(loci: Loci) { + return size(loci) === 0 ? true : false + } + + export function size(loci: Loci) { + let size = 0 + for (const group of loci.groups) { + size += OrderedSet.size(group.ids) + } + return size + } } \ No newline at end of file diff --git a/src/mol-model/structure/structure/element/loci.ts b/src/mol-model/structure/structure/element/loci.ts index 5006485b8d94681951a2810bab0f0f939a50cd02..df9e6ac52703bb34b5b6049a5cfe51e513d8abf3 100644 --- a/src/mol-model/structure/structure/element/loci.ts +++ b/src/mol-model/structure/structure/element/loci.ts @@ -53,6 +53,10 @@ export namespace Loci { return true } + export function isEmpty(loci: Loci) { + return size(loci) === 0 ? true : false + } + export function size(loci: Loci) { let s = 0; for (const u of loci.elements) s += OrderedSet.size(u.indices); @@ -101,7 +105,7 @@ export namespace Loci { indices = SortedArray.ofSortedArray(_indices) } - elements.push({ unit, indices }) + if (OrderedSet.size(indices) > 0) elements.push({ unit, indices }) } }); @@ -111,7 +115,7 @@ export namespace Loci { /** Create union of `xs` and `ys` */ export function union(xs: Loci, ys: Loci): Loci { if (xs.elements.length > ys.elements.length) return union(ys, xs); - if (xs.elements.length === 0) return ys; + if (Loci.isEmpty(xs)) return ys; const map = new Map<number, OrderedSet<UnitIndex>>(); @@ -155,7 +159,7 @@ export namespace Loci { export function areIntersecting(xs: Loci, ys: Loci): boolean { if (xs.elements.length > ys.elements.length) return areIntersecting(ys, xs); - if (xs.elements.length === 0) return ys.elements.length === 0; + if (Loci.isEmpty(xs)) return Loci.isEmpty(ys); const map = new Map<number, OrderedSet<UnitIndex>>(); @@ -286,7 +290,7 @@ export namespace Loci { } export function toScriptExpression(loci: Loci) { - if (loci.elements.length === 0) return MS.struct.generator.empty(); + if (Loci.isEmpty(loci)) return MS.struct.generator.empty(); const models = loci.structure.models; const sourceIndexMap = new Map<string, { modelLabel: string, modelIndex: number, xs: UniqueArray<number, number> }>(); diff --git a/src/mol-model/structure/structure/element/stats.ts b/src/mol-model/structure/structure/element/stats.ts index 2d53df6001a0f719b2f459c38d18f5f402d8b465..497bfd897be5a91e36576e3e1299138dc3925f94 100644 --- a/src/mol-model/structure/structure/element/stats.ts +++ b/src/mol-model/structure/structure/element/stats.ts @@ -92,7 +92,7 @@ export namespace Stats { export function ofLoci(loci: Loci) { const stats = create() - if (loci.elements.length > 0) { + if (!Loci.isEmpty(loci)) { for (const e of loci.elements) handleElement(stats, e) } return stats diff --git a/src/mol-model/structure/structure/structure.ts b/src/mol-model/structure/structure/structure.ts index d5575c999a4731e100788c31e230e510b18892e4..a88ab91f81dc5bca543665ce3ae06658e00093f1 100644 --- a/src/mol-model/structure/structure/structure.ts +++ b/src/mol-model/structure/structure/structure.ts @@ -468,6 +468,10 @@ namespace Structure { return a.structure === b.structure } + export function isLociEmpty(loci: Loci) { + return loci.structure.isEmpty + } + export function create(units: ReadonlyArray<Unit>, props?: Props): Structure { return new Structure(units, props); } diff --git a/src/mol-model/structure/structure/unit/links.ts b/src/mol-model/structure/structure/unit/links.ts index 8ea138d4d1d8fc8ef0d9e634513c5bb6a4064c13..12e2830c1ad6c07604d467ed5acd0dbc629482c1 100644 --- a/src/mol-model/structure/structure/unit/links.ts +++ b/src/mol-model/structure/structure/unit/links.ts @@ -62,6 +62,10 @@ namespace Link { return true } + export function isLociEmpty(loci: Loci) { + return loci.links.length === 0 ? true : false + } + export function remapLoci(loci: Loci, structure: Structure): Loci { if (structure === loci.structure) return loci diff --git a/src/mol-plugin/ui/sequence/sequence.tsx b/src/mol-plugin/ui/sequence/sequence.tsx index 2d1528bc11a77d88016310b099a5f5f19a229b55..d221eb5288d5d62e263f019ee6fa4ce14fcc3c15 100644 --- a/src/mol-plugin/ui/sequence/sequence.tsx +++ b/src/mol-plugin/ui/sequence/sequence.tsx @@ -13,6 +13,7 @@ import { ButtonsType, ModifiersKeys, getButtons, getModifiers } from '../../../m import { ValueBox } from '../../../mol-util'; import { Residue } from './residue'; import { SequenceWrapper } from './wrapper'; +import { StructureElement } from '../../../mol-model/structure'; type SequenceProps = { sequenceWrapper: SequenceWrapper.Any } type SequenceState = { markerData: ValueBox<Uint8Array> } @@ -62,7 +63,7 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P, Sequ const ev = { current: Interactivity.Loci.Empty, modifiers } if (seqId !== undefined) { const loci = this.props.sequenceWrapper.getLoci(seqId); - if (loci.elements.length > 0) ev.current = { loci }; + if (!StructureElement.Loci.isEmpty(loci)) ev.current = { loci }; } this.plugin.behaviors.interaction.highlight.next(ev) } @@ -71,7 +72,7 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P, Sequ const ev = { current: Interactivity.Loci.Empty, buttons, modifiers } if (seqId !== undefined) { const loci = this.props.sequenceWrapper.getLoci(seqId); - if (loci.elements.length > 0) ev.current = { loci }; + if (!StructureElement.Loci.isEmpty(loci)) ev.current = { loci }; } this.plugin.behaviors.interaction.click.next(ev) } diff --git a/src/mol-plugin/util/structure-element-selection.ts b/src/mol-plugin/util/structure-element-selection.ts index bf82095a7158f553b5e547ae3b6201a3a9625275..cbc8c83201fc2899d048e602031ec7660628f1a9 100644 --- a/src/mol-plugin/util/structure-element-selection.ts +++ b/src/mol-plugin/util/structure-element-selection.ts @@ -69,7 +69,7 @@ class StructureElementSelectionManager { if (entry) { entry.selection = StructureElement.Loci.subtract(entry.selection, loci); this.plugin.events.interactivity.selectionUpdated.next() - return entry.selection.elements.length === 0 ? EmptyLoci : entry.selection; + return StructureElement.Loci.isEmpty(entry.selection) ? EmptyLoci : entry.selection; } } return EmptyLoci @@ -81,7 +81,7 @@ class StructureElementSelectionManager { if (entry) { entry.selection = loci; this.plugin.events.interactivity.selectionUpdated.next() - return entry.selection.elements.length === 0 ? EmptyLoci : entry.selection; + return StructureElement.Loci.isEmpty(entry.selection) ? EmptyLoci : entry.selection; } } return EmptyLoci; @@ -94,7 +94,7 @@ class StructureElementSelectionManager { const k = keys.next(); if (k.done) break; const s = this.entries.get(k.value)!; - if (s.selection.elements.length > 0) selections.push(s.selection); + if (!StructureElement.Loci.isEmpty(s.selection)) selections.push(s.selection); s.selection = StructureElement.Loci(s.selection.structure, []); } this.plugin.events.interactivity.selectionUpdated.next() diff --git a/src/mol-plugin/util/structure-representation-helper.ts b/src/mol-plugin/util/structure-representation-helper.ts index e9b9131891531f339922938d2262e7335a5611f9..f14484abf60621c0acceb57c1e3ecbde50502ad9 100644 --- a/src/mol-plugin/util/structure-representation-helper.ts +++ b/src/mol-plugin/util/structure-representation-helper.ts @@ -65,7 +65,7 @@ export class StructureRepresentationHelper { bundle: StructureElement.Bundle.fromLoci(combinedLoci) }) } else { - const combinedLoci = getCombinedLoci(modifier, loci, StructureElement.Loci(s, [])) + const combinedLoci = getCombinedLoci(modifier, loci, StructureElement.Loci.none(s)) const params = StructureRepresentation3DHelpers.getDefaultParams(this.plugin, type as any, s) const p = params.type.params diff --git a/src/mol-repr/structure/complex-representation.ts b/src/mol-repr/structure/complex-representation.ts index 4ffd090a3693850cf6bd409b1ef337c750ffcc81..eae6db967ffbe77367020d834a7c99fa0c1084c1 100644 --- a/src/mol-repr/structure/complex-representation.ts +++ b/src/mol-repr/structure/complex-representation.ts @@ -58,11 +58,9 @@ export function ComplexRepresentation<P extends StructureParams>(label: string, if (!_structure) return false if (!StructureElement.Loci.is(loci) && !Link.isLoci(loci)) return false if (!Structure.areRootsEquivalent(loci.structure, _structure)) return false - if (StructureElement.Loci.is(loci)) { - loci = StructureElement.Loci.remap(loci, _structure) - } else if (Link.isLoci(loci)) { - loci = Link.remapLoci(loci, _structure) - } + // Remap `loci` from equivalent structure to the current `_structure` + loci = Loci.remap(loci, _structure) + if (Loci.isEmpty(loci)) return false return visual ? visual.mark(loci, action) : false } diff --git a/src/mol-repr/structure/units-representation.ts b/src/mol-repr/structure/units-representation.ts index 6eb6ba7a96985b31519468f58be4b3f989061847..5ae02a3d0581935075da83fe7c06a60edfac6491 100644 --- a/src/mol-repr/structure/units-representation.ts +++ b/src/mol-repr/structure/units-representation.ts @@ -167,13 +167,9 @@ export function UnitsRepresentation<P extends UnitsParams>(label: string, ctx: R if (!_structure) return false if (!StructureElement.Loci.is(loci) && !Link.isLoci(loci)) return false if (!Structure.areRootsEquivalent(loci.structure, _structure)) return false - if (StructureElement.Loci.is(loci)) { - loci = StructureElement.Loci.remap(loci, _structure) - if (loci.elements.length === 0) return false - } else if (Link.isLoci(loci)) { - loci = Link.remapLoci(loci, _structure) - if (loci.links.length === 0) return false - } + // Remap `loci` from equivalent structure to the current `_structure` + loci = Loci.remap(loci, _structure) + if (Loci.isEmpty(loci)) return false visuals.forEach(({ visual }) => { changed = visual.mark(loci, action) || changed })