Преглед изворни кода

added Structure property to StructureElement and Bond locations

David Sehnal пре 5 година
родитељ
комит
a080114690
59 измењених фајлова са 268 додато и 207 уклоњено
  1. 1 1
      src/apps/structure-info/model.ts
  2. 1 1
      src/examples/proteopedia-wrapper/coloring.ts
  3. 4 3
      src/mol-model-props/computed/accessible-surface-area/shrake-rupley/area.ts
  4. 2 1
      src/mol-model-props/computed/accessible-surface-area/shrake-rupley/radii.ts
  5. 9 5
      src/mol-model-props/computed/interactions/interactions.ts
  6. 5 4
      src/mol-model-props/computed/representations/interactions-inter-unit-cylinder.ts
  7. 4 4
      src/mol-model-props/computed/representations/interactions-intra-unit-cylinder.ts
  8. 16 12
      src/mol-model-props/rcsb/representations/validation-report-clashes.ts
  9. 2 2
      src/mol-model/structure/export/categories/modified-residues.ts
  10. 4 3
      src/mol-model/structure/export/categories/secondary-structure.ts
  11. 3 3
      src/mol-model/structure/model/properties/custom/indexed.ts
  12. 15 12
      src/mol-model/structure/query/context.ts
  13. 4 2
      src/mol-model/structure/query/queries/filters.ts
  14. 5 0
      src/mol-model/structure/query/queries/generators.ts
  15. 3 3
      src/mol-model/structure/query/queries/internal.ts
  16. 7 0
      src/mol-model/structure/query/queries/modifiers.ts
  17. 1 1
      src/mol-model/structure/query/utils/builders.ts
  18. 6 2
      src/mol-model/structure/query/utils/structure-distance.ts
  19. 15 3
      src/mol-model/structure/structure/element/location.ts
  20. 3 2
      src/mol-model/structure/structure/element/loci.ts
  21. 24 23
      src/mol-model/structure/structure/element/stats.ts
  22. 4 3
      src/mol-model/structure/structure/structure.ts
  23. 16 3
      src/mol-model/structure/structure/unit/bonds.ts
  24. 1 1
      src/mol-model/structure/structure/util/lookup3d.ts
  25. 9 9
      src/mol-plugin-ui/sequence.tsx
  26. 2 2
      src/mol-plugin-ui/sequence/chain.ts
  27. 1 1
      src/mol-plugin-ui/sequence/polymer.ts
  28. 1 1
      src/mol-plugin-ui/sequence/sequence.tsx
  29. 1 1
      src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts
  30. 6 5
      src/mol-repr/structure/visual/bond-inter-unit-cylinder.ts
  31. 5 5
      src/mol-repr/structure/visual/bond-intra-unit-cylinder.ts
  32. 2 2
      src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts
  33. 2 2
      src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts
  34. 2 2
      src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts
  35. 5 3
      src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts
  36. 1 1
      src/mol-repr/structure/visual/ellipsoid-mesh.ts
  37. 2 2
      src/mol-repr/structure/visual/gaussian-surface-mesh.ts
  38. 1 1
      src/mol-repr/structure/visual/gaussian-surface-wireframe.ts
  39. 3 6
      src/mol-repr/structure/visual/label-text.ts
  40. 1 1
      src/mol-repr/structure/visual/molecular-surface-mesh.ts
  41. 1 1
      src/mol-repr/structure/visual/molecular-surface-wireframe.ts
  42. 2 2
      src/mol-repr/structure/visual/orientation-ellipsoid-mesh.ts
  43. 1 1
      src/mol-repr/structure/visual/polymer-backbone-cylinder.ts
  44. 1 1
      src/mol-repr/structure/visual/polymer-gap-cylinder.ts
  45. 3 3
      src/mol-repr/structure/visual/util/bond.ts
  46. 3 3
      src/mol-repr/structure/visual/util/common.ts
  47. 4 4
      src/mol-repr/structure/visual/util/element.ts
  48. 6 6
      src/mol-repr/structure/visual/util/gaussian.ts
  49. 5 5
      src/mol-repr/structure/visual/util/molecular-surface.ts
  50. 2 2
      src/mol-repr/structure/visual/util/nucleotide.ts
  51. 6 6
      src/mol-repr/structure/visual/util/polymer.ts
  52. 11 11
      src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts
  53. 11 11
      src/mol-repr/structure/visual/util/polymer/gap-iterator.ts
  54. 6 6
      src/mol-repr/structure/visual/util/polymer/trace-iterator.ts
  55. 1 1
      src/mol-theme/color/chain-id.ts
  56. 1 1
      src/mol-theme/color/entity-source.ts
  57. 1 1
      src/mol-theme/color/polymer-id.ts
  58. 3 3
      src/mol-theme/label.ts
  59. 1 1
      src/perf-tests/structure.ts

+ 1 - 1
src/apps/structure-info/model.ts

