diff --git a/CHANGELOG.md b/CHANGELOG.md index 82c807e707b582f94f6a81fe3db30b971b48c9db..7cd972e722db61d82e2c96afd1052da04c358560 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,17 +8,18 @@ Note that since we don't clearly distinguish between a public and private interf - Make `PluginContext.initContainer` checkered canvas background optional - Store URL of downloaded assets to detect zip/gzip based on extension +- Add optional `operator.key`; can be referenced in `IndexPairBonds` ## [v3.23.0] - 2022-10-19 - Add `PluginContext.initContainer/mount/unmount` methods; these should make it easier to reuse a plugin context with both custom and built-in UI - Add `PluginContext.canvas3dInitialized` - `createPluginUI` now resolves after the 3d canvas has been initialized -- Change EM Volume Streaming default from `Whote Structure` to `Auto` +- Change EM Volume Streaming default from `Whole Structure` to `Auto` ## [v3.22.0] - 2022-10-17 -- Replace `VolumeIsosurfaceParams.pickingGranularity` param with `Volume.PickingGranuality` +- Replace `VolumeIsosurfaceParams.pickingGranularity` param with `Volume.PickingGranuality` ## [v3.21.0] - 2022-10-17 diff --git a/src/mol-math/geometry/symmetry-operator.ts b/src/mol-math/geometry/symmetry-operator.ts index 3f848728976d022c1199434f64f17237d9d77db4..cbc2a5eda01f3d26d0f059e3e496d6743f8a4de9 100644 --- a/src/mol-math/geometry/symmetry-operator.ts +++ b/src/mol-math/geometry/symmetry-operator.ts @@ -1,11 +1,11 @@ /** - * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { lerp as scalar_lerp } from '../../mol-math/interpolate'; -import { defaults } from '../../mol-util'; import { Mat3 } from '../linear-algebra/3d/mat3'; import { Mat4 } from '../linear-algebra/3d/mat4'; import { Quat } from '../linear-algebra/3d/quat'; @@ -29,11 +29,13 @@ interface SymmetryOperator { readonly hkl: Vec3, /** spacegroup symmetry operator index, -1 if not applicable */ readonly spgrOp: number, + /** unique (external) key, -1 if not available */ + readonly key: number, readonly matrix: Mat4, - // cache the inverse of the transform + /** cache the inverse of the transform */ readonly inverse: Mat4, - // optimize the identity case + /** optimize the identity case */ readonly isIdentity: boolean, /** @@ -51,19 +53,20 @@ namespace SymmetryOperator { export const RotationTranslationEpsilon = 0.005; - export type CreateInfo = { assembly?: SymmetryOperator['assembly'], ncsId?: number, hkl?: Vec3, spgrOp?: number } + export type CreateInfo = { assembly?: SymmetryOperator['assembly'], ncsId?: number, hkl?: Vec3, spgrOp?: number, key?: number } export function create(name: string, matrix: Mat4, info?: CreateInfo | SymmetryOperator): SymmetryOperator { - let { assembly, ncsId, hkl, spgrOp } = info || { }; + let { assembly, ncsId, hkl, spgrOp, key } = info || { }; const _hkl = hkl ? Vec3.clone(hkl) : Vec3(); - spgrOp = defaults(spgrOp, -1); + spgrOp = spgrOp ?? -1; + key = key ?? -1; ncsId = ncsId || -1; const isIdentity = Mat4.isIdentity(matrix); const suffix = getSuffix(info, isIdentity); - if (isIdentity) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId, suffix }; + if (isIdentity) return { name, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId, suffix, key }; if (!Mat4.isRotationAndTranslation(matrix, RotationTranslationEpsilon)) { console.warn(`Symmetry operator (${name}) should be a composition of rotation and translation.`); } - return { name, assembly, matrix, inverse: Mat4.invert(Mat4(), matrix), isIdentity: false, hkl: _hkl, spgrOp, ncsId, suffix }; + return { name, assembly, matrix, inverse: Mat4.invert(Mat4(), matrix), isIdentity: false, hkl: _hkl, spgrOp, key, ncsId, suffix }; } function isSymmetryOperator(x: any): x is SymmetryOperator { diff --git a/src/mol-model-formats/structure/property/bonds/index-pair.ts b/src/mol-model-formats/structure/property/bonds/index-pair.ts index c6021eb47183f67a6fee53a8bda06a2183f2e60a..96e0945e91f7491d0743645e48dc5fee9f59c6ea 100644 --- a/src/mol-model-formats/structure/property/bonds/index-pair.ts +++ b/src/mol-model-formats/structure/property/bonds/index-pair.ts @@ -14,6 +14,8 @@ import { ElementIndex } from '../../../../mol-model/structure'; export type IndexPairsProps = { readonly key: ArrayLike<number> + readonly operatorA: ArrayLike<number> + readonly operatorB: ArrayLike<number> readonly order: ArrayLike<number> readonly distance: ArrayLike<number> readonly flag: ArrayLike<BondType.Flag> @@ -24,18 +26,22 @@ export type IndexPairBonds = { bonds: IndexPairs, maxDistance: number } function getGraph(indexA: ArrayLike<ElementIndex>, indexB: ArrayLike<ElementIndex>, props: Partial<IndexPairsProps>, count: number): IndexPairs { const builder = new IntAdjacencyGraph.EdgeBuilder(count, indexA, indexB); const key = new Int32Array(builder.slotCount); + const operatorA = new Array(builder.slotCount); + const operatorB = new Array(builder.slotCount); const order = new Int8Array(builder.slotCount); const distance = new Array(builder.slotCount); const flag = new Array(builder.slotCount); for (let i = 0, _i = builder.edgeCount; i < _i; i++) { builder.addNextEdge(); builder.assignProperty(key, props.key ? props.key[i] : -1); + builder.assignProperty(operatorA, props.operatorA ? props.operatorA[i] : -1); + builder.assignProperty(operatorB, props.operatorB ? props.operatorB[i] : -1); builder.assignProperty(order, props.order ? props.order[i] : 1); builder.assignProperty(distance, props.distance ? props.distance[i] : -1); builder.assignProperty(flag, props.flag ? props.flag[i] : BondType.Flag.Covalent); } - return builder.createGraph({ key, order, distance, flag }); + return builder.createGraph({ key, operatorA, operatorB, order, distance, flag }); } export namespace IndexPairBonds { @@ -50,6 +56,10 @@ export namespace IndexPairBonds { indexA: Column<number>, indexB: Column<number>, key?: Column<number>, + /** Operator key for indexA. Used in bond computation. */ + operatorA?: Column<number>, + /** Operator key for indexB. Used in bond computation. */ + operatorB?: Column<number>, order?: Column<number>, /** * Useful for bonds in periodic cells. That is, only bonds within the given @@ -83,11 +93,13 @@ export namespace IndexPairBonds { const indexA = pairs.indexA.toArray() as ArrayLike<ElementIndex>; const indexB = pairs.indexB.toArray() as ArrayLike<ElementIndex>; const key = pairs.key && pairs.key.toArray(); + const operatorA = pairs.operatorA && pairs.operatorA.toArray(); + const operatorB = pairs.operatorB && pairs.operatorB.toArray(); const order = pairs.order && pairs.order.toArray(); const distance = pairs.distance && pairs.distance.toArray(); const flag = pairs.flag && pairs.flag.toArray(); return { - bonds: getGraph(indexA, indexB, { key, order, distance, flag }, count), + bonds: getGraph(indexA, indexB, { key, operatorA, operatorB, order, distance, flag }, count), maxDistance: p.maxDistance }; } diff --git a/src/mol-model/structure/query/context.ts b/src/mol-model/structure/query/context.ts index c51b441a77da6cfffd623877b0734df8e87fc757..7cf7070a41db2e78ce0fbef36e706b873b872b98 100644 --- a/src/mol-model/structure/query/context.ts +++ b/src/mol-model/structure/query/context.ts @@ -155,6 +155,6 @@ class QueryContextBondInfo<U extends Unit = Unit> { } get length() { - return StructureElement.Location.distance(this.a, this. b); + return StructureElement.Location.distance(this.a, this.b); } } \ No newline at end of file diff --git a/src/mol-model/structure/structure/properties.ts b/src/mol-model/structure/structure/properties.ts index 1ee3110f99cea729f8561224c2056042774763ac..71e003b232d9dd679a624d9162f792b683dc993c 100644 --- a/src/mol-model/structure/structure/properties.ts +++ b/src/mol-model/structure/structure/properties.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> * @author Alexander Rose <alexander.rose@weirdbyte.de> @@ -173,6 +173,7 @@ const unit = { multiChain: p(l => Unit.Traits.is(l.unit.traits, Unit.Trait.MultiChain)), object_primitive: p(l => l.unit.objectPrimitive), operator_name: p(l => l.unit.conformation.operator.name), + operator_key: p(l => l.unit.conformation.operator.key), model_index: p(l => l.unit.model.modelNum), model_label: p(l => l.unit.model.label), model_entry_id: p(l => l.unit.model.entryId), diff --git a/src/mol-model/structure/structure/unit/bonds/inter-compute.ts b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts index 74f5127df14aabdc3ba3ec71e6ca40ba1c92804c..c8d34230db378656cc0aa60007a77be463292b83 100644 --- a/src/mol-model/structure/structure/unit/bonds/inter-compute.ts +++ b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts @@ -71,6 +71,8 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput const testDistanceSq = (bRadius + maxRadius) * (bRadius + maxRadius); builder.startUnitPair(unitA.id, unitB.id); + const opKeyA = unitA.conformation.operator.key; + const opKeyB = unitB.conformation.operator.key; for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) { const aI = atomsA[_aI]; @@ -80,7 +82,7 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput if (!props.forceCompute && indexPairs) { const { maxDistance } = indexPairs; - const { offset, b, edgeProps: { order, distance, flag, key } } = indexPairs.bonds; + const { offset, b, edgeProps: { order, distance, flag, key, operatorA, operatorB } } = indexPairs.bonds; const srcA = sourceIndex.value(aI); const aeI = getElementIdx(type_symbolA.value(aI)); @@ -90,6 +92,10 @@ function findPairBonds(unitA: Unit.Atomic, unitB: Unit.Atomic, props: BondComput const _bI = SortedArray.indexOf(unitB.elements, bI) as StructureElement.UnitIndex; if (_bI < 0) continue; + const opA = operatorA[i]; + const opB = operatorB[i]; + if ((opA >= 0 && opA !== opKeyA) || (opB >= 0 && opB !== opKeyB)) continue; + const beI = getElementIdx(type_symbolA.value(bI)); const d = distance[i]; diff --git a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts index 29fd505381b21fd4791c49718f3900b8eeb001cb..71bef526e5b8e7c850c46e1c84f180fe907ea976 100644 --- a/src/mol-model/structure/structure/unit/bonds/intra-compute.ts +++ b/src/mol-model/structure/structure/unit/bonds/intra-compute.ts @@ -55,7 +55,7 @@ function findIndexPairBonds(unit: Unit.Atomic) { const { type_symbol } = unit.model.atomicHierarchy.atoms; const atomCount = unit.elements.length; const { maxDistance } = indexPairs; - const { offset, b, edgeProps: { order, distance, flag, key } } = indexPairs.bonds; + const { offset, b, edgeProps: { order, distance, flag, key, operatorA, operatorB } } = indexPairs.bonds; const { atomSourceIndex: sourceIndex } = unit.model.atomicHierarchy; const { invertedIndex } = Model.getInvertedAtomSourceIndex(unit.model); @@ -66,6 +66,8 @@ function findIndexPairBonds(unit: Unit.Atomic) { const orders: number[] = []; const keys: number[] = []; + const opKey = unit.conformation.operator.key; + for (let _aI = 0 as StructureElement.UnitIndex; _aI < atomCount; _aI++) { const aI = atoms[_aI]; const aeI = getElementIdx(type_symbol.value(aI)); @@ -80,6 +82,10 @@ function findIndexPairBonds(unit: Unit.Atomic) { const _bI = SortedArray.indexOf(unit.elements, bI) as StructureElement.UnitIndex; if (_bI < 0) continue; + const opA = operatorA[i]; + const opB = operatorB[i]; + if ((opA >= 0 && opA !== opKey) || (opB >= 0 && opB !== opKey)) continue; + const beI = getElementIdx(type_symbol.value(bI)); const d = distance[i]; diff --git a/src/mol-script/language/symbol-table/structure-query.ts b/src/mol-script/language/symbol-table/structure-query.ts index 209da4376e81c413caada96b2a2b544146e3de48..e7125a35814c22831970ee0571f198158e9df89a 100644 --- a/src/mol-script/language/symbol-table/structure-query.ts +++ b/src/mol-script/language/symbol-table/structure-query.ts @@ -273,6 +273,7 @@ const atomProperty = { sourceIndex: atomProp(Type.Num, 'Index of the atom/element in the input file.'), operatorName: atomProp(Type.Str, 'Name of the symmetry operator applied to this element.'), + operatorKey: atomProp(Type.Num, 'Key of the symmetry operator applied to this element.'), modelIndex: atomProp(Type.Num, 'Index of the model in the input file.'), modelLabel: atomProp(Type.Str, 'Label/header of the model in the input file.') }, diff --git a/src/mol-script/runtime/query/table.ts b/src/mol-script/runtime/query/table.ts index deab2d4baa0c10ab7fc3641b258ef8a1c7a0cce2..a87b23c7e6509c4324585904548578e9f8f10dd6 100644 --- a/src/mol-script/runtime/query/table.ts +++ b/src/mol-script/runtime/query/table.ts @@ -299,6 +299,7 @@ const symbols = [ D(MolScript.structureQuery.atomProperty.core.z, atomProp(StructureProperties.atom.z)), D(MolScript.structureQuery.atomProperty.core.sourceIndex, atomProp(StructureProperties.atom.sourceIndex)), D(MolScript.structureQuery.atomProperty.core.operatorName, atomProp(StructureProperties.unit.operator_name)), + D(MolScript.structureQuery.atomProperty.core.operatorKey, atomProp(StructureProperties.unit.operator_key)), D(MolScript.structureQuery.atomProperty.core.modelIndex, atomProp(StructureProperties.unit.model_index)), D(MolScript.structureQuery.atomProperty.core.modelLabel, atomProp(StructureProperties.unit.model_label)), D(MolScript.structureQuery.atomProperty.core.atomKey, (ctx, xs) => { diff --git a/src/mol-script/script/mol-script/symbols.ts b/src/mol-script/script/mol-script/symbols.ts index c4bd1cfe251bba567bfdd89eabc319cea26ece99..feb891928cd41da4977ee58ffee8132a15ed0601 100644 --- a/src/mol-script/script/mol-script/symbols.ts +++ b/src/mol-script/script/mol-script/symbols.ts @@ -1,7 +1,8 @@ /** - * Copyright (c) 2018 Mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2022 Mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal <david.sehnal@gmail.com> + * @author Alexander Rose <alexander.rose@weirdbyte.de> */ import { UniqueArray } from '../../../mol-data/generic'; @@ -205,6 +206,7 @@ export const SymbolTable = [ Alias(MolScript.structureQuery.atomProperty.core.z, 'atom.z'), Alias(MolScript.structureQuery.atomProperty.core.sourceIndex, 'atom.src-index'), Alias(MolScript.structureQuery.atomProperty.core.operatorName, 'atom.op-name'), + Alias(MolScript.structureQuery.atomProperty.core.operatorKey, 'atom.op-key'), Alias(MolScript.structureQuery.atomProperty.core.modelIndex, 'atom.model-index'), Alias(MolScript.structureQuery.atomProperty.core.modelLabel, 'atom.model-label'), Alias(MolScript.structureQuery.atomProperty.core.atomKey, 'atom.key'), @@ -253,6 +255,7 @@ export const SymbolTable = [ 'Bond Properties', Alias(MolScript.structureQuery.bondProperty.order, 'bond.order'), Alias(MolScript.structureQuery.bondProperty.length, 'bond.length'), + Alias(MolScript.structureQuery.bondProperty.key, 'bond.key'), Alias(MolScript.structureQuery.bondProperty.atomA, 'bond.atom-a'), Alias(MolScript.structureQuery.bondProperty.atomB, 'bond.atom-b'), Macro(MSymbol('bond.is', Arguments.List(StructureQueryTypes.BondFlag), Type.Bool,