From b77994d01f64cd3831c364472c45a5973fd326bb Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Fri, 10 Jan 2020 15:30:29 -0800 Subject: [PATCH] improved bond handling - filter (include/exclude) type in repr - cleaned-up bond types and names --- .../structure/mmcif/bonds/struct_conn.ts | 4 +- src/mol-model/structure/model/types.ts | 42 ++++++++++++++++--- .../util/structure-selection-helper.ts | 2 +- .../visual/bond-inter-unit-cylinder.ts | 26 ++++++++---- .../visual/bond-intra-unit-cylinder.ts | 24 +++++++---- src/mol-repr/structure/visual/util/bond.ts | 10 ++++- src/mol-script/runtime/query/table.ts | 11 +---- 7 files changed, 82 insertions(+), 37 deletions(-) diff --git a/src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts b/src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts index 9850bc6a6..b29c31dce 100644 --- a/src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts +++ b/src/mol-model-formats/structure/mmcif/bonds/struct_conn.ts @@ -235,9 +235,9 @@ export namespace StructConn { case 'covale': flags = BondType.Flag.Covalent; break; - case 'disulf': flags = BondType.Flag.Covalent | BondType.Flag.Sulfide; break; + case 'disulf': flags = BondType.Flag.Covalent | BondType.Flag.Disulfide; break; case 'hydrog': - flags = BondType.Flag.Hydrogen; + flags = BondType.Flag.HydrogenBond; break; case 'metalc': flags = BondType.Flag.MetallicCoordination; break; } diff --git a/src/mol-model/structure/model/types.ts b/src/mol-model/structure/model/types.ts index 88f5513e7..d59187274 100644 --- a/src/mol-model/structure/model/types.ts +++ b/src/mol-model/structure/model/types.ts @@ -592,11 +592,10 @@ export namespace BondType { None = 0x0, Covalent = 0x1, MetallicCoordination = 0x2, - Hydrogen = 0x4, - Ionic = 0x8, - Sulfide = 0x10, - Aromatic = 0x20, - Computed = 0x40 + HydrogenBond = 0x4, + Disulfide = 0x8, + Aromatic = 0x10, + Computed = 0x20 // currently at most 16 flags are supported!! } @@ -607,6 +606,39 @@ export namespace BondType { export function isCovalent(flags: BondType.Flag) { return (flags & BondType.Flag.Covalent) !== 0; } + + export const Names = { + 'covalent': Flag.Covalent, + 'metal-coordination': Flag.MetallicCoordination, + 'hydrogen-bond': Flag.HydrogenBond, + 'disulfide': Flag.HydrogenBond, + 'aromatic': Flag.HydrogenBond, + 'computed': Flag.HydrogenBond, + } + export type Names = keyof typeof Names + + export function isName(name: string): name is Names { + return name in Names + } + + export function fromName(name: Names): Flag { + switch (name) { + case 'covalent': return Flag.Covalent; + case 'metal-coordination': return Flag.MetallicCoordination; + case 'hydrogen-bond': return Flag.HydrogenBond; + case 'disulfide': return Flag.Disulfide; + case 'aromatic': return Flag.Aromatic; + case 'computed': return Flag.Computed; + } + } + + export function fromNames(names: Names[]): Flag { + let f = Flag.None + for (let i = 0, il = names.length; i < il; ++i) { + f |= fromName(names[i]) + } + return f + } } /** diff --git a/src/mol-plugin/util/structure-selection-helper.ts b/src/mol-plugin/util/structure-selection-helper.ts index 64929a2e2..a24f03399 100644 --- a/src/mol-plugin/util/structure-selection-helper.ts +++ b/src/mol-plugin/util/structure-selection-helper.ts @@ -264,7 +264,7 @@ const disulfideBridges = StructureSelectionQuery('Disulfide Bridges', MS.struct. MS.struct.generator.bondedAtomicPairs({ 0: MS.core.flags.hasAny([ MS.struct.bondProperty.flags(), - MS.core.type.bitflags([BondType.Flag.Sulfide]) + MS.core.type.bitflags([BondType.Flag.Disulfide]) ]) }) ]) diff --git a/src/mol-repr/structure/visual/bond-inter-unit-cylinder.ts b/src/mol-repr/structure/visual/bond-inter-unit-cylinder.ts index 013ff64aa..e2813e6d5 100644 --- a/src/mol-repr/structure/visual/bond-inter-unit-cylinder.ts +++ b/src/mol-repr/structure/visual/bond-inter-unit-cylinder.ts @@ -10,14 +10,15 @@ import { Structure, StructureElement, Bond, Unit } from '../../../mol-model/stru import { Theme } from '../../../mol-theme/theme'; import { Mesh } from '../../../mol-geo/geometry/mesh/mesh'; import { Vec3 } from '../../../mol-math/linear-algebra'; -import { BitFlags } from '../../../mol-util'; -import { createBondCylinderMesh, BondCylinderParams, BondIterator } from './util/bond'; +import { BitFlags, arrayEqual } from '../../../mol-util'; +import { createBondCylinderMesh, BondCylinderParams, BondIterator, ignoreBondType } from './util/bond'; import { ComplexMeshParams, ComplexVisual, ComplexMeshVisual } from '../complex-visual'; import { VisualUpdateState } from '../../util'; import { PickingId } from '../../../mol-geo/geometry/picking'; import { EmptyLoci, Loci } from '../../../mol-model/loci'; import { Interval, OrderedSet } from '../../../mol-data/int'; import { isHydrogen } from './util/common'; +import { BondType } from '../../../mol-model/structure/model/types'; const tmpRefPosBondIt = new Bond.ElementBondIterator() function setRefPosition(pos: Vec3, structure: Structure, unit: Unit.Atomic, index: StructureElement.UnitIndex) { @@ -36,7 +37,16 @@ const tmpLoc = StructureElement.Location.create() function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structure, theme: Theme, props: PD.Values<InterUnitBondParams>, mesh?: Mesh) { const bonds = structure.interUnitBonds const { edgeCount, edges } = bonds - const { sizeFactor, sizeAspectRatio, ignoreHydrogens } = props + const { sizeFactor, sizeAspectRatio, ignoreHydrogens, includeTypes, excludeTypes } = props + + const include = BondType.fromNames(includeTypes) + const exclude = BondType.fromNames(excludeTypes) + + const ignoreHydrogen = ignoreHydrogens ? (edgeIndex: number) => { + const b = edges[edgeIndex] + const uA = b.unitA, uB = b.unitB + return isHydrogen(uA, uA.elements[b.indexA]) || isHydrogen(uB, uB.elements[b.indexB]) + } : () => false if (!edgeCount) return Mesh.createEmpty(mesh) @@ -75,11 +85,7 @@ function createInterUnitBondCylinderMesh(ctx: VisualContext, structure: Structur const sizeB = theme.size.size(tmpLoc) return Math.min(sizeA, sizeB) * sizeFactor * sizeAspectRatio }, - ignore: ignoreHydrogens ? (edgeIndex: number) => { - const b = edges[edgeIndex] - const uA = b.unitA, uB = b.unitB - return isHydrogen(uA, uA.elements[b.indexA]) || isHydrogen(uB, uB.elements[b.indexB]) - } : () => false + ignore: (edgeIndex: number) => ignoreHydrogen(edgeIndex) || ignoreBondType(include, exclude, edges[edgeIndex].props.flag) } return createBondCylinderMesh(ctx, builderProps, props, mesh) @@ -109,7 +115,9 @@ export function InterUnitBondVisual(materialId: number): ComplexVisual<InterUnit newProps.bondScale !== currentProps.bondScale || newProps.bondSpacing !== currentProps.bondSpacing || newProps.ignoreHydrogens !== currentProps.ignoreHydrogens || - newProps.bondCap !== currentProps.bondCap + newProps.bondCap !== currentProps.bondCap || + !arrayEqual(newProps.includeTypes, currentProps.includeTypes) || + !arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ) } }, materialId) diff --git a/src/mol-repr/structure/visual/bond-intra-unit-cylinder.ts b/src/mol-repr/structure/visual/bond-intra-unit-cylinder.ts index a07c04b85..d3844f4b9 100644 --- a/src/mol-repr/structure/visual/bond-intra-unit-cylinder.ts +++ b/src/mol-repr/structure/visual/bond-intra-unit-cylinder.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> * @author David Sehnal <david.sehnal@gmail.com> @@ -11,14 +11,15 @@ import { Unit, Structure, StructureElement, Bond } from '../../../mol-model/stru import { Theme } from '../../../mol-theme/theme'; import { Mesh } from '../../../mol-geo/geometry/mesh/mesh'; import { Vec3 } from '../../../mol-math/linear-algebra'; -import { BitFlags } from '../../../mol-util'; -import { createBondCylinderMesh, BondCylinderParams, BondIterator } from './util/bond'; +import { BitFlags, arrayEqual } from '../../../mol-util'; +import { createBondCylinderMesh, BondCylinderParams, BondIterator, ignoreBondType } from './util/bond'; import { UnitsMeshParams, UnitsVisual, UnitsMeshVisual, StructureGroup } from '../units-visual'; import { VisualUpdateState } from '../../util'; import { PickingId } from '../../../mol-geo/geometry/picking'; import { EmptyLoci, Loci } from '../../../mol-model/loci'; import { Interval, OrderedSet } from '../../../mol-data/int'; import { isHydrogen } from './util/common'; +import { BondType } from '../../../mol-model/structure/model/types'; function createIntraUnitBondCylinderMesh(ctx: VisualContext, unit: Unit, structure: Structure, theme: Theme, props: PD.Values<IntraUnitBondParams>, mesh?: Mesh) { if (!Unit.isAtomic(unit)) return Mesh.createEmpty(mesh) @@ -29,7 +30,14 @@ function createIntraUnitBondCylinderMesh(ctx: VisualContext, unit: Unit, structu const bonds = unit.bonds const { edgeCount, a, b, edgeProps, offset } = bonds const { order: _order, flags: _flags } = edgeProps - const { sizeFactor, sizeAspectRatio, ignoreHydrogens } = props + const { sizeFactor, sizeAspectRatio, ignoreHydrogens, includeTypes, excludeTypes } = props + + const include = BondType.fromNames(includeTypes) + const exclude = BondType.fromNames(excludeTypes) + + const ignoreHydrogen = ignoreHydrogens ? (edgeIndex: number) => { + return isHydrogen(unit, elements[a[edgeIndex]]) || isHydrogen(unit, elements[b[edgeIndex]]) + } : () => false if (!edgeCount) return Mesh.createEmpty(mesh) @@ -68,9 +76,7 @@ function createIntraUnitBondCylinderMesh(ctx: VisualContext, unit: Unit, structu const sizeB = theme.size.size(location) return Math.min(sizeA, sizeB) * sizeFactor * sizeAspectRatio }, - ignore: ignoreHydrogens ? (edgeIndex: number) => { - return isHydrogen(unit, elements[a[edgeIndex]]) || isHydrogen(unit, elements[b[edgeIndex]]) - } : () => false + ignore: (edgeIndex: number) => ignoreHydrogen(edgeIndex) || ignoreBondType(include, exclude, _flags[edgeIndex]) } return createBondCylinderMesh(ctx, builderProps, props, mesh) @@ -100,7 +106,9 @@ export function IntraUnitBondVisual(materialId: number): UnitsVisual<IntraUnitBo newProps.bondScale !== currentProps.bondScale || newProps.bondSpacing !== currentProps.bondSpacing || newProps.ignoreHydrogens !== currentProps.ignoreHydrogens || - newProps.bondCap !== currentProps.bondCap + newProps.bondCap !== currentProps.bondCap || + !arrayEqual(newProps.includeTypes, currentProps.includeTypes) || + !arrayEqual(newProps.excludeTypes, currentProps.excludeTypes) ) } }, materialId) diff --git a/src/mol-repr/structure/visual/util/bond.ts b/src/mol-repr/structure/visual/util/bond.ts index 5088ba970..fdc46e8c8 100644 --- a/src/mol-repr/structure/visual/util/bond.ts +++ b/src/mol-repr/structure/visual/util/bond.ts @@ -1,5 +1,5 @@ /** - * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. + * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose <alexander.rose@weirdbyte.de> */ @@ -21,10 +21,16 @@ export const BondCylinderParams = { bondSpacing: PD.Numeric(1, { min: 0, max: 2, step: 0.01 }), bondCap: PD.Boolean(false), radialSegments: PD.Numeric(16, { min: 2, max: 56, step: 2 }), + includeTypes: PD.MultiSelect(Object.keys(BondType.Names) as BondType.Names[], PD.objectToOptions(BondType.Names)), + excludeTypes: PD.MultiSelect([] as BondType.Names[], PD.objectToOptions(BondType.Names)), } export const DefaultBondCylinderProps = PD.getDefaultValues(BondCylinderParams) export type BondCylinderProps = typeof DefaultBondCylinderProps +export function ignoreBondType(include: BondType.Flag, exclude: BondType.Flag, f: BondType.Flag) { + return !BondType.is(include, f) || BondType.is(exclude, f) +} + const tmpShiftV12 = Vec3.zero() const tmpShiftV13 = Vec3.zero() @@ -99,7 +105,7 @@ export function createBondCylinderMesh(ctx: VisualContext, bondBuilder: BondCyli const f = flags(edgeIndex) builderState.currentGroup = edgeIndex - if (BondType.is(f, BondType.Flag.MetallicCoordination) || BondType.is(f, BondType.Flag.Hydrogen)) { + if (BondType.is(f, BondType.Flag.MetallicCoordination) || BondType.is(f, BondType.Flag.HydrogenBond)) { // show metall coordinations and hydrogen bonds with dashed cylinders cylinderProps.radiusTop = cylinderProps.radiusBottom = linkRadius / 3 cylinderProps.topCap = cylinderProps.bottomCap = true diff --git a/src/mol-script/runtime/query/table.ts b/src/mol-script/runtime/query/table.ts index afa124280..4eaa59ebc 100644 --- a/src/mol-script/runtime/query/table.ts +++ b/src/mol-script/runtime/query/table.ts @@ -353,16 +353,7 @@ function atomProp(p: (e: StructureElement.Location) => any): (ctx: QueryContext, } function bondFlag(current: BondType, f: string): BondType { - switch (f.toLowerCase()) { - case 'covalent': return current | BondType.Flag.Covalent; - case 'metallic': return current | BondType.Flag.MetallicCoordination; - case 'ionic': return current | BondType.Flag.Ionic; - case 'hydrogen': return current | BondType.Flag.Hydrogen; - case 'sulfide': return current | BondType.Flag.Sulfide; - case 'aromatic': return current | BondType.Flag.Aromatic; - case 'computed': return current | BondType.Flag.Computed; - default: return current; - } + return current | (BondType.isName(f) ? BondType.fromName(f) : BondType.Flag.None) } function secondaryStructureFlag(current: SecondaryStructureType, f: string): SecondaryStructureType { -- GitLab