@@ -152,7 +152,7 @@ export function printRings(structure: Structure) {
 
 export function printUnits(structure: Structure) {
     console.log('\nUnits\n=============');
-    const l = StructureElement.Location.create();
+    const l = StructureElement.Location.create(structure);
 
     for (const unit of structure.units) {
         l.unit = unit;

+ 1 - 1
src/examples/proteopedia-wrapper/coloring.ts

@@ -54,7 +54,7 @@ export function createProteopediaCustomTheme(colors: number[]) {
         const colors = props.colors, colorCount = colors.length, defaultColor = colors[0].color;
 
         if (ctx.structure) {
-            const l = StructureElement.Location.create()
+            const l = StructureElement.Location.create(ctx.structure)
             const { models } = ctx.structure
             const asymIdSerialMap = new Map<string, number>()
             for (let i = 0, il = models.length; i < il; ++i) {

+ 4 - 3
src/mol-model-props/computed/accessible-surface-area/shrake-rupley/area.ts

@@ -31,10 +31,11 @@ export async function computeArea(runtime: RuntimeContext, ctx: ShrakeRupleyCont
 const aPos = Vec3();
 const bPos = Vec3();
 const testPoint = Vec3();
-const aLoc = StructureElement.Location.create()
-const bLoc = StructureElement.Location.create()
+const aLoc = StructureElement.Location.create(void 0 as any)
+const bLoc = StructureElement.Location.create(void 0 as any)
 
 function setLocation(l: StructureElement.Location, structure: Structure, serialIndex: number) {
+    l.structure = structure;
     l.unit = structure.units[structure.serialMapping.unitIndices[serialIndex]]
     l.element = structure.serialMapping.elementIndices[serialIndex]
     return l
@@ -63,7 +64,7 @@ function computeRange(ctx: ShrakeRupleyContext, begin: number, end: number) {
         const neighbors = []; // TODO reuse
         for (let iI = 0; iI < count; ++iI) {
             const bUnit = units[iI]
-            StructureElement.Location.set(bLoc, bUnit, bUnit.elements[indices[iI]])
+            StructureElement.Location.set(bLoc, ctx.structure, bUnit, bUnit.elements[indices[iI]])
             const bI = cumulativeUnitElementCount[unitIndexMap.get(bUnit.id)] + indices[iI]
 
             const radius2 = VdWLookup[atomRadiusType[bI]];

+ 2 - 1
src/mol-model-props/computed/accessible-surface-area/shrake-rupley/radii.ts

@@ -12,7 +12,7 @@ import { VdwRadius } from '../../../../mol-model/structure/model/properties/atom
 import { StructureElement, StructureProperties } from '../../../../mol-model/structure/structure';
 import { getElementMoleculeType } from '../../../../mol-model/structure/util';
 
-const l = StructureElement.Location.create()
+const l = StructureElement.Location.create(void 0)
 
 export function assignRadiusForHeavyAtoms(ctx: ShrakeRupleyContext) {
     const { label_comp_id, key } = StructureProperties.residue
@@ -23,6 +23,7 @@ export function assignRadiusForHeavyAtoms(ctx: ShrakeRupleyContext) {
     let residueIdx = 0
     let serialResidueIdx = -1
 
+    l.structure = structure;
     for (let i = 0, m = 0, il = structure.units.length; i < il; ++i) {
         const unit = structure.units[i]
         const { elements } = unit

+ 9 - 5
src/mol-model-props/computed/interactions/interactions.ts

@@ -39,6 +39,7 @@ interface Interactions {
 
 namespace Interactions {
     export interface Element {
+        structure: Structure,
         unitA: Unit
         /** Index into features of unitA */
         indexA: Features.FeatureIndex
@@ -48,8 +49,9 @@ namespace Interactions {
     }
     export interface Location extends DataLocation<Interactions, Element> {}
 
-    export function Location(interactions: Interactions, unitA?: Unit, indexA?: Features.FeatureIndex, unitB?: Unit, indexB?: Features.FeatureIndex): Location {
-        return DataLocation('interactions', interactions, { unitA: unitA as any, indexA: indexA as any, unitB: unitB as any, indexB: indexB as any });
+    export function Location(interactions: Interactions, structure: Structure, unitA?: Unit, indexA?: Features.FeatureIndex, unitB?: Unit, indexB?: Features.FeatureIndex): Location {
+        return DataLocation('interactions', interactions, 
+            { structure: structure as any, unitA: unitA as any, indexA: indexA as any, unitB: unitB as any, indexB: indexB as any });
     }
 
     export function isLocation(x: any): x is Location {
@@ -87,7 +89,9 @@ namespace Interactions {
     export interface Loci extends DataLoci<StructureInteractions, Element> { }
 
     export function Loci(structure: Structure, interactions: Interactions, elements: ReadonlyArray<Element>): Loci {
-        return DataLoci('interactions', { structure, interactions }, elements, (boundingSphere) => getBoundingSphere(interactions, elements, boundingSphere), () => getLabel(interactions, elements));
+        return DataLoci('interactions', { structure, interactions }, elements,
+            (boundingSphere) => getBoundingSphere(interactions, elements, boundingSphere),
+            () => getLabel(structure, interactions, elements));
     }
 
     export function isLoci(x: any): x is Loci {
@@ -103,7 +107,7 @@ namespace Interactions {
         }, boundingSphere)
     }
 
-    export function getLabel(interactions: Interactions, elements: ReadonlyArray<Element>) {
+    export function getLabel(structure: Structure, interactions: Interactions, elements: ReadonlyArray<Element>) {
         const element = elements[0]
         if (element === undefined) return ''
         const { unitA, indexA, unitB, indexB } = element
@@ -116,7 +120,7 @@ namespace Interactions {
         }
         return [
             _label(interactions, element),
-            bondLabel(Bond.Location(unitA, mA[oA[indexA]], unitB, mB[oB[indexB]]), options)
+            bondLabel(Bond.Location(structure, unitA, mA[oA[indexA]], structure, unitB, mB[oB[indexB]]), options)
         ].join('</br>')
     }
 }

+ 5 - 4
src/mol-model-props/computed/representations/interactions-inter-unit-cylinder.ts

@@ -21,7 +21,7 @@ import { InteractionsProvider } from '../interactions';
 import { LocationIterator } from '../../../mol-geo/util/location-iterator';
 import { InteractionFlag } from '../interactions/common';
 
-const tmpLoc = StructureElement.Location.create()
+const tmpLoc = StructureElement.Location.create(void 0)
 
 function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InteractionsInterUnitParams>, mesh?: Mesh) {
     if (!structure.hasAtomic) return Mesh.createEmpty(mesh)
@@ -49,6 +49,7 @@ function createInterUnitInteractionCylinderMesh(ctx: VisualContext, structure: S
         radius: (edgeIndex: number) => {
             const b = edges[edgeIndex]
             const fA = unitsFeatures.get(b.unitA.id)
+            tmpLoc.structure = structure;
             tmpLoc.unit = b.unitA
             tmpLoc.element = b.unitA.elements[fA.members[fA.offsets[b.indexA]]]
             const sizeA = theme.size.size(tmpLoc)
@@ -100,8 +101,8 @@ function getInteractionLoci(pickingId: PickingId, structure: Structure, id: numb
         const interactions = InteractionsProvider.get(structure).value!
         const c = interactions.contacts.edges[groupId]
         return Interactions.Loci(structure, interactions, [
-            { unitA: c.unitA, indexA: c.indexA, unitB: c.unitB, indexB: c.indexB },
-            { unitA: c.unitB, indexA: c.indexB, unitB: c.unitA, indexB: c.indexA },
+            { structure, unitA: c.unitA, indexA: c.indexA, unitB: c.unitB, indexB: c.indexB },
+            { structure, unitA: c.unitB, indexA: c.indexB, unitB: c.unitA, indexB: c.indexA },
         ])
     }
     return EmptyLoci
@@ -130,7 +131,7 @@ function createInteractionsIterator(structure: Structure): LocationIterator {
     const { contacts } = interactions
     const groupCount = contacts.edgeCount
     const instanceCount = 1
-    const location = Interactions.Location(interactions)
+    const location = Interactions.Location(interactions, structure)
     const { element } = location
     const getLocation = (groupIndex: number) => {
         const c = contacts.edges[groupIndex]

+ 4 - 4
src/mol-model-props/computed/representations/interactions-intra-unit-cylinder.ts

@@ -24,7 +24,7 @@ import { InteractionFlag } from '../interactions/common';
 async function createIntraUnitInteractionsCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<InteractionsIntraUnitParams>, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
-    const location = StructureElement.Location.create(unit)
+    const location = StructureElement.Location.create(structure, unit)
 
     const interactions = InteractionsProvider.get(structure).value!
     const features = interactions.unitsFeatures.get(unit.id)
@@ -97,8 +97,8 @@ function getInteractionLoci(pickingId: PickingId, structureGroup: StructureGroup
         const interactions = InteractionsProvider.get(structure).value!
         const { a, b } = interactions.unitsContacts.get(unit.id)
         return Interactions.Loci(structure, interactions, [
-            { unitA: unit, indexA: a[groupId], unitB: unit, indexB: b[groupId] },
-            { unitA: unit, indexA: b[groupId], unitB: unit, indexB: a[groupId] },
+            { structure, unitA: unit, indexA: a[groupId], unitB: unit, indexB: b[groupId] },
+            { structure, unitA: unit, indexA: b[groupId], unitB: unit, indexB: a[groupId] },
         ])
     }
     return EmptyLoci
@@ -134,7 +134,7 @@ function createInteractionsIterator(structureGroup: StructureGroup): LocationIte
     const contacts = interactions.unitsContacts.get(unit.id)
     const groupCount = contacts.edgeCount * 2
     const instanceCount = group.units.length
-    const location = Interactions.Location(interactions)
+    const location = Interactions.Location(interactions, structure)
     const { element } = location
     const getLocation = (groupIndex: number, instanceIndex: number) => {
         const instanceUnit = group.units[instanceIndex]

+ 16 - 12
src/mol-model-props/rcsb/representations/validation-report-clashes.ts

@@ -91,7 +91,7 @@ function getIntraClashBoundingSphere(unit: Unit.Atomic, clashes: IntraUnitClashe
     }, boundingSphere)
 }
 
-function getIntraClashLabel(unit: Unit.Atomic, clashes: IntraUnitClashes, elements: number[]) {
+function getIntraClashLabel(structure: Structure, unit: Unit.Atomic, clashes: IntraUnitClashes, elements: number[]) {
     const idx = elements[0]
     if (idx === undefined) return ''
     const { edgeProps: { id, magnitude, distance } } = clashes
@@ -100,12 +100,14 @@ function getIntraClashLabel(unit: Unit.Atomic, clashes: IntraUnitClashes, elemen
 
     return [
         `Clash id: ${id[idx]} | Magnitude: ${mag} \u212B | Distance: ${dist} \u212B`,
-        bondLabel(Bond.Location(unit, clashes.a[idx], unit, clashes.b[idx]))
+        bondLabel(Bond.Location(structure, unit, clashes.a[idx], structure, unit, clashes.b[idx]))
     ].join('</br>')
 }
 
-function IntraClashLoci(unit: Unit.Atomic, clashes: IntraUnitClashes, elements: number[]) {
-    return DataLoci('intra-clashes', { unit, clashes }, elements, (boundingSphere: Sphere3D) =>  getIntraClashBoundingSphere(unit, clashes, elements, boundingSphere), () => getIntraClashLabel(unit, clashes, elements))
+function IntraClashLoci(structure: Structure, unit: Unit.Atomic, clashes: IntraUnitClashes, elements: number[]) {
+    return DataLoci('intra-clashes', { unit, clashes }, elements,
+        (boundingSphere: Sphere3D) =>  getIntraClashBoundingSphere(unit, clashes, elements, boundingSphere),
+        () => getIntraClashLabel(structure, unit, clashes, elements))
 }
 
 function getIntraClashLoci(pickingId: PickingId, structureGroup: StructureGroup, id: number) {
@@ -115,7 +117,7 @@ function getIntraClashLoci(pickingId: PickingId, structureGroup: StructureGroup,
         const unit = group.units[instanceId]
         if (Unit.isAtomic(unit)) {
             const clashes = ClashesProvider.get(structure).value!.intraUnit.get(unit.id)
-            return IntraClashLoci(unit, clashes, [groupId])
+            return IntraClashLoci(structure, unit, clashes, [groupId])
         }
     }
     return EmptyLoci
@@ -134,7 +136,7 @@ function createIntraClashIterator(structureGroup: StructureGroup): LocationItera
     const { a } = clashes
     const groupCount = clashes.edgeCount * 2
     const instanceCount = group.units.length
-    const location = StructureElement.Location.create()
+    const location = StructureElement.Location.create(structure)
     const getLocation = (groupIndex: number, instanceIndex: number) => {
         const unit = group.units[instanceIndex]
         location.unit = unit
@@ -203,7 +205,7 @@ function getInterClashBoundingSphere(clashes: InterUnitClashes, elements: number
     }, boundingSphere)
 }
 
-function getInterClashLabel(clashes: InterUnitClashes, elements: number[]) {
+function getInterClashLabel(structure: Structure, clashes: InterUnitClashes, elements: number[]) {
     const idx = elements[0]
     if (idx === undefined) return ''
     const c = clashes.edges[idx]
@@ -212,19 +214,21 @@ function getInterClashLabel(clashes: InterUnitClashes, elements: number[]) {
 
     return [
         `Clash id: ${c.props.id} | Magnitude: ${mag} \u212B | Distance: ${dist} \u212B`,
-        bondLabel(Bond.Location(c.unitA, c.indexA, c.unitB, c.indexB))
+        bondLabel(Bond.Location(structure, c.unitA, c.indexA, structure, c.unitB, c.indexB))
     ].join('</br>')
 }
 
-function InterClashLoci(clashes: InterUnitClashes, elements: number[]) {
-    return DataLoci('inter-clashes', clashes, elements, (boundingSphere: Sphere3D) =>  getInterClashBoundingSphere(clashes, elements, boundingSphere), () => getInterClashLabel(clashes, elements))
+function InterClashLoci(structure: Structure, clashes: InterUnitClashes, elements: number[]) {
+    return DataLoci('inter-clashes', clashes, elements,
+        (boundingSphere: Sphere3D) =>  getInterClashBoundingSphere(clashes, elements, boundingSphere),
+        () => getInterClashLabel(structure, clashes, elements))
 }
 
 function getInterClashLoci(pickingId: PickingId, structure: Structure, id: number) {
     const { objectId, groupId } = pickingId
     if (id === objectId) {
         const clashes = ClashesProvider.get(structure).value!.interUnit
-        return InterClashLoci(clashes, [groupId])
+        return InterClashLoci(structure, clashes, [groupId])
     }
     return EmptyLoci
 }
@@ -239,7 +243,7 @@ function createInterClashIterator(structure: Structure): LocationIterator {
     const clashes = ClashesProvider.get(structure).value!.interUnit
     const groupCount = clashes.edgeCount
     const instanceCount = 1
-    const location = StructureElement.Location.create()
+    const location = StructureElement.Location.create(structure)
     const getLocation = (groupIndex: number) => {
         const clash = clashes.edges[groupIndex]
         location.unit = clash.unitA

+ 2 - 2
src/mol-model/structure/export/categories/modified-residues.ts

@@ -35,7 +35,7 @@ function getModifiedResidues({ structures }: CifExportContext): StructureElement
 
     const ret = [];
     const prop = P.residue.label_comp_id;
-    const loc = StructureElement.Location.create();
+    const loc = StructureElement.Location.create(structure);
     for (const unit of structure.units) {
         if (!Unit.isAtomic(unit) || !unit.conformation.operator.isIdentity) continue;
         const residues = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements);
@@ -45,7 +45,7 @@ function getModifiedResidues({ structures }: CifExportContext): StructureElement
             loc.element = unit.elements[seg.start];
             const name = prop(loc);
             if (map.has(name)) {
-                ret[ret.length] = StructureElement.Location.create(loc.unit, loc.element);
+                ret[ret.length] = StructureElement.Location.clone(loc);
             }
         }
     }

+ 4 - 3
src/mol-model/structure/export/categories/secondary-structure.ts

@@ -77,7 +77,8 @@ function findElements<T extends SecondaryStructure.Element>(ctx: CifExportContex
     const { key, elements } = secondaryStructure;
     const ssElements: SSElement<any>[] = [];
 
-    for (const unit of ctx.structures[0].units) {
+    const structure = ctx.structures[0];
+    for (const unit of structure.units) {
         // currently can only support this for "identity" operators.
         if (!Unit.isAtomic(unit) || !unit.conformation.operator.isIdentity) continue;
 
@@ -103,8 +104,8 @@ function findElements<T extends SecondaryStructure.Element>(ctx: CifExportContex
                 if (startIdx !== key[current.index]) {
                     move = false;
                     ssElements[ssElements.length] = {
-                        start: StructureElement.Location.create(unit, segs.offsets[start]),
-                        end: StructureElement.Location.create(unit, segs.offsets[prev]),
+                        start: StructureElement.Location.create(structure, unit, segs.offsets[start]),
+                        end: StructureElement.Location.create(structure, unit, segs.offsets[prev]),
                         length: prev - start + 1,
                         element
                     }

+ 3 - 3
src/mol-model/structure/model/properties/custom/indexed.ts

@@ -109,7 +109,7 @@ class SegmentedMappedIndexedCustomProperty<Idx extends IndexedCustomProperty.Ind
                 const seg = chains.move();
                 if (!this.has(seg.index) || seenIndices.has(seg.index)) continue;
                 seenIndices.add(seg.index);
-                loci[loci.length] = StructureElement.Location.create(unit, unit.elements[seg.start]);
+                loci[loci.length] = StructureElement.Location.create(structure, unit, unit.elements[seg.start]);
             }
         }
 
@@ -154,7 +154,7 @@ class ElementMappedCustomProperty<T = any> implements IndexedCustomProperty<Elem
                 const e = elements[i];
                 if (!this.has(e) || seenIndices.has(e)) continue;
                 seenIndices.add(elements[i]);
-                loci[loci.length] = StructureElement.Location.create(unit, e);
+                loci[loci.length] = StructureElement.Location.create(structure, unit, e);
             }
         }
 
@@ -202,7 +202,7 @@ class EntityMappedCustomProperty<T = any> implements IndexedCustomProperty<Entit
                 const eI = index.getEntityFromChain(seg.index);
                 if (!this.has(eI) || seenIndices.has(eI)) continue;
                 seenIndices.add(eI);
-                loci[loci.length] = StructureElement.Location.create(unit, unit.elements[seg.start]);
+                loci[loci.length] = StructureElement.Location.create(structure, unit, unit.elements[seg.start]);
             }
         }
 

+ 15 - 12
src/mol-model/structure/query/context.ts

@@ -6,13 +6,12 @@
 
 import { Structure, StructureElement, Unit } from '../structure';
 import { now } from '../../../mol-util/now';
-import { ElementIndex } from '../model';
 import { BondType } from '../model/types';
 import { StructureSelection } from './selection';
 import { defaultBondTest } from './queries/internal';
 
 export interface QueryContextView {
-    readonly element: StructureElement.Location;
+    readonly element: Readonly<StructureElement.Location>;
     readonly currentStructure: Structure;
 }
 
@@ -28,7 +27,7 @@ export class QueryContext implements QueryContextView {
     readonly inputStructure: Structure;
 
     /** Current element */
-    readonly element = StructureElement.Location.create();
+    readonly element: StructureElement.Location = StructureElement.Location.create(void 0);
     currentStructure: Structure = void 0 as any;
 
     /** Current bond between atoms */
@@ -37,14 +36,9 @@ export class QueryContext implements QueryContextView {
     /** Supply this from the outside. Used by the internal.generator.current symbol */
     currentSelection: StructureSelection | undefined = void 0;
 
-    setElement(unit: Unit, e: ElementIndex) {
-        this.element.unit = unit;
-        this.element.element = e;
-    }
-
     pushCurrentElement(): StructureElement.Location {
         this.currentElementStack[this.currentElementStack.length] = this.element;
-        (this.element as StructureElement.Location) = StructureElement.Location.create();
+        (this.element as StructureElement.Location) = StructureElement.Location.create(void 0);
         return this.element;
     }
 
@@ -112,16 +106,21 @@ export interface QueryContextOptions {
 export interface QueryPredicate { (ctx: QueryContext): boolean }
 export interface QueryFn<T = any> { (ctx: QueryContext): T }
 
-export class QueryContextBondInfo<U extends Unit = Unit> {
-    a: StructureElement.Location<U> = StructureElement.Location.create();
+class QueryContextBondInfo<U extends Unit = Unit> {
+    a: StructureElement.Location<U> = StructureElement.Location.create(void 0);
     aIndex: StructureElement.UnitIndex = 0 as StructureElement.UnitIndex;
-    b: StructureElement.Location<U> = StructureElement.Location.create();
+    b: StructureElement.Location<U> = StructureElement.Location.create(void 0);
     bIndex: StructureElement.UnitIndex = 0 as StructureElement.UnitIndex;
     type: BondType = BondType.Flag.None;
     order: number = 0;
 
     private testFn: QueryPredicate = defaultBondTest;
 
+    setStructure(s: Structure) {
+        this.a.structure = s;
+        this.b.structure = s;
+    }
+
     setTestFn(fn?: QueryPredicate) {
         this.testFn = fn || defaultBondTest;
     }
@@ -136,6 +135,10 @@ export class QueryContextBondInfo<U extends Unit = Unit> {
     }
 
     private swap() {
+        // const sA = this.a.structure;
+        // this.a.structure = this.b.structure;
+        // this.b.structure = sA;
+
         const idxA = this.aIndex;
         this.aIndex = this.bIndex;
         this.bIndex = idxA;

+ 4 - 2
src/mol-model/structure/query/queries/filters.ts

@@ -56,6 +56,7 @@ export function getCurrentStructureProperties(ctx: QueryContext, props: UnitType
     const { units } = ctx.currentStructure;
     const l = ctx.pushCurrentElement();
 
+    l.structure = ctx.currentStructure;
     for (const unit of units) {
         l.unit = unit;
         const elements = unit.elements;
@@ -239,6 +240,9 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
     const atomicBond = queryCtx.atomicBond;
 
     const interBonds = input.interUnitBonds;
+
+    atomicBond.setStructure(input);
+
     for (const unit of structure.units) {
         if (!Unit.isAtomic(unit)) continue;
 
@@ -248,8 +252,6 @@ function checkConnected(ctx: IsConnectedToCtx, structure: Structure) {
         const bondedUnits = interBonds.getConnectedUnits(unit);
         const buCount = bondedUnits.length;
 
-        atomicBond.a.unit = inputUnit;
-
         const srcElements = unit.elements;
         const inputElements = inputUnit.elements;
 

+ 5 - 0
src/mol-model/structure/query/queries/generators.ts

@@ -64,6 +64,7 @@ function atomGroupsLinear(atomTest: QueryPredicate): StructureQuery {
         const l = ctx.pushCurrentElement();
         const builder = inputStructure.subsetBuilder(true);
 
+        l.structure = inputStructure;
         for (const unit of units) {
             l.unit = unit;
             const elements = unit.elements;
@@ -89,6 +90,7 @@ function atomGroupsSegmented({ unitTest, entityTest, chainTest, residueTest, ato
         const l = ctx.pushCurrentElement();
         const builder = inputStructure.subsetBuilder(true);
 
+        l.structure = inputStructure;
         for (const unit of units) {
             l.unit = unit;
             if (!unitTest(ctx)) continue;
@@ -158,6 +160,7 @@ function atomGroupsGrouped({ unitTest, entityTest, chainTest, residueTest, atomT
         const l = ctx.pushCurrentElement();
         const builder = new LinearGroupingBuilder(inputStructure);
 
+        l.structure = inputStructure;
         for (const unit of units) {
             l.unit = unit;
             if (!unitTest(ctx)) continue;
@@ -295,6 +298,8 @@ export function bondedAtomicPairs(bondTest?: QueryPredicate): StructureQuery {
         const atomicBond = ctx.atomicBond;
         atomicBond.setTestFn(bondTest);
 
+        atomicBond.setStructure(structure);
+
         // Process intra unit bonds
         for (const unit of structure.units) {
             if (unit.kind !== Unit.Kind.Atomic) continue;

+ 3 - 3
src/mol-model/structure/query/queries/internal.ts

@@ -22,7 +22,7 @@ export function defaultBondTest(ctx: QueryContext) {
 export function atomicSequence(): StructureQuery {
     return function query_atomicSequence(ctx) {
         const { inputStructure } = ctx;
-        const l = StructureElement.Location.create();
+        const l = StructureElement.Location.create(inputStructure);
 
         const units: Unit[] = [];
         for (const unit of inputStructure.units) {
@@ -50,7 +50,7 @@ export function atomicSequence(): StructureQuery {
 export function water(): StructureQuery {
     return function query_water(ctx) {
         const { inputStructure } = ctx;
-        const l = StructureElement.Location.create();
+        const l = StructureElement.Location.create(inputStructure);
 
         const units: Unit[] = [];
         for (const unit of inputStructure.units) {
@@ -69,7 +69,7 @@ export function water(): StructureQuery {
 export function atomicHet(): StructureQuery {
     return function query_atomicHet(ctx) {
         const { inputStructure } = ctx;
-        const l = StructureElement.Location.create();
+        const l = StructureElement.Location.create(inputStructure);
 
         const units: Unit[] = [];
         for (const unit of inputStructure.units) {

+ 7 - 0
src/mol-model/structure/query/queries/modifiers.ts

@@ -94,6 +94,7 @@ function getIncludeSurroundingsWithRadius(ctx: QueryContext, source: Structure,
     const { elementRadius, elementRadiusClosure, sourceMaxRadius, radius } = params;
 
     ctx.pushCurrentElement();
+    ctx.element.structure = structure;
     for (const unit of structure.units) {
         ctx.element.unit = unit;
         const { x, y, z } = unit.conformation;
@@ -115,6 +116,7 @@ function getIncludeSurroundingsWithRadius(ctx: QueryContext, source: Structure,
 
 function createElementRadiusFn(ctx: QueryContext, eRadius: QueryFn<number>): StructureElement.Property<number> {
     return e => {
+        ctx.element.structure = e.structure;
         ctx.element.unit = e.unit;
         ctx.element.element = e.element;
         return eRadius(ctx);
@@ -123,6 +125,7 @@ function createElementRadiusFn(ctx: QueryContext, eRadius: QueryFn<number>): Str
 
 function findStructureRadius(ctx: QueryContext, eRadius: QueryFn<number>) {
     let r = 0;
+    ctx.element.structure = ctx.inputStructure;
     for (const unit of ctx.inputStructure.units) {
         ctx.element.unit = unit;
         const elements = unit.elements;
@@ -252,6 +255,7 @@ export function expandProperty(query: StructureQuery, property: QueryFn): Struct
         const builders: StructureSubsetBuilder[] = [];
         ctx.pushCurrentElement();
         StructureSelection.forEach(src, (s, sI) => {
+            ctx.element.structure = s;
             for (const unit of s.units) {
                 ctx.element.unit = unit;
                 const elements = unit.elements;
@@ -272,6 +276,7 @@ export function expandProperty(query: StructureQuery, property: QueryFn): Struct
             if (sI % 10 === 0) ctx.throwIfTimedOut();
         });
 
+        ctx.element.structure = ctx.inputStructure;
         for (const unit of ctx.inputStructure.units) {
             ctx.element.unit = unit;
             const elements = unit.elements;
@@ -355,6 +360,8 @@ function expandConnected(ctx: QueryContext, structure: Structure) {
         const inputUnitA = inputStructure.unitMap.get(unit.id) as Unit.Atomic;
         const { offset: intraBondOffset, b: intraBondB, edgeProps: { flags, order } } = inputUnitA.bonds;
 
+        atomicBond.setStructure(inputStructure);
+
         // Process intra unit bonds
         atomicBond.a.unit = inputUnitA;
         atomicBond.b.unit = inputUnitA;

+ 1 - 1
src/mol-model/structure/query/utils/builders.ts

@@ -56,7 +56,7 @@ export class LinearGroupingBuilder {
 
     private singletonSelection(): StructureSelection {
         const builder = this.source.subsetBuilder(true);
-        const loc = StructureElement.Location.create();
+        const loc = StructureElement.Location.create(this.source);
         for (let i = 0, _i = this.builders.length; i < _i; i++) {
             this.builders[i].setSingletonLocation(loc);
             builder.addToUnit(loc.unit.id, loc.element);

+ 6 - 2
src/mol-model/structure/query/utils/structure-distance.ts

@@ -61,12 +61,14 @@ namespace MinMaxDist {
 
         const { units } = a;
         let withinRange = false;
+        ctx.element.structure = a;
         for (let i = 0, _i = units.length; i < _i; i++) {
             const unit = units[i];
             const { elements, conformation: { position } } = unit;
+            ctx.element.unit = unit;
             for (let i = 0, _i = elements.length; i < _i; i++) {
                 const e = elements[i];
-                ctx.setElement(unit, e);
+                ctx.element.element = e;
                 const tp = toPoint(ctx, b, position(e, distPivot), elementRadius(ctx), minDist, maxDist, elementRadius);
                 if (tp === Result.BelowMin) return Result.BelowMin;
                 if (tp === Result.WithinMax) withinRange = true;
@@ -102,12 +104,14 @@ namespace MaxRadiusDist {
         if (a.elementCount === 0 || b.elementCount === 0) return 0;
 
         const { units } = a;
+        ctx.element.structure = a;
         for (let i = 0, _i = units.length; i < _i; i++) {
             const unit = units[i];
+            ctx.element.unit = unit;
             const { elements, conformation: { position } } = unit;
             for (let i = 0, _i = elements.length; i < _i; i++) {
                 const e = elements[i];
-                ctx.setElement(unit, e);
+                ctx.element.element = e;
                 if (toPoint(ctx, b, position(e, distPivot), elementRadius(ctx), maxDist, elementRadius)) return true;
             }
         }

+ 15 - 3
src/mol-model/structure/structure/element/location.ts

@@ -8,22 +8,34 @@
 import { ElementIndex } from '../../model';
 import Unit from '../unit';
 import { Vec3 } from '../../../../mol-math/linear-algebra';
+import Structure from '../structure';
 
 export { Location }
 
 interface Location<U = Unit> {
     readonly kind: 'element-location',
+    structure: Structure,
     unit: U,
     /** Index into element (atomic/coarse) properties of unit.model */
     element: ElementIndex
 }
 
 namespace Location {
-    export function create<U extends Unit>(unit?: U, element?: ElementIndex): Location<U> {
-        return { kind: 'element-location', unit: unit!, element: element || (0 as ElementIndex) };
+    export function create<U extends Unit>(structure: Structure | undefined, unit?: U, element?: ElementIndex): Location<U> {
+        return {
+            kind: 'element-location', 
+            structure: structure as any,
+            unit: unit as any,
+            element: element || (0 as ElementIndex)
+        };
     }
 
-    export function set(a: Location, unit?: Unit, element?: ElementIndex): Location {
+    export function clone<U extends Unit>(l: Location<U>): Location<U> {
+        return create(l.structure, l.unit, l.element);
+    }
+
+    export function set(a: Location, structure?: Structure, unit?: Unit, element?: ElementIndex): Location {
+        if (structure) a.structure = structure;
         if (unit) a.unit = unit
         if (element !== undefined) a.element = element
         return a;

+ 3 - 2
src/mol-model/structure/structure/element/loci.ts

@@ -90,11 +90,12 @@ export namespace Loci {
         const unit = loci.elements[0].unit;
         const element = unit.elements[OrderedSet.getAt(loci.elements[0].indices, 0)];
         if (e) {
+            e.structure = loci.structure;
             e.unit = loci.elements[0].unit;
             e.element = element;
             return e;
         }
-        return Location.create(unit, element);
+        return Location.create(loci.structure, unit, element);
     }
 
     export function toStructure(loci: Loci): Structure {
@@ -365,7 +366,7 @@ export namespace Loci {
 
     export function extendToWholeEntities(loci: Loci): Loci {
         const elements: Loci['elements'][0][] = []
-        const l = Location.create()
+        const l = Location.create(loci.structure)
         const entities = new Set<string>()
         const { units } = loci.structure
 

+ 24 - 23
src/mol-model/structure/structure/element/stats.ts

@@ -10,6 +10,7 @@ import Unit from '../unit';
 import { Loci } from './loci';
 import { Location } from './location';
 import { ChainIndex } from '../../model/indexing';
+import Structure from '../structure';
 
 export interface Stats {
     elementCount: number
@@ -37,12 +38,12 @@ export namespace Stats {
             unitCount: 0,
             structureCount: 0,
 
-            firstElementLoc: Location.create(),
-            firstConformationLoc: Location.create(),
-            firstResidueLoc: Location.create(),
-            firstChainLoc: Location.create(),
-            firstUnitLoc: Location.create(),
-            firstStructureLoc: Location.create(),
+            firstElementLoc: Location.create(void 0),
+            firstConformationLoc: Location.create(void 0),
+            firstResidueLoc: Location.create(void 0),
+            firstChainLoc: Location.create(void 0),
+            firstUnitLoc: Location.create(void 0),
+            firstStructureLoc: Location.create(void 0),
         }
     }
 
@@ -51,7 +52,7 @@ export namespace Stats {
         map.set(key, count + inc)
     }
 
-    function handleElement(stats: Stats, element: Loci['elements'][0]) {
+    function handleElement(stats: Stats, structure: Structure, element: Loci['elements'][0]) {
         const { indices, unit } = element
         const { elements } = unit
         const size = OrderedSet.size(indices)
@@ -60,19 +61,19 @@ export namespace Stats {
         const residueAltIdCounts = new Map<string, number>()
 
         if (size > 0) {
-            Location.set(stats.firstElementLoc, unit, elements[OrderedSet.start(indices)])
+            Location.set(stats.firstElementLoc, structure, unit, elements[OrderedSet.start(indices)])
         }
 
         // count single element unit as unit not element
         if (size === elements.length) {
             stats.unitCount += 1
             if (stats.unitCount === 1) {
-                Location.set(stats.firstUnitLoc, unit, elements[OrderedSet.start(indices)])
+                Location.set(stats.firstUnitLoc, structure, unit, elements[OrderedSet.start(indices)])
             }
         } else if (size === 1) {
             stats.elementCount += 1
             if (stats.elementCount === 1) {
-                Location.set(stats.firstElementLoc, unit, elements[OrderedSet.start(indices)])
+                Location.set(stats.firstElementLoc, structure, unit, elements[OrderedSet.start(indices)])
             }
         } else {
             if (Unit.isAtomic(unit)) {
@@ -99,7 +100,7 @@ export namespace Stats {
                         // full residue
                         stats.residueCount += 1
                         if (stats.residueCount === 1) {
-                            Location.set(stats.firstResidueLoc, unit, offsets[rI])
+                            Location.set(stats.firstResidueLoc, structure, unit, offsets[rI])
                         }
                     } else {
                         // partial residue
@@ -116,7 +117,7 @@ export namespace Stats {
                                     if (stats.conformationCount === 1) {
                                         for (let l = offsets[rI], _l = offsets[rI + 1]; l < _l; ++l) {
                                             if (k === label_alt_id.value(l)) {
-                                                Location.set(stats.firstConformationLoc, unit, l)
+                                                Location.set(stats.firstConformationLoc, structure, unit, l)
                                                 break
                                             }
                                         }
@@ -131,13 +132,13 @@ export namespace Stats {
             } else {
                 stats.elementCount += size
                 if (stats.elementCount === 1) {
-                    Location.set(stats.firstElementLoc, unit, elements[OrderedSet.start(indices)])
+                    Location.set(stats.firstElementLoc, structure, unit, elements[OrderedSet.start(indices)])
                 }
             }
         }
     }
 
-    function handleUnitChainsSimple(stats: Stats, element: Loci['elements'][0]) {
+    function handleUnitChainsSimple(stats: Stats, structure: Structure, element: Loci['elements'][0]) {
         const { indices, unit } = element;
         const size = OrderedSet.size(indices);
         if (size === 0) return;
@@ -148,7 +149,7 @@ export namespace Stats {
             if (size === elements.length) {
                 stats.chainCount += 1;
                 if (stats.chainCount === 1) {
-                    Location.set(stats.firstChainLoc, unit, elements[OrderedSet.start(indices)]);
+                    Location.set(stats.firstChainLoc, structure, unit, elements[OrderedSet.start(indices)]);
                 }
             }
             return;
@@ -186,13 +187,13 @@ export namespace Stats {
                 // full chain
                 stats.chainCount += 1;
                 if (stats.chainCount === 1) {
-                    Location.set(stats.firstChainLoc, unit, offsets[cI]);
+                    Location.set(stats.firstChainLoc, structure, unit, offsets[cI]);
                 }
             }
         }
     }
 
-    function handleUnitChainsPartitioned(stats: Stats, lociElements: Loci['elements'], start: number, end: number) {
+    function handleUnitChainsPartitioned(stats: Stats, structure: Structure, lociElements: Loci['elements'], start: number, end: number) {
         let element = lociElements[start];
 
         // all the elements have the same model since they are part of the same group so this is ok.
@@ -273,7 +274,7 @@ export namespace Stats {
                 const eI = elements[OrderedSet.getAt(indices, i)];
                 const cI = index[eI];
                 if (cI === firstCI) {
-                    Location.set(stats.firstChainLoc, unit, eI);
+                    Location.set(stats.firstChainLoc, structure, unit, eI);
                     return;
                 }
             }
@@ -289,13 +290,13 @@ export namespace Stats {
             stats.structureCount += 1
             if (stats.structureCount === 1) {
                 const { unit, indices } = loci.elements[0]
-                Location.set(stats.firstStructureLoc, unit, unit.elements[OrderedSet.min(indices)])
+                Location.set(stats.firstStructureLoc, loci.structure, unit, unit.elements[OrderedSet.min(indices)])
             }
         } else {
             for (const e of loci.elements) {
-                handleElement(stats, e)
+                handleElement(stats, loci.structure, e)
                 if (!Unit.Traits.is(e.unit.traits, Unit.Trait.Partitioned)) {
-                    handleUnitChainsSimple(stats, e);
+                    handleUnitChainsSimple(stats, loci.structure, e);
                 } else {
                     hasPartitions = true;
                 }
@@ -314,9 +315,9 @@ export namespace Stats {
                 const end = i;
                 i--;
                 if (end - start === 1) {
-                    handleUnitChainsSimple(stats, e);
+                    handleUnitChainsSimple(stats, loci.structure, e);
                 } else {
-                    handleUnitChainsPartitioned(stats, loci.elements, start, end);
+                    handleUnitChainsPartitioned(stats, loci.structure, loci.elements, start, end);
                 }
             }
         }

+ 4 - 3
src/mol-model/structure/structure/structure.ts

@@ -401,7 +401,7 @@ function getModels(s: Structure) {
 function getUniqueResidueNames(s: Structure) {
     const prop = StructureProperties.residue.label_comp_id;
     const names = new Set<string>();
-    const loc = StructureElement.Location.create();
+    const loc = StructureElement.Location.create(s);
     for (const unit of s.units) {
         // TODO: support coarse unit?
         if (!Unit.isAtomic(unit)) continue;
@@ -418,7 +418,7 @@ function getUniqueResidueNames(s: Structure) {
 
 function getEntityIndices(structure: Structure): ReadonlyArray<EntityIndex> {
     const { units } = structure;
-    const l = StructureElement.Location.create();
+    const l = StructureElement.Location.create(structure);
     const keys = UniqueArray.create<number, EntityIndex>();
 
     for (const unit of units) {
@@ -871,7 +871,7 @@ namespace Structure {
     }
 
     export class ElementLocationIterator implements Iterator<StructureElement.Location> {
-        private current = StructureElement.Location.create();
+        private current: StructureElement.Location;
         private unitIndex = 0;
         private elements: StructureElement.Set;
         private maxIdx = 0;
@@ -905,6 +905,7 @@ namespace Structure {
         }
 
         constructor(private structure: Structure) {
+            this.current = StructureElement.Location.create(structure);
             this.hasNext = structure.elementCount > 0;
             if (this.hasNext) {
                 this.elements = structure.units[0].elements;

+ 16 - 3
src/mol-model/structure/structure/unit/bonds.ts

@@ -19,16 +19,28 @@ export * from './bonds/inter-compute'
 namespace Bond {
     export interface Location<U extends Unit = Unit> {
         readonly kind: 'bond-location',
+        
+        aStructure: Structure,
         aUnit: U,
         /** Index into aUnit.elements */
         aIndex: StructureElement.UnitIndex,
+
+        bStructure: Structure,
         bUnit: U,
         /** Index into bUnit.elements */
         bIndex: StructureElement.UnitIndex,
     }
 
-    export function Location(aUnit?: Unit, aIndex?: StructureElement.UnitIndex, bUnit?: Unit, bIndex?: StructureElement.UnitIndex): Location {
-        return { kind: 'bond-location', aUnit: aUnit as any, aIndex: aIndex as any, bUnit: bUnit as any, bIndex: bIndex as any };
+    export function Location(aStructure?: Structure, aUnit?: Unit, aIndex?: StructureElement.UnitIndex, bStructure?: Structure, bUnit?: Unit, bIndex?: StructureElement.UnitIndex): Location {
+        return {
+            kind: 'bond-location',
+            aStructure: aStructure as any,
+            aUnit: aUnit as any,
+            aIndex: aIndex as any,
+            bStructure: bStructure as any,
+            bUnit: bUnit as any,
+            bIndex: bIndex as any
+        };
     }
 
     export function isLocation(x: any): x is Location {
@@ -37,6 +49,7 @@ namespace Bond {
 
     export function areLocationsEqual(locA: Location, locB: Location) {
         return (
+            locA.aStructure.label === locB.aStructure.label && locA.bStructure.label === locB.bStructure.label &&
             locA.aIndex === locB.aIndex && locA.bIndex === locB.bIndex &&
             locA.aUnit.id === locB.aUnit.id && locA.bUnit.id === locB.bUnit.id
         )
@@ -86,7 +99,7 @@ namespace Bond {
             const indexB = SortedArray.indexOf(unitB.elements, elementB) as StructureElement.UnitIndex | -1
             if (indexB === -1) return
 
-            bonds.push(Location(unitA, indexA, unitB, indexB))
+            bonds.push(Location(loci.structure, unitA, indexA, loci.structure, unitB, indexB))
         });
 
         return Loci(structure, bonds);

+ 1 - 1
src/mol-model/structure/structure/util/lookup3d.ts

@@ -100,7 +100,7 @@ export class StructureLookup3D {
         const closeUnits = this.unitLookup.find(x, y, z, radius);
         if (closeUnits.count === 0) return;
 
-        const se = StructureElement.Location.create();
+        const se = StructureElement.Location.create(this.structure);
         const queryRadius = pivotR + maxRadius + radius;
 
         for (let t = 0, _t = closeUnits.count; t < _t; t++) {

+ 9 - 9
src/mol-plugin-ui/sequence.tsx

@@ -39,13 +39,13 @@ function splitModelEntityId(modelEntityId: string) {
 
 function getSequenceWrapper(state: SequenceViewState, structureSelection: StructureElementSelectionManager): SequenceWrapper.Any | string {
     const { structure, modelEntityId, chainGroupId, operatorKey } = state
-    const l = StructureElement.Location.create()
+    const l = StructureElement.Location.create(structure)
     const [ modelIdx, entityId ] = splitModelEntityId(modelEntityId)
 
     const units: Unit[] = []
 
     for (const unit of structure.units) {
-        StructureElement.Location.set(l, unit, unit.elements[0])
+        StructureElement.Location.set(l, structure, unit, unit.elements[0])
         if (structure.getModelIndex(unit.model) !== modelIdx) continue
         if (SP.entity.id(l) !== entityId) continue
         if (unit.chainGroupId !== chainGroupId) continue
@@ -60,7 +60,7 @@ function getSequenceWrapper(state: SequenceViewState, structureSelection: Struct
 
         let sw: SequenceWrapper<any>
         if (unit.polymerElements.length) {
-            const l = StructureElement.Location.create(unit, unit.elements[0])
+            const l = StructureElement.Location.create(structure, unit, unit.elements[0])
             const entitySeq = unit.model.sequence.byEntityKey[SP.entity.key(l)]
             // check if entity sequence is available
             if (entitySeq && entitySeq.sequence.length <= MaxDisplaySequenceLength) {
@@ -92,11 +92,11 @@ function getSequenceWrapper(state: SequenceViewState, structureSelection: Struct
 
 function getModelEntityOptions(structure: Structure) {
     const options: [string, string][] = []
-    const l = StructureElement.Location.create()
+    const l = StructureElement.Location.create(structure)
     const seen = new Set<string>()
 
     for (const unit of structure.units) {
-        StructureElement.Location.set(l, unit, unit.elements[0])
+        StructureElement.Location.set(l, structure, unit, unit.elements[0])
         const id = SP.entity.id(l)
         const modelIdx = structure.getModelIndex(unit.model)
         const key = `${modelIdx}|${id}`
@@ -121,12 +121,12 @@ function getModelEntityOptions(structure: Structure) {
 
 function getChainOptions(structure: Structure, modelEntityId: string) {
     const options: [number, string][] = []
-    const l = StructureElement.Location.create()
+    const l = StructureElement.Location.create(structure)
     const seen = new Set<number>()
     const [ modelIdx, entityId ] = splitModelEntityId(modelEntityId)
 
     for (const unit of structure.units) {
-        StructureElement.Location.set(l, unit, unit.elements[0])
+        StructureElement.Location.set(l, structure, unit, unit.elements[0])
         if (structure.getModelIndex(unit.model) !== modelIdx) continue
         if (SP.entity.id(l) !== entityId) continue
 
@@ -147,12 +147,12 @@ function getChainOptions(structure: Structure, modelEntityId: string) {
 
 function getOperatorOptions(structure: Structure, modelEntityId: string, chainGroupId: number) {
     const options: [string, string][] = []
-    const l = StructureElement.Location.create()
+    const l = StructureElement.Location.create(structure)
     const seen = new Set<string>()
     const [ modelIdx, entityId ] = splitModelEntityId(modelEntityId)
 
     for (const unit of structure.units) {
-        StructureElement.Location.set(l, unit, unit.elements[0])
+        StructureElement.Location.set(l, structure, unit, unit.elements[0])
         if (structure.getModelIndex(unit.model) !== modelIdx) continue
         if (SP.entity.id(l) !== entityId) continue
         if (unit.chainGroupId !== chainGroupId) continue

+ 2 - 2
src/mol-plugin-ui/sequence/chain.ts

@@ -54,14 +54,14 @@ export class ChainSequenceWrapper extends SequenceWrapper<StructureUnit> {
         let residueCount = 0
         let elementCount = 0
         const counts: string[] = []
-        const l = StructureElement.Location.create()
+        const l = StructureElement.Location.create(data.structure)
 
         const unitIndices = new Map<number, Interval<StructureElement.UnitIndex>>()
         const lociElements: StructureElement.Loci['elements'][0][] = []
 
         for (let i = 0, il = data.units.length; i < il; ++i) {
             const unit = data.units[i]
-            StructureElement.Location.set(l, unit, unit.elements[0])
+            StructureElement.Location.set(l, data.structure, unit, unit.elements[0])
             const entitySeq = unit.model.sequence.byEntityKey[StructureProperties.entity.key(l)]
             if (entitySeq) residueCount += entitySeq.sequence.length
             elementCount += unit.elements.length

+ 1 - 1
src/mol-plugin-ui/sequence/polymer.ts

@@ -66,7 +66,7 @@ export class PolymerSequenceWrapper extends SequenceWrapper<StructureUnit> {
     }
 
     constructor(data: StructureUnit) {
-        const l = StructureElement.Location.create(data.units[0], data.units[0].elements[0])
+        const l = StructureElement.Location.create(data.structure, data.units[0], data.units[0].elements[0])
         const entitySeq = data.units[0].model.sequence.byEntityKey[SP.entity.key(l)]
 
         const length = entitySeq.sequence.length

+ 1 - 1
src/mol-plugin-ui/sequence/sequence.tsx

@@ -176,7 +176,7 @@ export class Sequence<P extends SequenceProps> extends PluginUIComponent<P> {
         return classList.join(' ')
     }
 
-    private location = StructureElement.Location.create();
+    private location = StructureElement.Location.create(void 0);
     private getSequenceNumber(seqIdx: number) {
         let seqNum = ''
         const loci = this.props.sequenceWrapper.getLoci(seqIdx)

+ 1 - 1
src/mol-plugin/behavior/dynamic/custom-props/pdbe/structure-quality-report.ts

@@ -30,7 +30,7 @@ export const PDBeStructureQualityReport = PluginBehavior.create<{ autoAttach: bo
                     const u = e.unit;
                     if (!u.model.customProperties.has(StructureQualityReportProvider.descriptor)) return void 0;
 
-                    const se = StructureElement.Location.create(u, u.elements[OrderedSet.getAt(e.indices, 0)]);
+                    const se = StructureElement.Location.create(loci.structure, u, u.elements[OrderedSet.getAt(e.indices, 0)]);
                     const issues = StructureQualityReport.getIssues(se);
                     if (issues.length === 0) return 'PDBe Validation: No Issues';
                     return `PDBe Validation: ${issues.join(', ')}`;

+ 6 - 5
src/mol-repr/structure/visual/bond-inter-unit-cylinder.ts

@@ -33,7 +33,7 @@ function setRefPosition(pos: Vec3, structure: Structure, unit: Unit.Atomic, inde
 }
 
 const tmpRef = Vec3()
-const tmpLoc = StructureElement.Location.create()
+const tmpLoc = StructureElement.Location.create(void 0)
 
 function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InterUnitBondParams>, mesh?: Mesh) {
     const bonds = structure.interUnitBonds
@@ -90,6 +90,7 @@ function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structur
         },
         radius: (edgeIndex: number) => {
             const b = edges[edgeIndex]
+            tmpLoc.structure = structure
             tmpLoc.unit = b.unitA
             tmpLoc.element = b.unitA.elements[b.indexA]
             const sizeA = theme.size.size(tmpLoc)
@@ -142,12 +143,12 @@ function getBondLoci(pickingId: PickingId, structure: Structure, id: number) {
         const bond = structure.interUnitBonds.edges[groupId]
         return Bond.Loci(structure, [
             Bond.Location(
-                bond.unitA, bond.indexA as StructureElement.UnitIndex,
-                bond.unitB, bond.indexB as StructureElement.UnitIndex
+                structure, bond.unitA, bond.indexA as StructureElement.UnitIndex,
+                structure, bond.unitB, bond.indexB as StructureElement.UnitIndex
             ),
             Bond.Location(
-                bond.unitB, bond.indexB as StructureElement.UnitIndex,
-                bond.unitA, bond.indexA as StructureElement.UnitIndex
+                structure, bond.unitB, bond.indexB as StructureElement.UnitIndex,
+                structure, bond.unitA, bond.indexA as StructureElement.UnitIndex
             )
         ])
     }

+ 5 - 5
src/mol-repr/structure/visual/bond-intra-unit-cylinder.ts

@@ -25,7 +25,7 @@ import { ignoreBondType, BondCylinderParams, BondIterator } from './util/bond';
 function createIntraUnitBondCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<IntraUnitBondParams>, mesh?: Mesh) {
     if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh)
 
-    const location = StructureElement.Location.create(unit)
+    const location = StructureElement.Location.create(structure, unit)
 
     const elements = unit.elements;
     const bonds = unit.bonds
@@ -135,12 +135,12 @@ function getBondLoci(pickingId: PickingId, structureGroup: StructureGroup, id: n
         if (Unit.isAtomic(unit)) {
             return Bond.Loci(structure, [
                 Bond.Location(
-                    unit, unit.bonds.a[groupId] as StructureElement.UnitIndex,
-                    unit, unit.bonds.b[groupId] as StructureElement.UnitIndex
+                    structure, unit, unit.bonds.a[groupId] as StructureElement.UnitIndex,
+                    structure, unit, unit.bonds.b[groupId] as StructureElement.UnitIndex
                 ),
                 Bond.Location(
-                    unit, unit.bonds.b[groupId] as StructureElement.UnitIndex,
-                    unit, unit.bonds.a[groupId] as StructureElement.UnitIndex
+                    structure, unit, unit.bonds.b[groupId] as StructureElement.UnitIndex,
+                    structure, unit, unit.bonds.a[groupId] as StructureElement.UnitIndex
                 )
             ])
         }

+ 2 - 2
src/mol-repr/structure/visual/carbohydrate-link-cylinder.ts

@@ -24,7 +24,7 @@ function createCarbohydrateLinkCylinderMesh(ctx: VisualContext, structure: Struc
     const { links, elements } = structure.carbohydrates
     const { linkSizeFactor } = props
 
-    const location = StructureElement.Location.create()
+    const location = StructureElement.Location.create(structure)
 
     const builderProps = {
         linkCount: links.length,
@@ -74,7 +74,7 @@ function CarbohydrateLinkIterator(structure: Structure): LocationIterator {
     const { elements, links } = structure.carbohydrates
     const groupCount = links.length
     const instanceCount = 1
-    const location = StructureElement.Location.create()
+    const location = StructureElement.Location.create(structure)
     const getLocation = (groupIndex: number) => {
         const link = links[groupIndex]
         const carbA = elements[link.carbohydrateIndexA]

+ 2 - 2
src/mol-repr/structure/visual/carbohydrate-symbol-mesh.ts

@@ -53,7 +53,7 @@ function createCarbohydrateSymbolMesh(ctx: VisualContext, structure: Structure,
 
     const carbohydrates = structure.carbohydrates
     const n = carbohydrates.elements.length
-    const l = StructureElement.Location.create()
+    const l = StructureElement.Location.create(structure)
 
     for (let i = 0; i < n; ++i) {
         const c = carbohydrates.elements[i];
@@ -187,7 +187,7 @@ function CarbohydrateElementIterator(structure: Structure): LocationIterator {
     const carbElements = structure.carbohydrates.elements
     const groupCount = carbElements.length * 2
     const instanceCount = 1
-    const location = StructureElement.Location.create()
+    const location = StructureElement.Location.create(structure)
     function getLocation (groupIndex: number, instanceIndex: number) {
         const carb = carbElements[Math.floor(groupIndex / 2)]
         const ring = carb.unit.rings.all[carb.ringIndex]

+ 2 - 2
src/mol-repr/structure/visual/carbohydrate-terminal-link-cylinder.ts

@@ -25,7 +25,7 @@ function createCarbohydrateTerminalLinkCylinderMesh(ctx: VisualContext, structur
     const { terminalLinks, elements } = structure.carbohydrates
     const { terminalLinkSizeFactor } = props
 
-    const location = StructureElement.Location.create()
+    const location = StructureElement.Location.create(structure)
 
     const builderProps = {
         linkCount: terminalLinks.length,
@@ -91,7 +91,7 @@ function CarbohydrateTerminalLinkIterator(structure: Structure): LocationIterato
     const { elements, terminalLinks } = structure.carbohydrates
     const groupCount = terminalLinks.length
     const instanceCount = 1
-    const location = StructureElement.Location.create()
+    const location = StructureElement.Location.create(structure)
     const getLocation = (groupIndex: number) => {
         const terminalLink = terminalLinks[groupIndex]
         if (terminalLink.fromCarbohydrate) {

+ 5 - 3
src/mol-repr/structure/visual/cross-link-restraint-cylinder.ts

@@ -24,7 +24,7 @@ function createCrossLinkRestraintCylinderMesh(ctx: VisualContext, structure: Str
     if (!crossLinks.count) return Mesh.createEmpty(mesh)
     const { sizeFactor } = props
 
-    const location = StructureElement.Location.create()
+    const location = StructureElement.Location.create(structure)
 
     const builderProps = {
         linkCount: crossLinks.count,
@@ -76,8 +76,10 @@ function CrossLinkRestraintIterator(structure: Structure): LocationIterator {
     const location = Bond.Location()
     const getLocation = (groupIndex: number) => {
         const pair = pairs[groupIndex]
+        location.aStructure = structure
         location.aUnit = pair.unitA
         location.aIndex = pair.indexA
+        location.bStructure = structure
         location.bUnit = pair.unitB
         location.bIndex = pair.indexB
         return location
@@ -91,8 +93,8 @@ function getLinkLoci(pickingId: PickingId, structure: Structure, id: number) {
         const pair = structure.crossLinkRestraints.pairs[groupId]
         if (pair) {
             return Bond.Loci(structure, [
-                Bond.Location(pair.unitA, pair.indexA, pair.unitB, pair.indexB),
-                Bond.Location(pair.unitB, pair.indexB, pair.unitA, pair.indexA)
+                Bond.Location(structure, pair.unitA, pair.indexA, structure, pair.unitB, pair.indexB),
+                Bond.Location(structure, pair.unitB, pair.indexB, structure, pair.unitA, pair.indexA)
             ])
         }
     }

+ 1 - 1
src/mol-repr/structure/visual/ellipsoid-mesh.ts

@@ -74,7 +74,7 @@ export function createEllipsoidMesh(ctx: VisualContext, unit: Unit, structure: S
     const { U } = data
     const space = data._schema.U.space
     const pos = unit.conformation.invariantPosition
-    const l = StructureElement.Location.create()
+    const l = StructureElement.Location.create(structure)
     l.unit = unit
 
     for (let i = 0; i < elementCount; i++) {

+ 2 - 2
src/mol-repr/structure/visual/gaussian-surface-mesh.ts

@@ -38,7 +38,7 @@ export function getGaussianSurfaceVisual(webgl?: WebGLContext) {
 
 async function createGaussianSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, mesh?: Mesh): Promise<Mesh> {
     const { smoothness } = props
-    const { transform, field, idField } = await computeUnitGaussianDensity(unit, props, ctx.webgl).runInContext(ctx.runtime)
+    const { transform, field, idField } = await computeUnitGaussianDensity(structure, unit, props, ctx.webgl).runInContext(ctx.runtime)
 
     const params = {
         isoLevel: Math.exp(-smoothness),
@@ -119,7 +119,7 @@ async function createGaussianSurfaceTextureMesh(ctx: VisualContext, unit: Unit,
     if (!ctx.webgl) throw new Error('webgl context required to create gaussian surface texture-mesh')
     const isoLevel = Math.exp(-props.smoothness)
 
-    const densityTextureData = await computeUnitGaussianDensityTexture2d(unit, props, ctx.webgl).runInContext(ctx.runtime)
+    const densityTextureData = await computeUnitGaussianDensityTexture2d(structure, unit, props, ctx.webgl).runInContext(ctx.runtime)
     // console.log(densityTextureData)
     // console.log('vertexGroupTexture', readTexture(ctx.webgl, densityTextureData.texture))
     // ctx.webgl.waitForGpuCommandsCompleteSync()

+ 1 - 1
src/mol-repr/structure/visual/gaussian-surface-wireframe.ts

@@ -17,7 +17,7 @@ import { VisualUpdateState } from '../../util';
 
 async function createGaussianWireframe(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: GaussianDensityProps, lines?: Lines): Promise<Lines> {
     const { smoothness } = props
-    const { transform, field, idField } = await computeUnitGaussianDensity(unit, props, ctx.webgl).runInContext(ctx.runtime)
+    const { transform, field, idField } = await computeUnitGaussianDensity(structure, unit, props, ctx.webgl).runInContext(ctx.runtime)
 
     const params = {
         isoLevel: Math.exp(-smoothness),

+ 3 - 6
src/mol-repr/structure/visual/label-text.ts

@@ -67,8 +67,7 @@ const tmpVec = Vec3();
 const boundaryHelper = new BoundaryHelper();
 
 function createChainText(ctx: VisualContext, structure: Structure, theme: Theme, props: LabelTextProps, text?: Text): Text {
-
-    const l = StructureElement.Location.create();
+    const l = StructureElement.Location.create(structure);
     const { units, serialMapping } = structure;
     const { auth_asym_id, label_asym_id } = StructureProperties.chain;
     const { cumulativeUnitElementCount } = serialMapping
@@ -93,8 +92,7 @@ function createChainText(ctx: VisualContext, structure: Structure, theme: Theme,
 }
 
 function createResidueText(ctx: VisualContext, structure: Structure, theme: Theme, props: LabelTextProps, text?: Text): Text {
-
-    const l = StructureElement.Location.create();
+    const l = StructureElement.Location.create(structure);
     const { units, serialMapping } = structure;
     const { auth_seq_id, label_comp_id } = StructureProperties.residue;
     const { cumulativeUnitElementCount } = serialMapping
@@ -145,8 +143,7 @@ function createResidueText(ctx: VisualContext, structure: Structure, theme: Them
 }
 
 function createElementText(ctx: VisualContext, structure: Structure, theme: Theme, props: LabelTextProps, text?: Text): Text {
-
-    const l = StructureElement.Location.create();
+    const l = StructureElement.Location.create(structure);
     const { units, serialMapping } = structure;
     const { label_atom_id, label_alt_id } = StructureProperties.atom;
     const { cumulativeUnitElementCount } = serialMapping

+ 1 - 1
src/mol-repr/structure/visual/molecular-surface-mesh.ts

@@ -27,7 +27,7 @@ export type MolecularSurfaceMeshParams = typeof MolecularSurfaceMeshParams
 
 async function createMolecularSurfaceMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: MolecularSurfaceProps, mesh?: Mesh): Promise<Mesh> {
 
-    const { transform, field, idField } = await computeUnitMolecularSurface(unit, props).runInContext(ctx.runtime)
+    const { transform, field, idField } = await computeUnitMolecularSurface(structure, unit, props).runInContext(ctx.runtime)
     const params = {
         isoLevel: props.probeRadius,
         scalarField: field,

+ 1 - 1
src/mol-repr/structure/visual/molecular-surface-wireframe.ts

@@ -28,7 +28,7 @@ export type MolecularSurfaceWireframeParams = typeof MolecularSurfaceWireframePa
 
 async function createMolecularSurfaceWireframe(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: MolecularSurfaceProps, lines?: Lines): Promise<Lines> {
 
-    const { transform, field, idField } = await computeUnitMolecularSurface(unit, props).runInContext(ctx.runtime)
+    const { transform, field, idField } = await computeUnitMolecularSurface(structure, unit, props).runInContext(ctx.runtime)
     const params = {
         isoLevel: props.probeRadius,
         scalarField: field,

+ 2 - 2
src/mol-repr/structure/visual/orientation-ellipsoid-mesh.ts

@@ -88,10 +88,10 @@ export function createOrientationEllipsoidMesh(ctx: VisualContext, unit: Unit, s
 //
 
 function UnitIterator(structureGroup: StructureGroup): LocationIterator {
-    const { group } = structureGroup
+    const { group, structure } = structureGroup
     const groupCount = 1
     const instanceCount = group.units.length
-    const location = StructureElement.Location.create()
+    const location = StructureElement.Location.create(structure)
     const getLocation = (groupIndex: number, instanceIndex: number) => {
         const unit = group.units[instanceIndex]
         location.unit = unit

+ 1 - 1
src/mol-repr/structure/visual/polymer-backbone-cylinder.ts

@@ -42,7 +42,7 @@ function createPolymerBackboneCylinderMesh(ctx: VisualContext, unit: Unit, struc
     const pB = Vec3.zero()
     const cylinderProps: CylinderProps = { radiusTop: 1, radiusBottom: 1, radialSegments }
 
-    const polymerBackboneIt = PolymerBackboneIterator(unit)
+    const polymerBackboneIt = PolymerBackboneIterator(structure, unit)
     while (polymerBackboneIt.hasNext) {
         const { centerA, centerB } = polymerBackboneIt.move()
         pos(centerA.element, pA)

+ 1 - 1
src/mol-repr/structure/visual/polymer-gap-cylinder.ts

@@ -48,7 +48,7 @@ function createPolymerGapCylinderMesh(ctx: VisualContext, unit: Unit, structure:
     }
 
     let i = 0
-    const polymerGapIt = PolymerGapIterator(unit)
+    const polymerGapIt = PolymerGapIterator(structure, unit)
     while (polymerGapIt.hasNext) {
         const { centerA, centerB } = polymerGapIt.move()
         if (centerA.element === centerB.element) {

+ 3 - 3
src/mol-repr/structure/visual/util/bond.ts

@@ -25,11 +25,11 @@ export function ignoreBondType(include: BondType.Flag, exclude: BondType.Flag, f
 
 export namespace BondIterator {
     export function fromGroup(structureGroup: StructureGroup): LocationIterator {
-        const { group } = structureGroup
+        const { group, structure } = structureGroup
         const unit = group.units[0]
         const groupCount = Unit.isAtomic(unit) ? unit.bonds.edgeCount * 2 : 0
         const instanceCount = group.units.length
-        const location = StructureElement.Location.create()
+        const location = StructureElement.Location.create(structure)
         const getLocation = (groupIndex: number, instanceIndex: number) => {
             const unit = group.units[instanceIndex]
             location.unit = unit
@@ -42,7 +42,7 @@ export namespace BondIterator {
     export function fromStructure(structure: Structure): LocationIterator {
         const groupCount = structure.interUnitBonds.edgeCount
         const instanceCount = 1
-        const location = StructureElement.Location.create()
+        const location = StructureElement.Location.create(structure)
         const getLocation = (groupIndex: number) => {
             const bond = structure.interUnitBonds.edges[groupIndex]
             location.unit = bond.unitA

+ 3 - 3
src/mol-repr/structure/visual/util/common.ts

@@ -105,7 +105,7 @@ export function getConformation(unit: Unit) {
     }
 }
 
-export function getUnitConformationAndRadius(unit: Unit, ignoreHydrogens = false) {
+export function getUnitConformationAndRadius(structure: Structure, unit: Unit, ignoreHydrogens = false) {
     const conformation = getConformation(unit)
     const { elements } = unit
 
@@ -135,7 +135,7 @@ export function getUnitConformationAndRadius(unit: Unit, ignoreHydrogens = false
         id
     }
 
-    const l = StructureElement.Location.create(unit)
+    const l = StructureElement.Location.create(structure, unit)
     const sizeTheme = PhysicalSizeTheme({}, {})
     const radius = (index: number) => {
         l.element = index as ElementIndex
@@ -146,7 +146,7 @@ export function getUnitConformationAndRadius(unit: Unit, ignoreHydrogens = false
 }
 
 export function getStructureConformationAndRadius(structure: Structure, ignoreHydrogens = false) {
-    const l = StructureElement.Location.create()
+    const l = StructureElement.Location.create(structure)
     const sizeTheme = PhysicalSizeTheme({}, {})
 
     let xs: ArrayLike<number>

+ 4 - 4
src/mol-repr/structure/visual/util/element.ts

@@ -38,7 +38,7 @@ export function createElementSphereMesh(ctx: VisualContext, unit: Unit, structur
 
     const v = Vec3.zero()
     const pos = unit.conformation.invariantPosition
-    const l = StructureElement.Location.create()
+    const l = StructureElement.Location.create(structure)
     l.unit = unit
 
     for (let i = 0; i < elementCount; i++) {
@@ -159,10 +159,10 @@ export function getSerialElementLoci(pickingId: PickingId, structure: Structure,
 
 export namespace ElementIterator {
     export function fromGroup(structureGroup: StructureGroup): LocationIterator {
-        const { group } = structureGroup
+        const { group, structure } = structureGroup
         const groupCount = group.elements.length
         const instanceCount = group.units.length
-        const location = StructureElement.Location.create()
+        const location = StructureElement.Location.create(structure)
         const getLocation = (groupIndex: number, instanceIndex: number) => {
             const unit = group.units[instanceIndex]
             location.unit = unit
@@ -177,7 +177,7 @@ export namespace ElementIterator {
         const groupCount = elementCount
         const instanceCount = 1
         const { unitIndices, elementIndices } = structure.serialMapping
-        const location = StructureElement.Location.create()
+        const location = StructureElement.Location.create(structure)
         const getLocation = (groupIndex: number) => {
             location.unit = units[unitIndices[groupIndex]]
             location.element = elementIndices[groupIndex]

+ 6 - 6
src/mol-repr/structure/visual/util/gaussian.ts

@@ -34,22 +34,22 @@ export type GaussianDensityTextureProps = typeof DefaultGaussianDensityTexturePr
 
 //
 
-export function computeUnitGaussianDensity(unit: Unit, props: GaussianDensityProps, webgl?: WebGLContext) {
-    const { position, radius } = getUnitConformationAndRadius(unit, props.ignoreHydrogens)
+export function computeUnitGaussianDensity(structure: Structure, unit: Unit, props: GaussianDensityProps, webgl?: WebGLContext) {
+    const { position, radius } = getUnitConformationAndRadius(structure, unit, props.ignoreHydrogens)
     return Task.create('Gaussian Density', async ctx => {
         return await GaussianDensity(ctx, position, unit.lookup3d.boundary.box, radius, props, webgl);
     });
 }
 
-export function computeUnitGaussianDensityTexture(unit: Unit, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
-    const { position, radius } = getUnitConformationAndRadius(unit, props.ignoreHydrogens)
+export function computeUnitGaussianDensityTexture(structure: Structure, unit: Unit, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
+    const { position, radius } = getUnitConformationAndRadius(structure, unit, props.ignoreHydrogens)
     return Task.create('Gaussian Density', async ctx => {
         return GaussianDensityTexture(webgl, position, unit.lookup3d.boundary.box, radius, props, texture);
     });
 }
 
-export function computeUnitGaussianDensityTexture2d(unit: Unit, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
-    const { position, radius } = getUnitConformationAndRadius(unit, props.ignoreHydrogens)
+export function computeUnitGaussianDensityTexture2d(structure: Structure, unit: Unit, props: GaussianDensityTextureProps, webgl: WebGLContext, texture?: Texture) {
+    const { position, radius } = getUnitConformationAndRadius(structure, unit, props.ignoreHydrogens)
     return Task.create('Gaussian Density', async ctx => {
         return GaussianDensityTexture2d(webgl, position, unit.lookup3d.boundary.box, radius, props, texture);
     });

+ 5 - 5
src/mol-repr/structure/visual/util/molecular-surface.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit } from '../../../../mol-model/structure';
+import { Unit, Structure } from '../../../../mol-model/structure';
 import { Task, RuntimeContext } from '../../../../mol-task';
 import { getUnitConformationAndRadius } from './common';
 import { PositionData, DensityData } from '../../../../mol-math/geometry';
@@ -15,9 +15,9 @@ export type MolecularSurfaceProps = MolecularSurfaceCalculationProps & {
     ignoreHydrogens: boolean
 }
 
-function getPositionDataAndMaxRadius(unit: Unit, props: MolecularSurfaceProps) {
+function getPositionDataAndMaxRadius(structure: Structure, unit: Unit, props: MolecularSurfaceProps) {
     const { probeRadius, ignoreHydrogens } = props
-    const { position, radius } = getUnitConformationAndRadius(unit, ignoreHydrogens)
+    const { position, radius } = getUnitConformationAndRadius(structure, unit, ignoreHydrogens)
     const { indices } = position
     const n = OrderedSet.size(indices)
     const radii = new Float32Array(OrderedSet.end(indices))
@@ -33,8 +33,8 @@ function getPositionDataAndMaxRadius(unit: Unit, props: MolecularSurfaceProps) {
     return { position: { ...position, radius: radii }, maxRadius }
 }
 
-export function computeUnitMolecularSurface(unit: Unit, props: MolecularSurfaceProps) {
-    const { position, maxRadius } = getPositionDataAndMaxRadius(unit, props)
+export function computeUnitMolecularSurface(structure: Structure, unit: Unit, props: MolecularSurfaceProps) {
+    const { position, maxRadius } = getPositionDataAndMaxRadius(structure, unit, props)
     return Task.create('Molecular Surface', async ctx => {
         return await MolecularSurface(ctx, position, maxRadius, props);
     });

+ 2 - 2
src/mol-repr/structure/visual/util/nucleotide.ts

@@ -15,12 +15,12 @@ import { eachAtomicUnitTracedElement } from './polymer';
 
 export namespace NucleotideLocationIterator {
     export function fromGroup(structureGroup: StructureGroup): LocationIterator {
-        const { group } = structureGroup
+        const { group, structure } = structureGroup
         const u = group.units[0]
         const nucleotideElementIndices = Unit.isAtomic(u) ? u.nucleotideElements : []
         const groupCount = nucleotideElementIndices.length
         const instanceCount = group.units.length
-        const location = StructureElement.Location.create()
+        const location = StructureElement.Location.create(structure)
         const getLocation = (groupIndex: number, instanceIndex: number) => {
             const unit = group.units[instanceIndex]
             location.unit = unit

+ 6 - 6
src/mol-repr/structure/visual/util/polymer.ts

@@ -43,11 +43,11 @@ export function getGapRanges(unit: Unit): SortedRanges<ElementIndex> {
 
 export namespace PolymerLocationIterator {
     export function fromGroup(structureGroup: StructureGroup): LocationIterator {
-        const { group } = structureGroup
+        const { group, structure } = structureGroup
         const polymerElements = group.units[0].polymerElements
         const groupCount = polymerElements.length
         const instanceCount = group.units.length
-        const location = StructureElement.Location.create()
+        const location = StructureElement.Location.create(structure)
         const getLocation = (groupIndex: number, instanceIndex: number) => {
             const unit = group.units[instanceIndex]
             location.unit = unit
@@ -60,11 +60,11 @@ export namespace PolymerLocationIterator {
 
 export namespace PolymerGapLocationIterator {
     export function fromGroup(structureGroup: StructureGroup): LocationIterator {
-        const { group } = structureGroup
+        const { group, structure } = structureGroup
         const gapElements = group.units[0].gapElements
         const groupCount = gapElements.length
         const instanceCount = group.units.length
-        const location = StructureElement.Location.create()
+        const location = StructureElement.Location.create(structure)
         const getLocation = (groupIndex: number, instanceIndex: number) => {
             const unit = group.units[instanceIndex]
             location.unit = unit
@@ -214,8 +214,8 @@ export function getPolymerGapElementLoci(pickingId: PickingId, structureGroup: S
         const unitIndexB = OrderedSet.indexOf(unit.elements, unit.gapElements[groupId % 2 ? groupId - 1 : groupId + 1]) as StructureElement.UnitIndex
         if (unitIndexA !== -1 && unitIndexB !== -1) {
             return Bond.Loci(structure, [
-                Bond.Location(unit, unitIndexA, unit, unitIndexB),
-                Bond.Location(unit, unitIndexB, unit, unitIndexA)
+                Bond.Location(structure, unit, unitIndexA, structure, unit, unitIndexB),
+                Bond.Location(structure, unit, unitIndexB, structure, unit, unitIndexA)
             ])
         }
     }

+ 11 - 11
src/mol-repr/structure/visual/util/polymer/backbone-iterator.ts

@@ -4,19 +4,19 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit, StructureElement, ElementIndex, ResidueIndex } from '../../../../../mol-model/structure';
+import { Unit, Structure, StructureElement, ElementIndex, ResidueIndex } from '../../../../../mol-model/structure';
 import { Segmentation } from '../../../../../mol-data/int';
 import Iterator from '../../../../../mol-data/iterator';
 import SortedRanges from '../../../../../mol-data/int/sorted-ranges';
 import { getPolymerRanges } from '../polymer';
 
 /** Iterates over consecutive pairs of residues/coarse elements in polymers */
-export function PolymerBackboneIterator(unit: Unit): Iterator<PolymerBackbonePair> {
+export function PolymerBackboneIterator(structure: Structure, unit: Unit): Iterator<PolymerBackbonePair> {
     switch (unit.kind) {
-        case Unit.Kind.Atomic: return new AtomicPolymerBackboneIterator(unit)
+        case Unit.Kind.Atomic: return new AtomicPolymerBackboneIterator(structure, unit)
         case Unit.Kind.Spheres:
         case Unit.Kind.Gaussians:
-            return new CoarsePolymerBackboneIterator(unit)
+            return new CoarsePolymerBackboneIterator(structure, unit)
     }
 }
 
@@ -25,10 +25,10 @@ interface PolymerBackbonePair {
     centerB: StructureElement.Location
 }
 
-function createPolymerBackbonePair (unit: Unit) {
+function createPolymerBackbonePair (structure: Structure, unit: Unit) {
     return {
-        centerA: StructureElement.Location.create(unit),
-        centerB: StructureElement.Location.create(unit),
+        centerA: StructureElement.Location.create(structure, unit),
+        centerB: StructureElement.Location.create(structure, unit),
     }
 }
 
@@ -80,11 +80,11 @@ export class AtomicPolymerBackboneIterator implements Iterator<PolymerBackbonePa
         return this.value;
     }
 
-    constructor(private unit: Unit.Atomic) {
+    constructor(structure: Structure, private unit: Unit.Atomic) {
         this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex as ArrayLike<ElementIndex> // can assume it won't be -1 for polymer residues
         this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements)
         this.residueIt = Segmentation.transientSegments(unit.model.atomicHierarchy.residueAtomSegments, unit.elements)
-        this.value = createPolymerBackbonePair(unit)
+        this.value = createPolymerBackbonePair(structure, unit)
         this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext
     }
 }
@@ -126,9 +126,9 @@ export class CoarsePolymerBackboneIterator implements Iterator<PolymerBackbonePa
         return this.value;
     }
 
-    constructor(private unit: Unit.Spheres | Unit.Gaussians) {
+    constructor(structure: Structure, private unit: Unit.Spheres | Unit.Gaussians) {
         this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements);
-        this.value = createPolymerBackbonePair(unit)
+        this.value = createPolymerBackbonePair(structure, unit)
         this.hasNext = this.polymerIt.hasNext
     }
 }

+ 11 - 11
src/mol-repr/structure/visual/util/polymer/gap-iterator.ts

@@ -4,18 +4,18 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Unit, StructureElement, ElementIndex, ResidueIndex } from '../../../../../mol-model/structure';
+import { Unit, StructureElement, ElementIndex, ResidueIndex, Structure } from '../../../../../mol-model/structure';
 import Iterator from '../../../../../mol-data/iterator';
 import SortedRanges from '../../../../../mol-data/int/sorted-ranges';
 import { getGapRanges } from '../polymer';
 
 /** Iterates over gaps, i.e. the stem residues/coarse elements adjacent to gaps */
-export function PolymerGapIterator(unit: Unit): Iterator<PolymerGapPair> {
+export function PolymerGapIterator(structure: Structure, unit: Unit): Iterator<PolymerGapPair> {
     switch (unit.kind) {
-        case Unit.Kind.Atomic: return new AtomicPolymerGapIterator(unit)
+        case Unit.Kind.Atomic: return new AtomicPolymerGapIterator(structure, unit)
         case Unit.Kind.Spheres:
         case Unit.Kind.Gaussians:
-            return new CoarsePolymerGapIterator(unit)
+            return new CoarsePolymerGapIterator(structure, unit)
     }
 }
 
@@ -24,10 +24,10 @@ interface PolymerGapPair {
     centerB: StructureElement.Location
 }
 
-function createPolymerGapPair (unit: Unit) {
+function createPolymerGapPair (structure: Structure, unit: Unit) {
     return {
-        centerA: StructureElement.Location.create(unit),
-        centerB: StructureElement.Location.create(unit),
+        centerA: StructureElement.Location.create(structure, unit),
+        centerB: StructureElement.Location.create(structure, unit),
     }
 }
 
@@ -46,10 +46,10 @@ export class AtomicPolymerGapIterator implements Iterator<PolymerGapPair> {
         return this.value;
     }
 
-    constructor(private unit: Unit.Atomic) {
+    constructor(structure: Structure, private unit: Unit.Atomic) {
         this.traceElementIndex = unit.model.atomicHierarchy.derived.residue.traceElementIndex as ArrayLike<ElementIndex> // can assume it won't be -1 for polymer residues
         this.gapIt = SortedRanges.transientSegments(getGapRanges(unit), unit.elements);
-        this.value = createPolymerGapPair(unit)
+        this.value = createPolymerGapPair(structure, unit)
         this.hasNext = this.gapIt.hasNext
     }
 }
@@ -67,9 +67,9 @@ export class CoarsePolymerGapIterator implements Iterator<PolymerGapPair> {
         return this.value;
     }
 
-    constructor(private unit: Unit.Spheres | Unit.Gaussians) {
+    constructor(structure: Structure, private unit: Unit.Spheres | Unit.Gaussians) {
         this.gapIt = SortedRanges.transientSegments(getGapRanges(unit), unit.elements);
-        this.value = createPolymerGapPair(unit)
+        this.value = createPolymerGapPair(structure, unit)
         this.hasNext = this.gapIt.hasNext
     }
 }

+ 6 - 6
src/mol-repr/structure/visual/util/polymer/trace-iterator.ts

@@ -47,11 +47,11 @@ interface PolymerTraceElement {
 
 const SecStrucTypeNA = SecondaryStructureType.create(SecondaryStructureType.Flag.NA)
 
-function createPolymerTraceElement (unit: Unit): PolymerTraceElement {
+function createPolymerTraceElement (structure: Structure, unit: Unit): PolymerTraceElement {
     return {
-        center: StructureElement.Location.create(unit),
-        centerPrev: StructureElement.Location.create(unit),
-        centerNext: StructureElement.Location.create(unit),
+        center: StructureElement.Location.create(structure, unit),
+        centerPrev: StructureElement.Location.create(structure, unit),
+        centerNext: StructureElement.Location.create(structure, unit),
         first: false, last: false,
         initial: false, final: false,
         secStrucFirst: false, secStrucLast: false,
@@ -302,7 +302,7 @@ export class AtomicPolymerTraceIterator implements Iterator<PolymerTraceElement>
         this.cyclicPolymerMap = unit.model.atomicRanges.cyclicPolymerMap
         this.polymerIt = SortedRanges.transientSegments(this.polymerRanges, unit.elements)
         this.residueIt = Segmentation.transientSegments(this.residueAtomSegments, unit.elements);
-        this.value = createPolymerTraceElement(unit)
+        this.value = createPolymerTraceElement(structure, unit)
         this.hasNext = this.residueIt.hasNext && this.polymerIt.hasNext
 
         const secondaryStructure = SecondaryStructureProvider.get(structure).value?.get(unit.invariantId)
@@ -397,7 +397,7 @@ export class CoarsePolymerTraceIterator implements Iterator<PolymerTraceElement>
 
     constructor(private unit: Unit.Spheres | Unit.Gaussians, structure: Structure) {
         this.polymerIt = SortedRanges.transientSegments(getPolymerRanges(unit), unit.elements);
-        this.value = createPolymerTraceElement(unit)
+        this.value = createPolymerTraceElement(structure, unit)
         Vec3.set(this.value.d12, 1, 0, 0)
         Vec3.set(this.value.d23, 1, 0, 0)
         switch (unit.kind) {

+ 1 - 1
src/mol-theme/color/chain-id.ts

@@ -81,7 +81,7 @@ export function ChainIdColorTheme(ctx: ThemeDataContext, props: PD.Values<ChainI
     let legend: ScaleLegend | TableLegend | undefined
 
     if (ctx.structure) {
-        const l = StructureElement.Location.create()
+        const l = StructureElement.Location.create(ctx.structure)
         const asymIdSerialMap = getAsymIdSerialMap(ctx.structure.root)
 
         const labelTable = Array.from(asymIdSerialMap.keys())

+ 1 - 1
src/mol-theme/color/entity-source.ts

@@ -126,7 +126,7 @@ export function EntitySourceColorTheme(ctx: ThemeDataContext, props: PD.Values<E
     let legend: ScaleLegend | TableLegend | undefined
 
     if (ctx.structure) {
-        const l = StructureElement.Location.create()
+        const l = StructureElement.Location.create(ctx.structure)
         const { models } = ctx.structure.root
         const { seqToSrcByModelEntity, srcKeySerialMap } = getMaps(models)
 

+ 1 - 1
src/mol-theme/color/polymer-id.ts

@@ -90,7 +90,7 @@ export function PolymerIdColorTheme(ctx: ThemeDataContext, props: PD.Values<Poly
     let legend: ScaleLegend | TableLegend | undefined
 
     if (ctx.structure) {
-        const l = StructureElement.Location.create()
+        const l = StructureElement.Location.create(ctx.structure)
         const polymerAsymIdSerialMap = getPolymerAsymIdSerialMap(ctx.structure.root)
 
         const labelTable = Array.from(polymerAsymIdSerialMap.keys())

+ 3 - 3
src/mol-theme/label.ts

@@ -112,8 +112,8 @@ function _structureElementStatsLabel(stats: StructureElement.Stats, countsOnly =
 
 export function bondLabel(bond: Bond.Location, options: Partial<LabelOptions> = {}): string {
     const o = { ...DefaultLabelOptions, ...options }
-    const locA = StructureElement.Location.create(bond.aUnit, bond.aUnit.elements[bond.aIndex])
-    const locB = StructureElement.Location.create(bond.bUnit, bond.bUnit.elements[bond.bIndex])
+    const locA = StructureElement.Location.create(bond.aStructure, bond.aUnit, bond.aUnit.elements[bond.aIndex])
+    const locB = StructureElement.Location.create(bond.bStructure, bond.bUnit, bond.bUnit.elements[bond.bIndex])
     const labelA = _elementLabel(locA, o.granularity, o.hidePrefix)
     const labelB = _elementLabel(locB, o.granularity, o.hidePrefix)
     let offset = 0
@@ -137,7 +137,7 @@ export function bundleLabel(bundle: Loci.Bundle<any>, options: Partial<LabelOpti
         const o = { ...DefaultLabelOptions, ...options }
         const locations = (bundle.loci as StructureElement.Loci[]).map(l => {
             const { unit, indices } = l.elements[0]
-            return StructureElement.Location.create(unit, unit.elements[OrderedSet.start(indices)])
+            return StructureElement.Location.create(l.structure, unit, unit.elements[OrderedSet.start(indices)])
         })
         const labels = locations.map(l => _elementLabel(l, o.granularity, o.hidePrefix))
 

+ 1 - 1
src/perf-tests/structure.ts

@@ -117,7 +117,7 @@ export namespace PropertyAccess {
     }
 
     function sumProperty(structure: Structure, p: StructureElement.Property<number>) {
-        const l = StructureElement.Location.create();
+        const l = StructureElement.Location.create(structure);
         let s = 0;
 
         for (const unit of structure.units) {